Summary
Three product areas where the right first step is an owner decision, not code. Each is real surface on the web; none is obviously right for a native macOS client. Filing them together so the decisions are made once.
1. Dashboard / Front wall + widgets
/help/getting-started puts the Dashboard second in prominence, right after Home:
Dashboard (click the chart icon) shows only your own messages in a table view. It also includes Data Management (links to Exports and Settings) and your profile summary.
Its tiles: Scheduled posts, Exports, Settings, Profile Information, and a Lists tree (of lists and list folders). There is also a configurable front wall, plus widgets — Location, Weather, markets, news, transit, transit stops, bike-share — and a full-screen Clock page.
⚠️ New finding, 2026-09-07: this is backend-blocked for a Bearer client, not merely a design question. Every route is x-auth-type: session:
GET/PUT /api/user/dashboard-layout session
GET/PUT /api/user/front-wall-layout session
GET /api/widgets/{markets,news,transit,transit/stops,bike-share} session
(GET /api/weather and GET /api/location are public.)
So even if the answer is "build it", the saved-layout half cannot be built until the routes accept Bearer.
The design question stands regardless: a native app arguably wants its own arrangement rather than mirroring the web's saved layout. Note that most of the Dashboard's content already exists natively — Scheduled posts, Exports, Settings, and a lists tree all ship; what is missing is the aggregation and the own-messages table view. A native "Dashboard" could be assembled from existing pieces with no new API surface at all, which is a materially cheaper option than the layout-syncing one.
Options:
- Do nothing. The constituent features are already reachable from the sidebar.
- Native dashboard, no layout sync (cheapest useful option) — a summary scene assembled from what already ships, plus an own-messages table. No new routes, not blocked.
- Full parity including saved layouts and widgets. Blocked on the session-only routes above.
2. Blog
/help/blog documents a Blog feature, and the web footer links /blog. The public API surface is only subscribe / confirm / unsubscribe (POST /api/blog/subscribe, GET /api/blog/subscribe/confirm, GET/POST /api/blog/unsubscribe — all public, all Bearer-safe). Authoring lives entirely behind /api/admin/blog*, which is admin-only and out of scope for a general client.
Options:
- Do nothing. The blog is a website; a link out is enough.
- Reader only — render posts natively. ⚠️ Note there is no public read route in the spec (only subscribe/unsubscribe), so a native reader would need either a new public endpoint or HTML scraping. Scraping is not acceptable; confirm a read route exists before choosing this.
- Reader + subscribe management, using the three public routes.
Native authoring is admin-gated and almost certainly out of scope.
3. Subscription status surface
The web has /subscription (current tier, Manage, Cancel — all via Stripe). This repo's standing decision is the opposite for native: "billing is managed in the web app by owner decision", and there is no in-app-purchase surface.
Meanwhile LiveEntitlements already reads customerStatus (live probe 2026-09-07 returns "customerStatus":"subscriber"), and SettingsRootView's header comment still lists "Subscription" as planned — so the code is ambiguous about which way this went.
Options:
- Do nothing and delete the stale comment.
- Recommended — read-only status in Settings ▸ Account showing the current tier, plus a "Manage subscription in your browser" button. No Stripe contact from the app, no payment UI ever native. This is what the entitlement-gating work wants anyway: when a Free user hits an Upgrade prompt, it needs somewhere to send them.
- Full native billing. Reverses the standing decision and raises sandboxing and App Review questions. Not recommended.
What "done" looks like for this issue
Notes
work-consolidation.md tracks these as G28, G29, and G39. The dashboard's session-only constraint is new information from the 2026-09-07 sweep and materially changes option 3's cost.
Summary
Three product areas where the right first step is an owner decision, not code. Each is real surface on the web; none is obviously right for a native macOS client. Filing them together so the decisions are made once.
1. Dashboard / Front wall + widgets
/help/getting-startedputs the Dashboard second in prominence, right after Home:Its tiles: Scheduled posts, Exports, Settings, Profile Information, and a Lists tree (of lists and list folders). There is also a configurable front wall, plus widgets — Location, Weather, markets, news, transit, transit stops, bike-share — and a full-screen Clock page.
x-auth-type: session:(
GET /api/weatherandGET /api/locationare public.)So even if the answer is "build it", the saved-layout half cannot be built until the routes accept Bearer.
The design question stands regardless: a native app arguably wants its own arrangement rather than mirroring the web's saved layout. Note that most of the Dashboard's content already exists natively — Scheduled posts, Exports, Settings, and a lists tree all ship; what is missing is the aggregation and the own-messages table view. A native "Dashboard" could be assembled from existing pieces with no new API surface at all, which is a materially cheaper option than the layout-syncing one.
Options:
2. Blog
/help/blogdocuments a Blog feature, and the web footer links/blog. The public API surface is only subscribe / confirm / unsubscribe (POST /api/blog/subscribe,GET /api/blog/subscribe/confirm,GET/POST /api/blog/unsubscribe— all public, all Bearer-safe). Authoring lives entirely behind/api/admin/blog*, which is admin-only and out of scope for a general client.Options:
Native authoring is admin-gated and almost certainly out of scope.
3. Subscription status surface
The web has
/subscription(current tier, Manage, Cancel — all via Stripe). This repo's standing decision is the opposite for native: "billing is managed in the web app by owner decision", and there is no in-app-purchase surface.Meanwhile
LiveEntitlementsalready readscustomerStatus(live probe 2026-09-07 returns"customerStatus":"subscriber"), andSettingsRootView's header comment still lists "Subscription" as planned — so the code is ambiguous about which way this went.Options:
What "done" looks like for this issue
docs/decisions/.Notes
work-consolidation.mdtracks these as G28, G29, and G39. The dashboard's session-only constraint is new information from the 2026-09-07 sweep and materially changes option 3's cost.