Docs/Publishing/Reader keys and audiences

Reader keys and audiences

A publish config's audience decides who, if anyone, needs a credential to read it. A reader key is the long-lived, SDK-holdable credential a restricted publication's readers use.

The four audiences

Audience Anonymous reads? Grantable to
public Yes — no credential at all.
people No. person principals (WorkOS user ids)
agents No. agent principals (vault agent-token slugs)
people_agents No. either kind

For a restricted audience, a credential alone isn't enough — the resolved principal must also hold an explicit read grant on that publish config (vault_publish_grant, set via the publish config's grants array). A wrong-kind credential (e.g. a person token against an agents-only audience) is rejected even with a grant, because the grant itself is stored per (principal_kind, principal_id).

Credentials the pub API accepts

Every non-public request is a Authorization: Bearer <credential>. The pub API resolves the credential in this order:

  1. Reader key (solr_<key_id>_<secret>) — a person principal, minted via the flow below.
  2. Agent token — the same HS256 vault JWT /api/mcp accepts. It must be cloud-scoped and match the vault's current agent_token_epoch (a rotated/revoked token stops working immediately). Because every vault currently issues one shared agent token with slug *, the token's own vaultId claim is also checked against the publish's vault — an agent token minted for vault A can never unlock vault B's restricted publication, even if * is granted there too.
  3. First-party session JWT (RS256, 1h) — the credential native Solomon clients hold.

Anything else (missing bearer, malformed token, expired/revoked token) is treated as anonymous, so a restricted publication just doesn't resolve for that caller.

Bearer-only, no cookies. The pub API deliberately never honors the AuthKit session cookie, because it also sends Access-Control-Allow-Origin: *. Since no ambient cookie authority ever rides these requests, a malicious site fetching them cross-origin can only see what a bare curl could — it can never piggyback a signed-in user's session.

Minting a reader key

  1. GET/POST /api/v1/auth/device — bootstraps an OAuth 2.0 Device Authorization Grant (RFC 8628) against Solomon's AuthKit: shows the caller a user_code + verification_uri to open in a browser, then polls token_endpoint until approved. Returns 503 with device_flow_unconfigured if the deployment hasn't finished its AuthKit domain cutover — SDKs should surface "browser sign-in unavailable".
  2. Trade the resulting WorkOS access token (or an existing first-party session JWT) at POST /api/v1/auth/keys for a reader key. The full key (solr_<key_id>_<secret>) is returned exactly once — it's stored hash-only (SHA-256 of the secret), so losing it means minting a new one. Reader keys and agent tokens are themselves rejected at this endpoint (401): a read credential can never mint another credential.
  3. GET /api/v1/auth/keys lists your keys' metadata (id, prefix, label, timestamps) — never the secret. DELETE /api/v1/auth/keys/{keyId} soft-revokes one (revoked_at is stamped; the row stays listed).

A reader key currently only mints person principals — agents already carry their own vault-scoped agent token and use that directly against the pub API.

Fail-closed, always 404

Every failure mode on the read routes (/api/v1/pub/{slug}, .../notes/{path}, .../search) — unknown slug, disabled publish, missing credential, invalid/expired/revoked credential, a valid credential with no grant, or a valid credential of the wrong principal kind — returns the exact same 404 {"error": "not_found"}. This is deliberate: a 403 would tell a prober that a restricted publication exists at that slug even without access. Only the auth-management routes (/api/v1/auth/*) return a normal 401 (with WWW-Authenticate), since those aren't guarding content existence.

See also

  • [[Publish a folder or vault]] — scopes, audiences, and the entitlement gate.
  • [[The published API and SDKs]] — the endpoints these credentials unlock.
  • [[TypeScript SDK quickstart]] — where a reader key plugs in as a generated client's apiKey.