Authentication setup
Step-by-step guide for configuring authentication on the Astrocyte standalone gateway (astrocyte-gateway-py). Covers all five auth modes, access grants, and integration with OIDC providers.
Choose an auth mode
Section titled “Choose an auth mode”Set ASTROCYTE_AUTH_MODE to select how the gateway authenticates incoming requests:
| Mode | Env var value | Client sends | Maps to | Production? |
|---|---|---|---|---|
| Dev | dev (default) |
X-Astrocyte-Principal header |
principal |
No |
| API key | api_key |
X-Api-Key + X-Astrocyte-Principal |
principal |
Internal only |
| Per-user tokens | token |
Authorization: Bearer <token> (or X-Api-Key) |
principal, groups and grants from the token’s registry entry |
Yes (teams without an IdP) |
| JWT HS256 | jwt_hs256 or jwt |
Authorization: Bearer <token> |
principal from sub claim |
Yes (symmetric) |
| JWT OIDC | jwt_oidc |
Authorization: Bearer <token> |
principal, actor, tenant_id from claims |
Yes (enterprise SSO) |
Every mode produces an AstrocyteContext that flows to all operations — retain, recall, reflect, forget — and drives access control when enabled.
Mode 1: Dev (default)
Section titled “Mode 1: Dev (default)”Trusts the client-supplied principal header with no verification. Use only for local development and testing.
ASTROCYTE_AUTH_MODE=devClient request:
curl -X POST http://localhost:8080/v1/retain \ -H "Content-Type: application/json" \ -H "X-Astrocyte-Principal: user:alice" \ -d '{"content": "Alice likes dark mode", "bank_id": "user-alice"}'If the header is omitted, the request proceeds as anonymous (no principal, no access control).
Mode 2: API key
Section titled “Mode 2: API key”Shared secret verified with constant-time HMAC comparison. The client sends both the key and a principal header.
ASTROCYTE_AUTH_MODE=api_keyASTROCYTE_API_KEY=sk-your-secret-key-hereClient request:
curl -X POST http://localhost:8080/v1/recall \ -H "Content-Type: application/json" \ -H "X-Api-Key: sk-your-secret-key-here" \ -H "X-Astrocyte-Principal: agent:support-bot" \ -d '{"query": "user preferences", "bank_id": "shared"}'Errors:
| Status | Detail | Cause |
|---|---|---|
| 401 | Invalid or missing API key | X-Api-Key does not match ASTROCYTE_API_KEY |
| 401 | X-Astrocyte-Principal required | X-Astrocyte-Principal header missing or empty |
| 500 | ASTROCYTE_API_KEY is not set | Env var not configured |
When to use: Service-to-service calls where both sides share a secret. No key rotation mechanism — rotate by redeploying with a new value.
Anyone holding the shared key can send any X-Astrocyte-Principal, so this mode cannot tell teammates apart. For a team, use per-user tokens.
Mode 3: Per-user tokens
Section titled “Mode 3: Per-user tokens”Each person (or agent, or CI job) gets their own token. The gateway looks the token up in a registry file; the registry entry, not the client, decides the principal. X-Astrocyte-Principal is ignored in this mode. Tokens are stored as SHA-256 hashes; the plaintext is shown once, when the token is created.
ASTROCYTE_AUTH_MODE=tokenASTROCYTE_TOKENS_FILE=/etc/astrocyte/tokens.yamlThe gateway refuses to start if ASTROCYTE_TOKENS_FILE is unset, missing, or malformed. It re-reads the file whenever it changes, so new and revoked tokens take effect on the next request without a restart.
Mint, list, and revoke tokens
Section titled “Mint, list, and revoke tokens”# Alice: read and write every project bank, and a member of team:apipython -m astrocyte_gateway.tokens create --principal user:alice \ --banks 'project:*' --permissions read,write --groups team:api \ --label "alice laptop" --file tokens.yaml# → prints the token (astk_…) on stdout, once. Hand it to Alice; it is not stored.
# A read-only token for a CI jobpython -m astrocyte_gateway.tokens create --principal service:ci \ --banks 'project:api-1a2b3c' --permissions read --file tokens.yaml
python -m astrocyte_gateway.tokens list --file tokens.yaml # active tokens (never the plaintext)python -m astrocyte_gateway.tokens list --all --file tokens.yaml # include revokedpython -m astrocyte_gateway.tokens revoke 3f9a1c2e --file tokens.yaml--file defaults to $ASTROCYTE_TOKENS_FILE. A .json file name writes JSON; anything else writes YAML. The file is written atomically with mode 0600. Revoking keeps the row (with revoked_at) as an audit trail. With the release image, run the CLI through the image’s interpreter: docker run --rm -v "$PWD:/tokens" <image> -m astrocyte_gateway.tokens create … --file /tokens/tokens.yaml.
| Option | Meaning |
|---|---|
--principal |
user:<id>, agent:<id> or service:<id>. No wildcards. |
--banks |
Comma-separated bank ids or glob patterns (project:*). Optional. |
--permissions |
Comma-separated read, write, forget, admin, * for those banks. Default read,write. |
--groups |
Comma-separated team:<name> groups the principal belongs to. |
--label |
Free-text note shown by list. |
Client request
Section titled “Client request”curl -X POST http://localhost:8080/v1/retain \ -H "Content-Type: application/json" \ -H "Authorization: Bearer astk_…" \ -d '{"content": "We moved the queue to SQS", "bank_id": "project:api-1a2b3c"}'How token grants combine with config
Section titled “How token grants combine with config”A token’s --banks/--permissions and --groups are enforced by access control, so they need access_control.enabled: true. Without it the gateway refuses to start while any active token carries grants or groups (they would otherwise reach every bank), and refuses such a token added while it runs (403). Unscoped tokens work without access control, and then every token can read and write every bank; the gateway logs a warning saying so. With access control on, token grants are added to the grants in astrocyte.yaml for that principal; they never remove anything. Effective permissions on a bank are the union of:
- config grants whose
principalmatches the token’s principal, - config grants to any of the token’s
team:groups, - the token’s own bank grants.
So to issue a read-only token, give the principal no config-level write grant and mint the token with --permissions read. Two tokens for the same principal can carry different grants (a read-write laptop token and a read-only CI token). See access control setup.
Every memory written through the gateway records its author in metadata._actor, taken from the authenticated principal. A client-supplied _actor is overwritten.
Errors:
| Status | Detail | Cause |
|---|---|---|
| 401 | Bearer token or X-Api-Key required | No token sent |
| 401 | Invalid or revoked token | Token unknown or revoked |
| 403 | Principal ‘…’ denied ‘…’ on bank ‘…’ | Token and config grants don’t allow the operation |
| 500 | Token registry unavailable | The registry file became unreadable or malformed after startup |
When to use: Teams sharing a gateway without an identity provider. With an IdP, prefer jwt_oidc.
Mode 4: JWT HS256
Section titled “Mode 4: JWT HS256”Symmetric-key JWT verification. The issuer and gateway share a secret. The sub claim becomes the principal.
ASTROCYTE_AUTH_MODE=jwt_hs256ASTROCYTE_JWT_SECRET=your-256-bit-secretASTROCYTE_JWT_AUDIENCE=astrocyte # optional but recommendedGenerate a test token
Section titled “Generate a test token”import jwt, time
token = jwt.encode( { "sub": "user:alice", "aud": "astrocyte", "iat": int(time.time()), "exp": int(time.time()) + 3600, }, "your-256-bit-secret", algorithm="HS256",)print(token)Client request
Section titled “Client request”curl -X POST http://localhost:8080/v1/recall \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJhbGci..." \ -d '{"query": "user preferences", "bank_id": "user-alice"}'Required JWT claims
Section titled “Required JWT claims”| Claim | Required | Description |
|---|---|---|
sub |
Yes | Principal string (e.g. user:alice, agent:bot-1) |
aud |
If ASTROCYTE_JWT_AUDIENCE is set |
Must match exactly |
exp |
Recommended | Expiry timestamp |
iat |
Recommended | Issued-at timestamp |
If ASTROCYTE_JWT_AUDIENCE is not set, the gateway logs a warning and skips audience validation.
Errors:
| Status | Detail | Cause |
|---|---|---|
| 401 | Bearer token required | Authorization header missing or not Bearer ... |
| 401 | Invalid token | Signature invalid, expired, or audience mismatch |
| 401 | Token missing sub | sub claim absent or not a string |
| 500 | ASTROCYTE_JWT_SECRET is not set | Env var not configured |
Mode 5: JWT OIDC (RS256 + JWKS)
Section titled “Mode 5: JWT OIDC (RS256 + JWKS)”Asymmetric JWT verification using your IdP’s JWKS endpoint. The gateway fetches public keys from the IdP and verifies RS256 signatures — no shared secret needed.
ASTROCYTE_AUTH_MODE=jwt_oidcASTROCYTE_OIDC_JWKS_URL=https://auth.example.com/.well-known/jwks.jsonASTROCYTE_OIDC_ISSUER=https://auth.example.comASTROCYTE_OIDC_AUDIENCE=astrocyteASTROCYTE_OIDC_ACTOR_TYPE=user # optional, default "user"How it works
Section titled “How it works”- Client sends
Authorization: Bearer <token>(RS256-signed by your IdP) - Gateway extracts
kid(key ID) from the JWT header - Fetches the matching public key from
ASTROCYTE_OIDC_JWKS_URL(cached ~10 min) - Verifies RS256 signature,
iss, andaudclaims - Maps claims to a full
AstrocyteContextwith structured identity
Claim mapping
Section titled “Claim mapping”| JWT claim | Maps to | Default |
|---|---|---|
sub |
actor.id and part of principal |
Required |
astrocyte_actor_type |
actor.type |
ASTROCYTE_OIDC_ACTOR_TYPE env var, or "user" |
astrocyte_principal |
principal (explicit override) |
Computed as {actor_type}:{sub} |
tid or tenant_id |
tenant_id |
null |
| All other claims | actor.claims dict |
Stringified; complex types JSON-serialized |
Example: A token with {"sub": "abc-123", "astrocyte_actor_type": "agent", "tid": "tenant-1", "email": "bot@example.com"} produces:
AstrocyteContext( principal="agent:abc-123", actor=ActorIdentity( type="agent", id="abc-123", claims={"email": "bot@example.com"}, ), tenant_id="tenant-1",)Provider-specific setup
Section titled “Provider-specific setup”ASTROCYTE_OIDC_JWKS_URL=https://YOUR_DOMAIN.auth0.com/.well-known/jwks.jsonASTROCYTE_OIDC_ISSUER=https://YOUR_DOMAIN.auth0.com/ASTROCYTE_OIDC_AUDIENCE=astrocyteIn Auth0 dashboard: create an API with identifier astrocyte. Configure your application to request this audience in the token.
Keycloak
Section titled “Keycloak”ASTROCYTE_OIDC_JWKS_URL=https://keycloak.example.com/realms/YOUR_REALM/protocol/openid-connect/certsASTROCYTE_OIDC_ISSUER=https://keycloak.example.com/realms/YOUR_REALMASTROCYTE_OIDC_AUDIENCE=astrocyteIn Keycloak admin: create a client with astrocyte as the audience. Enable “Service Accounts” for machine-to-machine flows.
Azure AD (Entra ID)
Section titled “Azure AD (Entra ID)”ASTROCYTE_OIDC_JWKS_URL=https://login.microsoftonline.com/YOUR_TENANT_ID/discovery/v2.0/keysASTROCYTE_OIDC_ISSUER=https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0ASTROCYTE_OIDC_AUDIENCE=api://astrocyteRegister an app in Azure AD, expose an API with scope, and configure tid claim mapping for multi-tenant scenarios.
ASTROCYTE_OIDC_JWKS_URL=https://YOUR_DOMAIN.okta.com/oauth2/default/v1/keysASTROCYTE_OIDC_ISSUER=https://YOUR_DOMAIN.okta.com/oauth2/defaultASTROCYTE_OIDC_AUDIENCE=astrocyteIn Okta admin: create an authorization server or use the default. Add astrocyte as an audience.
AWS Cognito
Section titled “AWS Cognito”ASTROCYTE_OIDC_JWKS_URL=https://cognito-idp.REGION.amazonaws.com/POOL_ID/.well-known/jwks.jsonASTROCYTE_OIDC_ISSUER=https://cognito-idp.REGION.amazonaws.com/POOL_IDASTROCYTE_OIDC_AUDIENCE=YOUR_APP_CLIENT_IDIn Cognito: create a user pool, add an app client, and use the app client ID as the audience.
Errors
Section titled “Errors”| Status | Detail | Cause |
|---|---|---|
| 401 | Bearer token required | Authorization header missing |
| 401 | Invalid OIDC token: … | Signature, issuer, audience, or expiry validation failed |
| 401 | Token missing sub | sub claim absent |
| 500 | ASTROCYTE_OIDC_JWKS_URL is not set | Env var not configured |
| 500 | ASTROCYTE_OIDC_ISSUER and ASTROCYTE_OIDC_AUDIENCE are required | Env vars not configured |
Access grants
Section titled “Access grants”Authentication identifies who is calling. Access grants control what they can do. Enable access control in astrocyte.yaml:
access_control: enabled: true default_policy: owner_only # "owner_only" | "open" | "deny"Define grants
Section titled “Define grants”access_grants: # Support bot can read and write to any shared bank - bank_id: "shared-*" principal: "agent:support-bot" permissions: [read, write]
# Alice has full control of her bank - bank_id: "user-alice" principal: "user:alice" permissions: [read, write, forget, admin]
# All users can read the public bank - bank_id: "public" principal: "*" permissions: [read]Per-bank grants
Section titled “Per-bank grants”banks: team-engineering: access: - principal: "agent:code-reviewer" permissions: [read] - principal: "user:*" permissions: [read, write]Top-level access_grants and per-bank banks.*.access are merged.
Permissions
Section titled “Permissions”| Permission | Operations |
|---|---|
read |
recall(), reflect() |
write |
retain() |
forget |
forget() with specific memory_ids or tags |
admin |
Bank config, forget(scope="all"), export/import |
* |
All of the above |
Default policies
Section titled “Default policies”| Policy | Behavior when no grants match |
|---|---|
owner_only |
Only the principal whose ID matches the bank pattern can access |
open |
Allow all operations (no enforcement) |
deny |
Deny all operations |
On-behalf-of (OBO)
Section titled “On-behalf-of (OBO)”When identity.obo_enabled: true and a context has both actor and on_behalf_of, effective permissions are the intersection of grants for both principals. This prevents privilege escalation when an agent acts on behalf of a user.
Admin token
Section titled “Admin token”Admin routes (/v1/admin/*) use a separate token, independent of the auth mode:
ASTROCYTE_ADMIN_TOKEN=admin-secret-herecurl http://localhost:8080/v1/admin/sources \ -H "X-Admin-Token: admin-secret-here"If ASTROCYTE_ADMIN_TOKEN is not set, admin routes are unprotected.
Environment variable summary
Section titled “Environment variable summary”| Variable | Required by | Description |
|---|---|---|
ASTROCYTE_AUTH_MODE |
All | Auth mode: dev, api_key, token, jwt_hs256/jwt, jwt_oidc |
ASTROCYTE_API_KEY |
api_key |
Shared secret for API key auth |
ASTROCYTE_TOKENS_FILE |
token |
Token registry (YAML or JSON) written by python -m astrocyte_gateway.tokens |
ASTROCYTE_JWT_SECRET |
jwt_hs256 |
Symmetric key for HS256 JWT verification |
ASTROCYTE_JWT_AUDIENCE |
jwt_hs256 (optional) |
Expected aud claim |
ASTROCYTE_OIDC_JWKS_URL |
jwt_oidc |
JWKS endpoint URL |
ASTROCYTE_OIDC_ISSUER |
jwt_oidc |
Expected iss claim |
ASTROCYTE_OIDC_AUDIENCE |
jwt_oidc |
Expected aud claim |
ASTROCYTE_OIDC_ACTOR_TYPE |
jwt_oidc (optional) |
Default actor type (default: user) |
ASTROCYTE_ADMIN_TOKEN |
Admin routes (optional) | Token for /v1/admin/* routes |
Further reading
Section titled “Further reading”- Configuration reference — full
astrocyte.yamlschema includingaccess_control,identity, andaccess_grants - Access control setup — grants, OBO, common patterns
- Memory API reference — how
AstrocyteContextis passed to retain/recall/reflect/forget - Production-grade HTTP service — TLS, rate limits, CORS, observability checklist
- ADR-002 Identity model — design rationale for structured identity
- Access control — design doc for permissions, grants, and policy enforcement