Skip to content

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.


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.


Trusts the client-supplied principal header with no verification. Use only for local development and testing.

.env
ASTROCYTE_AUTH_MODE=dev

Client request:

Terminal window
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).


Shared secret verified with constant-time HMAC comparison. The client sends both the key and a principal header.

.env
ASTROCYTE_AUTH_MODE=api_key
ASTROCYTE_API_KEY=sk-your-secret-key-here

Client request:

Terminal window
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.


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.

.env
ASTROCYTE_AUTH_MODE=token
ASTROCYTE_TOKENS_FILE=/etc/astrocyte/tokens.yaml

The 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.

Terminal window
# Alice: read and write every project bank, and a member of team:api
python -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 job
python -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 revoked
python -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.
Terminal window
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"}'

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 principal matches 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.


Symmetric-key JWT verification. The issuer and gateway share a secret. The sub claim becomes the principal.

.env
ASTROCYTE_AUTH_MODE=jwt_hs256
ASTROCYTE_JWT_SECRET=your-256-bit-secret
ASTROCYTE_JWT_AUDIENCE=astrocyte # optional but recommended
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)
Terminal window
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"}'
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

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.

.env
ASTROCYTE_AUTH_MODE=jwt_oidc
ASTROCYTE_OIDC_JWKS_URL=https://auth.example.com/.well-known/jwks.json
ASTROCYTE_OIDC_ISSUER=https://auth.example.com
ASTROCYTE_OIDC_AUDIENCE=astrocyte
ASTROCYTE_OIDC_ACTOR_TYPE=user # optional, default "user"
  1. Client sends Authorization: Bearer <token> (RS256-signed by your IdP)
  2. Gateway extracts kid (key ID) from the JWT header
  3. Fetches the matching public key from ASTROCYTE_OIDC_JWKS_URL (cached ~10 min)
  4. Verifies RS256 signature, iss, and aud claims
  5. Maps claims to a full AstrocyteContext with structured identity
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",
)
Terminal window
ASTROCYTE_OIDC_JWKS_URL=https://YOUR_DOMAIN.auth0.com/.well-known/jwks.json
ASTROCYTE_OIDC_ISSUER=https://YOUR_DOMAIN.auth0.com/
ASTROCYTE_OIDC_AUDIENCE=astrocyte

In Auth0 dashboard: create an API with identifier astrocyte. Configure your application to request this audience in the token.

Terminal window
ASTROCYTE_OIDC_JWKS_URL=https://keycloak.example.com/realms/YOUR_REALM/protocol/openid-connect/certs
ASTROCYTE_OIDC_ISSUER=https://keycloak.example.com/realms/YOUR_REALM
ASTROCYTE_OIDC_AUDIENCE=astrocyte

In Keycloak admin: create a client with astrocyte as the audience. Enable “Service Accounts” for machine-to-machine flows.

Terminal window
ASTROCYTE_OIDC_JWKS_URL=https://login.microsoftonline.com/YOUR_TENANT_ID/discovery/v2.0/keys
ASTROCYTE_OIDC_ISSUER=https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0
ASTROCYTE_OIDC_AUDIENCE=api://astrocyte

Register an app in Azure AD, expose an API with scope, and configure tid claim mapping for multi-tenant scenarios.

Terminal window
ASTROCYTE_OIDC_JWKS_URL=https://YOUR_DOMAIN.okta.com/oauth2/default/v1/keys
ASTROCYTE_OIDC_ISSUER=https://YOUR_DOMAIN.okta.com/oauth2/default
ASTROCYTE_OIDC_AUDIENCE=astrocyte

In Okta admin: create an authorization server or use the default. Add astrocyte as an audience.

Terminal window
ASTROCYTE_OIDC_JWKS_URL=https://cognito-idp.REGION.amazonaws.com/POOL_ID/.well-known/jwks.json
ASTROCYTE_OIDC_ISSUER=https://cognito-idp.REGION.amazonaws.com/POOL_ID
ASTROCYTE_OIDC_AUDIENCE=YOUR_APP_CLIENT_ID

In Cognito: create a user pool, add an app client, and use the app client ID as the audience.

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

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"
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]
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.

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
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

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 routes (/v1/admin/*) use a separate token, independent of the auth mode:

.env
ASTROCYTE_ADMIN_TOKEN=admin-secret-here
Terminal window
curl http://localhost:8080/v1/admin/sources \
-H "X-Admin-Token: admin-secret-here"

If ASTROCYTE_ADMIN_TOKEN is not set, admin routes are unprotected.


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