Tokens & ownership
Understand flypod account sessions, per-site manage tokens, claim tokens, and how each credential changes ownership and expiry.
A flypod site is either temporary (deployed without an account, expires in 14 days) or yours (attached to an account, no expiry). Three credentials move a site between those states: an account session proves who you are, a per-site manage token proves you may change one specific site, and a claim token converts one anonymous site into an owned one. The CLI stores and sends all three for you; you almost never handle one by hand.
How ownership works
There are two states and one action:
| State | What it means |
|---|---|
| Temporary | Anonymous deploy. Live immediately, expires in 14 days. |
| Yours | Attached to an account. No expiry, listed on your account page. |
flypod ./dist # → temporary
flypod login # → everything you deployed on this machine becomes yoursThat is the whole model. flypod login claims every anonymous site this
machine remembers, not just the last one — so "deploy now, log in whenever" is a
real workflow, not a hopeful one. You never have to learn what a token is, and
the happy path never shows you one.
Then why do tokens exist?
With no account there is nothing else that can prove a site is yours, so each
site gets a manage_token: possession is ownership. It's plumbing, not
something you handle. flypod writes it to projects.json (mode 0600), keyed by
the folder you deployed from — which is how update, rollback, and versions
work with no arguments, and how flypod login proves ownership of every
remembered site at once. In the normal case the token is created, used, and
retired without you ever seeing it.
The server stores only its SHA-256 hash, so it can't be re-issued or rotated.
A leaked token keeps working until the site expires, is deleted, or is
claimed: once a site belongs to an account, only that account's session can
manage it and the manage_token stops working. That is exactly why it isn't
printed.
The one case where flypod does show you a token
Hiding a credential is only safe while the system is holding it for you. So:
If flypod cannot remember a site for you, it tells you, and it gives you the token. It never creates a site it can't get back to, silently.
If the config directory is read-only or ephemeral (a CI container,
a locked-down FLYPOD_CONFIG_DIR), the registry write fails while the deploy
still succeeds — the site is live and nobody is holding its credential. flypod
warns loudly and prints the manage_token, and emits it on stderr even under
-q (and includes it in the --json receipt, which otherwise carries no
token). That is the one moment you genuinely need it.
Otherwise, flypod token reveals it on demand — for the rare
case of acting on a site from a machine that doesn't remember it. Rarer than it
sounds: if the site is yours, logging in on the second machine is the better
path, because a session can be replaced and a manage_token cannot.
Credential map
| Credential | Scope | Used for |
|---|---|---|
| Account session token | One account | Owned deploys, account site listing, and managing sites owned by that account. Created by the flypod login device flow. |
manage_token | One anonymous site | Reading metadata, adding versions, rolling back, switching the version timeline, and proving control during login auto-attach. Stops working once the site is claimed. |
claim_token | One anonymous site | Cross-machine fallback for attaching an anonymous site to an account. |
Account session token
flypod login obtains a Better Auth session token after you approve a device in
the browser. The CLI saves it outside the project and sends it as a bearer token.
For stateless automation, FLYPOD_TOKEN can supply an existing session directly
(flypod login --token <session_token> saves one you already hold). There is no
separate CI-token button: a session is account-wide.
The CLI picks one credential per request, in this order: an explicit
--token, the site's stored manage_token, FLYPOD_TOKEN, the saved
flypod login session, then the legacy FLYPOD_API_KEY. With none, a deploy is
anonymous. A credential that is sent but not accepted fails with
token_rejected; it never silently downgrades to an anonymous deploy.
Treat this token like a password: it represents the whole account. The separate
custom API-key system — and its key-creation subcommand — is retired;
FLYPOD_API_KEY remains only as a legacy environment-variable fallback.
A note on prefixes, since the retired API keys used one too: fk_ is the
current prefix for per-site capability tokens. Both manage_token and
claim_token are 32 random bytes rendered as fk_<base64url>. Seeing fk_
tells you nothing about which credential you're holding, only that it's a
site-scoped capability token rather than an account session.
manage_token
Every deploy returns a manage_token. While the site is anonymous it
authorizes the version loop for that specific site: reading metadata, adding
versions, rolling back. Anonymous CLI deploys store it in projects.json so
flypod update and flypod rollback work without flags — you don't handle it.
Claiming supersedes it. On an owned site the server accepts only the owning
account's session, so a manage_token that leaked while the site was anonymous
stops working the moment it is claimed — which is also the way to revoke one.
A manage_token cannot delete a site. There is no delete route that accepts
one; deletion is account-only, from the signed-in account page (or the
delete_site tool of a signed-in chat-app connector).
If you use the HTTP API or the library directly, the calculus flips — there is no registry holding the token for you, so you must save what the response returns. The CLI is the surface that saves it on your behalf.
claim_token
Anonymous deploys also return a claim_token, valid for exactly one route:
POST /sites/:id/claim, behind flypod claim <site_id> <claim_token>. It's a
cross-machine fallback, and it is not the recommended path — flypod login
is. It can't re-point a site that already belongs to someone else: that route
returns 409 (already_claimed) if the site has a different owner, and 401
(invalid_claim) for a wrong token.
A site published from a chat app has no folder on disk to hold its token, so
the deploy_site result carries a keep_url (/keep#<site_id>.<claim_token>)
instead: open it, sign in, and press one button. The token sits in the URL
fragment, so it never reaches a server log.
Don't have agents or CI jobs stash a claim_token for later. Authenticate the
job, or log in on the machine.
Anonymous vs owned
| Anonymous | Owned | |
|---|---|---|
| Created by | Deploy with no account session | Deploy with a saved login or FLYPOD_TOKEN |
| Managed via | manage_token | The owning account's session only |
| Deleted via | Nothing — it expires | Account page |
| Expiry | 14 days | None |
| Search indexing | noindex | Indexable |
| URL | <site_id>.flypod.page | <site_id>.flypod.page — claiming never moves a site |
Claiming through the CLI — flypod login or flypod claim — also clears the
14-day expiry on the local folder link, so it doesn't quietly self-destruct
on day 14 and leave your next flypod making a second site on a new URL. The
browser flow can't reach local files, so a site claimed from the dashboard is
permanent server-side while this machine's folder link still ages out; re-running
flypod update from that folder after it lapses is the case to watch for.
Ephemerality & TTL
Anonymous deploys expire 14 days after deploy and are swept by a garbage collector within minutes; deploying while logged in or claiming a site makes it permanent.
Comments
Anonymous, anchored comments on flypod sites, including visitor identity, access modes, threaded replies, and current hosted limitations.