aura_agent_* keys are live today, each scoped to one
published custom-agent deployment. praxa_sk_ keys are the tenant-wide
credential for the Praxa Execution Fabric (/v1/execute and the rest of the
/v1/* gateway) and are a developer preview.
Status:aura_agent_*deployment keys and embed origin-allowlists — Available today.praxa_sk_tenant keys, scopes, and the key console — Developer preview.
aura_agent_* keys are not being retired. Per the fabric design’s resolved
decisions, aura_agent_* “stays valid for custom-agent deployments and
migrates onto the tenant model later” — you do not need to pre-emptively
rotate onto praxa_sk_ when it ships (design spec §7.2).
For what a key’s scope actually constrains, see scopes-and-tenancy.md. For
throttling behavior, see rate-limits.md.
Key formats
These three are the only principal kinds the platform recognizes on
authenticated surfaces. All three resolve through one server-side principal
resolver to the same fail-closed, server-derived authority model — the
credential changes, the authorization logic doesn’t (design spec §3.2).
aura_agent_* — deployment credential
- Shape:
aura_agent_followed by 64 lowercase hex characters (32 random bytes fromcrypto.getRandomValues). - What it can do: authenticates one deployment’s External API channel
only. It has no separate scope list — the key is the deployment’s
identity, so its authority is exactly that deployment’s bound connectors,
knowledge, and memory (
docs/custom-agents.md). - Companion control: a deployment on the website-embed channel is
additionally bound to an explicit origin allowlist, checked at both token
exchange and run time — see
scopes-and-tenancy.mdfor the full mechanism. - Recognition on the wire:
Authorization: Bearer aura_agent_<64 hex>. The server distinguishes “not an Aura key” from “an Aura key that’s malformed” — a value that starts withaura_agent_but doesn’t match the full 64-hex-character shape is still flagged, so the route fails closed instead of silently treating it as anonymous (src/lib/custom-agents/api-auth.server.ts).
praxa_sk_ — tenant credential (developer preview)
- Shape:
praxa_sk_followed by a hex string. The prefix itself is a locked owner decision (2026-07-30); the exact byte length isn’t finalized in the design spec yet. - What it can do: scoped by the key, not implicit like
aura_agent_*. Each key carries scopes, an expiry, a per-key rate-limit tier, and a last-used timestamp, managed through a key CRUD console (design spec §3.2). - Endpoint family: the public gateway’s
/v1/*routes (execute,runs,runs/:id/events,runs/:id/approve,runs/:id/cancel,usage,webhooks), served atapi.praxa.io— already a live custom domain on the router worker, but the/v1/*dispatch to the gateway is flag-gated and not turned on yet (design spec §3.1, §4 F1). This is a different path from the existing/api/v1/*desktop gateway, which is Supabase-JWT-only and unrelated to API keys. - As of this writing, no endpoint serves this contract in production yet —
the request/event/error schemas exist in
packages/ai-platformbut are not wired to a live route (docs/ai-platform/README.md§7).
Lifecycle
Create
aura_agent_*: minted server-side when a deployment’s External API channel is created. 32 random bytes becomeaura_agent_<hex>(createAgentPublishableKey(),src/lib/custom-agents/embed-token.server.ts). The plaintext is returned exactly once, in that creation response.praxa_sk_(preview): issued from the tenant’s key console (design spec §3.2, §4 F2). Not built yet.
Rotate
aura_agent_*: therotate_deployment_secret(External API channel) androtate_embed_secret(website channel) mutations call Postgres RPCs that generate a fresh secret server-side, store its hash, and return the new plaintext exactly once in that mutation’s response (src/lib/custom-agents/repository.server.ts). The old key stops verifying immediately: “Secret rotation invalidates the old credential for future exchanges” (docs/custom-agents.md, “Lifecycle and versions”). Plan for a hard cutover, not a shared grace window.praxa_sk_(preview): the design spec names key CRUD and revocation explicitly but does not name a dedicated rotate operation the wayaura_agent_*has one. Until/unless one ships, treat rotation as create-new-then-revoke-old through the console — that’s an operational reading of the CRUD surface, not a documented endpoint.
Revoke
aura_agent_*: revocation is deployment-level, not a standalone key-revoke call, because the key is the deployment credential. Deployments have their ownactive,paused, andrevokedstates; pausing blocks new execution without destroying audit history, and revoking is the permanent form (docs/custom-agents.md).praxa_sk_(preview): revocation is a named, per-key field/operation — independent keys on the same tenant can be revoked individually (design spec §3.2).
Expire
aura_agent_*: no built-in expiry on the deployment credential itself. It’s valid until rotated or the deployment is paused/revoked. (Don’t confuse this with the website-embed visitor token exchanged for it — that token is short-lived and carries its owntoken_ttl_seconds, but it’s a different, downstream credential.)praxa_sk_(preview): an explicitexpiryfield, withlast-usedtracked separately (design spec §3.2).
Storage promises
- Hashed at rest, never stored or logged in plaintext.
For
aura_agent_*: a SHA-256 digest, hex-encoded, verified with a constant-time comparison (hashAgentPublishableKey,verifyAgentPublishableKey,src/lib/custom-agents/embed-token.server.ts) — the same digest Postgres computes viapgcrypto, so there’s one credential authority, not two drifting implementations. Forpraxa_sk_: the design spec commits to “hash-at-rest” without naming an algorithm. Theaura_agent_*SHA-256-plus-constant-time-compare implementation is the likely template but isn’t confirmed forplatform_api_keysyet. - Shown once. The plaintext key comes back in the create/rotate response body and never again — not on a later read, not in the console, not in logs. Losing it means rotating or issuing a new one, not recovering it.
- Never in logs or error responses. Server logs and errors carry stable
codes and correlation IDs, not provider payloads or secrets
(
docs/decisions/2026-07-24-ai-platform-extraction.md, “Security model”).
Best practices
- Environment variables or a secrets manager — never source control, a
mobile/web app bundle, or client-side JavaScript. Both key families are
bearer secrets: whoever holds one can act as that deployment
(
aura_agent_*) or that tenant (praxa_sk_). Anything that ships to a browser or device is readable by whoever has the browser or device. - Never hand a key to a browser or native client directly. Exchange it
for a short-lived, origin-bound token server-side instead. The website
embed channel already works this way: the long-lived credential is
exchanged for a short-lived visitor token bound to deployment, origin, and
session (
docs/custom-agents.md, “Runtime channels”) — mirror that pattern for anything you build on top of these keys. - Rotate on a schedule, and immediately after any suspected exposure — a key committed to a repo, a leaked log line, an offboarded integration. Rotation invalidates the old key immediately, so coordinate the cutover rather than assuming overlap.
- Least privilege. For
praxa_sk_, request only the scopes an integration needs (seescopes-and-tenancy.md). Foraura_agent_*, which has no scope list to narrow, the equivalent discipline is one key per deployment per integration — don’t share a single deployment’s key across unrelated systems just because it’s convenient. - One key per system. Distinct keys per integration let you revoke one caller without rotating every other one that happens to share a key.
Examples
aura_agent_* — calling a deployment’s External API today
praxa_sk_ — Execution Fabric (developer preview, illustrative)
Not live yet. Field names below follow the design spec’s /v1/execute
parameter list (task, context, tools, policy, delivery, plus an
Idempotency-Key header) — the JSON Schema itself is not yet published, so
treat the nesting as illustrative rather than a contract.
See also
scopes-and-tenancy.md— the tenant model and what a scope constrains.rate-limits.md— per-key throttling, 429/503 handling, retry guidance.