Skip to content

Agent registration (auth.md)

PerSQL implements auth.md, the open protocol that lets an agent register for a service and receive a scoped, short-lived access token — discoverable from a Markdown file at the service root. An agent that has never heard of PerSQL can find the manifest at https://api.persql.com/.well-known/auth.md, learn which flows exist, and bootstrap a token on its own.

There are two flows. Both end in a psql_live_… token you use against the /v1 API.

Two hops, both unauthenticated:

  1. GET /.well-known/oauth-protected-resource — RFC 9728 metadata pointing at the authorization server.
  2. GET /.well-known/oauth-authorization-server — its agent_auth block lists the identity_endpoint, token_endpoint, the supported flows, and a skill link to auth.md.

Your provider attests your identity with a signed token — any RS256/ES256 OIDC ID token (or ID-JAG assertion) from a trusted issuer, minted with audience persql. Wired today: GitHub Actions, Google Cloud service accounts / workload identity, and GitLab CI. Adding another issuer is a one-line registry entry, so an ID-JAG provider slots in the moment it ships a public JWKS.

import { registerAgent } from "@persql/sdk";
// `providerAssertion` is e.g. a GitHub Actions OIDC token (audience "persql").
const grant = await registerAgent(providerAssertion);
import { PerSQL } from "@persql/sdk";
const db = new PerSQL({ token: grant.accessToken }).database(grant.database);

Under the hood: POST /agent/identity with { "type": "identity_assertion", "assertion": "<jwt>" } returns a service-signed identity_assertion; the SDK exchanges it at /oauth/token (grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer) for the access token. The access token is short-lived — re-exchange the durable assertion to refresh, there is no refresh token:

import { exchangeAgentAssertion } from "@persql/sdk";
const fresh = await exchangeAgentAssertion(grant.identityAssertion!);

The raw GitHub Actions on-ramp (one curl, no SDK) is documented under self-signup — it’s the same verified flow.

When the trusted issuer is an enterprise IdP (WorkOS, Okta, Microsoft Entra, Google Workspace) that asserts a verified human email, the verified flow binds the workspace to that person, not a machine: an agent running as [email protected] registers a workspace for Madison — one she already owns if she has a PerSQL account, or one she can claim later by signing in with that email. Onboarding a customer’s IdP is a single config entry (its issuer URL + audience); PerSQL OIDC-discovers the rest. The trust boundary is the IdP’s email verification, so this binding is enabled per-issuer only for IdPs trusted for identity.

No provider attestation: a human confirms a short code in the browser, which provisions a workspace in their own account.

import { beginAgentClaim } from "@persql/sdk";
const claim = await beginAgentClaim({ clientName: "My Agent" });
console.log(`Visit ${claim.verificationUri} and confirm code ${claim.userCode}`);
// Resolves once the user approves it in the console (or rejects on expiry).
const grant = await claim.poll();

Under the hood: POST /agent/identity with { "type": "service_auth" } returns a claim_token plus a user_code and verification_uri. The agent shows the user the code; they sign in at the console, pick which workspace the agent should get, and confirm. The agent polls /oauth/token (grant_type=urn:workos:agent-auth:grant-type:claim) until it resolves into an admin token for that workspace.

Always show the user the user_code and have them check it matches before approving — that’s the defense against a forwarded link binding the agent to the wrong account.

  • Verified vs claimed token lifetime. The verified flow’s access token is short-lived; its identityAssertion is the durable (30-day), re-exchangeable credential — keep it secret. The claimed flow returns a normal no-expiry token instead, revocable any time under API tokens (a single human confirmation shouldn’t hand out a long-lived minting credential).
  • Tokens are admin-scoped to one workspace. A verified agent gets its own self-owned workspace; a claimed agent gets an admin token for the workspace the user picked. Neither can reach the user’s other workspaces.
  • Billing is usage-only against the workspace’s prepaid balance — top up before heavy use.