A Stacks app's dashboard cannot read analytics from analyticshq, because every read endpoint resolves a signed-in user and there is no credential a machine can hold.
Why now
The Stacks dashboard (buddy dev --dashboard) already has the seam for this. config.dashboard.providers.<domain>.driver accepts local or hq, resolution lives in storage/framework/core/defaults/app/Actions/Dashboard/dashboard-provider.ts, and a remote provider is wrapped so an unreachable backend renders an empty section with a reason instead of taking the page down.
Selecting hq today renders empty, with this reason, which is written into the framework as a constant:
No data. The hosted provider has no read API yet, so this section cannot be filled from it.
So the dashboard side is finished and waiting. This issue is the missing half.
The actual blocker, verified rather than assumed
It is not that there are no read endpoints. There are. The blocker is that every one of them authenticates a person.
routes/analytics.ts:890 is representative: GET /api/sites calls authUserId(request) and 401s without one, and every /api/sites/{siteId}/* read does the same. A Bearer token works, but the only issuable one belongs to a human: full user scope, dies when they leave, wrong shape for a server-to-server read from an app's config.
analyticshq differs from loghq and bughq in one way worth noting: it has no ingest-key equivalent on Site, because the tracker is a public script beaconing to a site id. So there is no existing per-site credential to extend, and this is a new one rather than a second mode on an old one.
There is already a narrow public read at GET /api/public/{siteId}/summary. That is the right instinct and the wrong grain for this: a dashboard needs the same breakdowns the analyticshq UI shows, under an authenticated key, not a public summary.
What would unblock it
A read-scoped site key, issued from site settings:
- scoped to one site, independent of any user account
- read-only
- presented as
Authorization: Bearer <key> - revocable, and listed with a last-used timestamp
The endpoints it must reach: whatever the web Analytics section ends up rendering. Worth settling that list on this issue rather than exposing all forty-odd /api/sites/{siteId}/* routes to a machine key by default. My suggestion is to start with the summary, the time series, and the top referrers/pages breakdowns, and add on demand.
The shape the dashboard needs back
The Analytics section resolves its provider through storage/framework/core/defaults/app/Actions/Dashboard/Analytics/web-analytics-provider.ts, and the stx components import the declared types directly, so the field names are a contract rather than a preference. Agreeing them here before either side builds avoids a translation layer that then has to be maintained.
Not in scope
Writes. The seam is read-only and no provider writes.
Where this is tracked
Raised by Chris: HQ apps should be usable from the Stacks dashboard for HQ.training and other Stacks apps. The framework-side driver is tracked in stacksjs/stacks, blocked on this.