# Open Questions & Spec Gaps

Gaps between the API (spec v1.4.0) and what the Hub clients need. Each has a working assumption so building can start; the "Status" column says what changed with v1.4.0.

| # | Topic | Gap | Working assumption | Affects | Status |
|---|---|---|---|---|---|
| 1 | Avatar upload | API stores `avatar_url` only; no upload endpoint | A small signed-URL upload service, then PATCH the URL. Until it exists, avatar editing is hidden (initials shown). | ACC-01, APL-02 icon | Open |
| 2 | Push on Windows and Linux | The API's providers are APNs, FCM and Web Push only — no WNS, and Linux desktops have no push service | Windows and Linux poll `unread-count` / new inbox items (60 s while running, opt-in background) and raise **local** notifications and badges. Ask the API team whether to add a `wns` provider. | NTF-*, Windows, Linux | **Resolved for iOS/macOS/Android/Web by the API's Push Notifications; open for Windows (WNS)** |
| 3 | Hub as an Application | Devices must belong to an Application | The Hub is the first-party Application `app_hub`: `access_tier: free`, `receives_platform_notifications: true`, `visibility: internal`, with APNs/FCM/Web Push credentials. iOS and macOS share one bundle id (Universal Purchase) because an Application has one APNs credential with one `topic`. Confirm with the API team that one APNs topic per Application is intended, or ask for multiple topics. | CLIENT-ARCHITECTURE §2, §6 | **Changed** — was "Hub is not an Application" |
| 4 | SSO & passkeys | SSO callback returns 501; no WebAuthn endpoints | Buttons hidden behind `sso` / `passkeys`. | AUTH-01 | Open |
| 5 | Role holders | No `GET /users?role_id=` filter | "People with this role" hidden on ROL-02. | ROL-02 | Open |
| 6 | Bulk operations | No bulk endpoints except `notifications/mark-read` | Bulk actions run one request per row (max 4 in parallel) with per-row results. Inbox "mark all read" uses the real bulk endpoint. | USR-01, ACX-05, NTF-01 | Partly resolved |
| 7 | Session and device names | `Session` has `user_agent` + `ip_address`; `Device` has an optional `name` | Hub sends a descriptive `User-Agent`, and registers devices with a readable `name` ("Dana's iPhone", "Chrome on macOS") the person can rename in NTF-03. | ACC-04, NTF-03 | Partly resolved |
| 8 | Locale list | No endpoint listing locales/time zones | Use the platform's locale and IANA lists. | SET-01 | Open |
| 9 | App launch contract | How each app receives the app token isn't standardized | `launch_url` with `#app_token=` by default; per-app override client-side until the API adds a field. | APP-03 | Open |
| 10 | Store payment rules | **Standard is now a consumer subscription ($6/user/month) that unlocks apps** — App Store and Play generally require in-app purchase for digital subscriptions used in the app | Mobile shows plan status and "Manage billing"; the **Upgrade** button is flag-gated per store (`billing_upgrade_ios`, `billing_upgrade_android`) and defaults **off** until legal/policy review (US external-purchase link entitlement, other regions' rules, or adding StoreKit / Play Billing products that the API would need to recognize). Web, macOS (outside the Mac App Store build), Windows and Linux upgrade via Stripe Checkout. | BIL-01/02/03 | **Changed** — was a B2B developer plan |
| 11 | Audit export | Phase 3 | Hidden behind `audit_export`. | AUD-01 | Open |
| 12 | Org suspension by admins | Needs `organizations.manage` | Org admins see no suspend control. | ORG-08 | Open |
| 13 | Test mode in UI | `test_mode` on users, keys, webhooks, devices, notifications, subscriptions | Developer builds expose test-mode sign-in; test-mode devices register with APNs `sandbox`; test-mode checkout needs Stripe test mode (`409 test_mode_billing_unavailable` otherwise). | AUTH-01, KEY-*, NTF-*, BIL-* | Extended |
| 14 | Web session storage | No OS secure store in browsers | Same-origin BFF holds the refresh token in an HttpOnly cookie. | Web | Open |
| 15 | CORS | Web app needs `Authorization`, `Idempotency-Key`, `If-Match` allowed and `ETag`, `Retry-After`, `Audit-Hot-Window-Start` exposed | Confirm API CORS config for `https://dashboard.substratalapps.com`. | Web | Open (header list extended) |
| 16 | Notifications not built yet | Every Notifications operation returns `501 not_implemented` today | Ship NTF-* and APL-07 behind `notifications` / `push` / `push_credentials` flags; 501 flips them off. | D13, APL-07 | New |
| 17 | Erasure vs. billing | Deleting an account doesn't cancel a Stripe subscription | ACC-08 warns subscribers and links to Manage billing first. Ask the API team whether erasure should cancel at period end. | ACC-08 | New |
| 18 | Annual plans / Standard trial | DECISIONS #31 still open | No UI; BIL-01 copy avoids promising either. | BIL-01 | New |
| 19 | Price display | Price shown before a subscription exists isn't returned by any endpoint | Show "$6 per month" from remote config (matches Pricing), and `unit_amount` once a subscription exists. | BIL-01/02 | New |
| 20 | Hub access disabled | An admin can disable someone's `app_hub` Entitlement | They can still sign in to the Hub (platform session) but can't register devices (403) — inbox-only mode with an explanation in NTF-03. | NTF-03/04 | New |
| 21 | Notification actions from the lock screen | Accepting an org invitation from a notification needs a valid platform session | Mobile/macOS require device unlock and refresh the session in the background; if refresh fails, open the app to ORG-09. | ORG-09, NTF-02 | New |
| 22 | Theme choice storage | `Settings` only has `theme` (light/dark/system); there is no field for which light or dark theme | Store `appearance.light_theme` / `appearance.dark_theme` as `app_hub` per-app settings, which requires both keys (string enums, defaults `moss-paper` / `forest-floor`) in `app_hub`'s `settings_schema`. Until then, keep them on the device. Alternative: add `light_theme` / `dark_theme` to `Settings`. | SET-01, THEMES.md | New |
