A published page is private property with a verified owner, and the HTML it serves never runs where the account lives
AgentKit Pages went from anonymous publishing to Assay accounts with per-device credentials in v0.7.0. Two decisions carry the security model: every page has one verified owner who grants and revokes access, and the control plane sits on a different origin from the arbitrary HTML agents publish.
From publish command to shared page
Everything below is a zoom into one band of this map. Read it left to right: secrets stay on the publisher's machine, identity and ownership live on the control plane, and the page the world loads is served from an origin that holds no credentials at all.
account.agentkit.sbs
render origin · pages.agentkit.sbs
origin boundary — crossed only by an explicit exchange
Trust map — the seven crossings a page makes between the machine that publishes it, the account that owns it, and the origin that serves it.
How a machine earns the right to publish
The first publish on a machine has no credential. Rather than failing, it starts the bounded
device flow on the account origin, and the credential it stores is scoped and dated. When the
service later rejects a stored credential, the publisher runs this same flow again instead of
leaving the operator holding an unactionable 401.
--name auth-flow-designaccount.agentkit.sbs/device the page's owner is this identitypages-token · mode 060090 days · pages:write · pages:deleteDevice authorization — the one path by which a machine acquires, and later re-acquires, the right to publish.
| File on the machine | What it holds | Why it is a separate file | If it is lost |
|---|---|---|---|
| pages-token ~/.config/agentkit/ |
The 90-day device credential, written mode 0600, carrying pages:write and pages:delete. |
It is the thing an owner revokes. The dashboard lists every publishing device with its grants and expiry. | Re-run the device flow; revoke the old device from the dashboard. |
| pages-slug-key ~/.config/agentkit/ |
An auto-generated HMAC key. A page's URL is HMAC(key, name) — cryptic hex, but deterministic, so republishing a name updates the same URL. |
Deliberately separate from the auth token so credential rotation never changes a URL. Copy it to other machines or the same name derives a different URL there. | HMAC-derived pages fall out of --name reach; recover slugs from the pages repo meta.yaml and manage them with --slug. |
Why the two origins exist, and what crosses between them
A published page is arbitrary HTML written by an agent. If it were served from the account
origin, its inline JavaScript would sit in the same origin as the dashboard and could read
the dashboard's cookie. So the session that proves who you are stays host-only on
account.agentkit.sbs, and what travels to the render origin is a random
capability: ten minutes long, scoped to a single page, and useless anywhere else.
Reading a private page — the session never leaves its origin; only a short-lived, single-page capability does.
agentkit.sbs stays separately credentialed. A device credential that can publish
pages cannot deploy the site.
What the owner can actually do
Sharing is not a property baked into the page — it is a row the owner can delete. The dashboard is where a page becomes reachable by someone else, and the only place where that reach is taken back.
Publishing devices
expires 2026-11-03
expires 2026-10-18
Revoking a device stops it publishing; the pages it already published keep their owner.
Your pages
Access · selected page
Every grant here is a row in page_access_tokens; revoking clears it.
Owner dashboard — devices, pages, and grants are the three lists an owner revokes from.
page_access_tokens — and when v0.7.2 added
explicit scopes, existing devices were backfilled through another immutable migration rather
than an edit.
Five ceilings, and what happens at each
Ownership answers who. These answer how much and for how long — the part of the model that limits the blast radius of a credential nobody has noticed is loose yet.
| Bound | Value | Where it lives | At the limit |
|---|---|---|---|
| Credential lifetime | 90 days | issued by the control plane | The publisher restarts device authorization rather than surfacing a bare 401. |
| Operations per device | 60 publish or delete per minute | D1 counter, configurable ceiling | 429 carrying Retry-After. Wait that interval; do not retry in a loop. |
| Page size | 5 MB, self-contained | publish path + serving CSP | Inline every asset — the CSP blocks external requests, so a page that needs the network is already broken. |
| Pages per account | 100 | account record | The cap is per account, not per device — every device an owner authorizes draws on the same 100. |
| Page capability | 10 minutes, one page | issued on the account origin | It expires and a fresh exchange is required; it was never valid for a second page. |
Quotas and lifetimes — each one is enforced on the control plane, never by the page itself.
What page privacy deliberately does not reach
--git, and that repository has its own visibility — a private page never implies a private archive.agentkit.sbs is credentialed separately from anything a publishing device holds.What has to stay true for the model to hold
account.agentkit.sbs, so JavaScript inside a published page cannot read it.page_access_tokens row — the failure v0.7.1 fixed.pages:delete is refused for a credential holding only pages:write.429 with Retry-After, and honoring that interval succeeds.