Authentication — Signet Docs

Docs / Reference

Authentication

Token-based authentication for the daemon API.

Auth

Auth is optional. By default the Daemon runs in local mode: no tokens, no authentication, localhost-only binding. Auth exists for team and remote deployments where the daemon is shared or exposed over a network.

The system is intentionally simple — no external dependencies, no OAuth flows, no user database. Portable harness connectors should use named API keys generated by the CLI. Legacy session tokens are signed with HMAC-SHA256 using a secret that lives on disk. Roles and scopes are embedded in the token or API-key record. The dashboard also supports an admin username/password login that issues a normal admin session token; SSO and SAML paths are reserved so those providers can be added without changing the dashboard auth boundary later.

Auth Modes

local (default) — No authentication required. All requests are granted full access without any token. The daemon should only be accessible from localhost when running in this mode. Best for single-user local development.

team — Every request must include a valid Bearer token. Use this when the daemon is accessible over a network or shared among multiple users. Unauthenticated requests get a 401 Unauthorized.

hybrid — The middle ground. Localhost requests can proceed without a token (full access), but if a token is included it will be validated. Remote requests always require a valid token. This lets local tooling work without credentials while still securing remote access.

Note: localhost detection in hybrid mode uses the TCP peer address. If the daemon cannot determine a localhost peer, it treats the request as remote and requires a valid bearer credential. Do not rely on hybrid behind a same-host reverse proxy for untrusted traffic: proxied public requests can arrive from 127.0.0.1 and be treated as local. Use team mode or enforce auth at the proxy for public deployments.

API Key Management

API keys are the preferred credential for remote connectors. They are named, revocable, and stored hashed at rest. The raw key is printed once when it is created. For a complete cross-machine connector walkthrough, see Remote Harness Connectors.

signet api-key create --name "work laptop pi" --connector pi --agent-id pi-work-laptop
signet api-key list
signet api-key revoke <id-or-prefix>

--agent-id is an auth scope, not just a label. A key created with --agent-id <agent-name> defaults authenticated requests to that agent, and scope checks reject requests for other agents.

Connector clients send the key as bearer auth:

Authorization: Bearer sig_sk_...

Use SIGNET_API_KEY on remote machines:

SIGNET_DAEMON_URL=https://signet-home.tailnet:3850 \
SIGNET_API_KEY=sig_sk_... \
signet connector install pi --agent-id pi-work-laptop

For a Codex client that must attach to a specific agent, create the scoped key on the daemon machine and install Codex with that key:

signet api-key create --name "codex tailnet" --connector codex --agent-id <agent-name>
npx -y @signetai/codex-plugin install --url https://signet-home.tailnet:3850 --api-key sig_sk_...

Verify the scope from the remote machine without printing the key:

curl -fsS "$SIGNET_DAEMON_URL/api/auth/whoami" \
  -H "Authorization: Bearer $SIGNET_API_KEY"

SIGNET_TOKEN remains a backwards-compatible alias for older integrations.

API keys can also carry an optional explicit permissions list. When present, that list narrows the key beyond its role; for example an admin key with only ["recall"] cannot perform admin mutations. Connector keys created with --connector default to the narrower connector permission set: recall, remember, and documents. For non-connector keys, omit permissions for the full role-level permission set.

Token Management

Tokens use a simple signed format: {base64url(payload)}.{base64url(hmac)}. The payload is a JSON object encoded with base64url. The signature is HMAC-SHA256 over the encoded payload, also base64url-encoded. There are no external dependencies — only Node’s built-in crypto module.

The signing secret is 32 random bytes, auto-generated on first use and stored at $SIGNET_WORKSPACE/.daemon/auth-secret (mode 0600). If you rotate the secret, all existing tokens are immediately invalidated.

Token claims:

ClaimTypeDescription
substringSubject identifier (who the token is for)
rolestringOne of: admin, operator, agent, readonly
scopeobjectOptional restriction (see Scope below)
iatnumberIssued-at timestamp (Unix seconds)
expnumberExpiry timestamp (Unix seconds)

Default TTLs: 7 days for regular tokens (defaultTokenTtlSeconds: 604800), 24 hours for session tokens (sessionTokenTtlSeconds: 86400). Both are configurable in agent.yaml.

Generate a token: POST /api/auth/token with { role, scope? } in the request body. Inspect the current token: GET /api/auth/whoami. Dashboard sessions use POST /api/auth/login with { username, password } and receive a shorter-lived admin bearer token. See HTTP API for full endpoint details.

Roles and Permissions

Four roles, ordered from most to least privileged:

admin — Full access including admin operations. Can do everything any other role can do, plus the admin permission which gates administrative endpoints.

operator — Everything except admin. Has full memory access plus documents, connectors, diagnostics, and analytics. Intended for trusted automation or infrastructure tooling.

agent — Core memory operations plus documents. Does not have access to connectors, diagnostics, analytics, or admin. The default role for AI assistant integrations.

readonlyrecall only. Can query memory but cannot write, modify, or delete anything.

Permission matrix:

Permissionadminoperatoragentreadonly
rememberyesyesyesno
recallyesyesyesyes
modifyyesyesyesno
forgetyesyesyesno
recoveryesyesyesno
documentsyesyesyesno
connectorsyesyesnono
diagnosticsyesyesnono
analyticsyesyesnono
adminyesnonono

In local mode, permission checks are bypassed entirely — all operations are allowed regardless of any claims present.

Scope

Tokens can be scoped to restrict access to a subset of resources. A scope is an object with up to three optional fields: project, agent, and user. When a scope field is set on a token, requests touching a different value for that field will be rejected with 403 Forbidden.

An unscoped token (empty scope object) has full access within its role’s permissions. The admin role bypasses scope checks entirely — admin tokens always have full resource access regardless of scope.

Scope restrictions only apply in team and hybrid modes. In local mode, scope is ignored.

Example: a token scoped to { agent: "mr-claude" } can only access memory records associated with that agent. Requests targeting a different agent will be rejected.

Rate Limiting

The daemon applies a sliding window rate limiter to destructive operations. State is in-memory and resets on daemon restart. Rate limits are only enforced in team and hybrid modes — local mode has no limits.

Default limits (per actor per minute):

OperationLimit
forget30
modify60
batchForget5
forceDelete3
admin10
inferenceExplain120
inferenceExecute20
inferenceGateway30
recallLlm60

The actor is identified by the token’s sub claim when present. For unauthenticated requests (hybrid mode, localhost), requests share the "anonymous" bucket.

When a limit is exceeded, the daemon returns 429 Too Many Requests with a Retry-After header indicating how many seconds until the window resets.

All rate limit configs can be overridden in agent.yaml under the auth.rateLimits key.

Configuration

Auth is configured in agent.yaml under the auth: key (see Configuration). All fields are optional — omitting the section entirely gives you local mode with defaults.

auth:
  mode: local  # local | team | hybrid
  defaultTokenTtlSeconds: 604800  # 7 days
  sessionTokenTtlSeconds: 86400   # 24 hours
  login:
    password:
      username: admin
      # Prefer SIGNET_ADMIN_PASSWORD or SIGNET_ADMIN_PASSWORD_HASH in env.
      # passwordHash may also be set here as pbkdf2-sha256$iterations$salt$hash.
      passwordHash: null
    sso:
      enabled: false
    saml:
      enabled: false
  rateLimits:
    forget:
      windowMs: 60000
      max: 30
    modify:
      windowMs: 60000
      max: 60
    batchForget:
      windowMs: 60000
      max: 5
    forceDelete:
      windowMs: 60000
      max: 3
    admin:
      windowMs: 60000
      max: 10
    login:
      windowMs: 60000
      max: 5
    inferenceExplain:
      windowMs: 60000
      max: 120
    inferenceExecute:
      windowMs: 60000
      max: 20
    inferenceGateway:
      windowMs: 60000
      max: 30

The secret path is always $SIGNET_WORKSPACE/.daemon/auth-secret and is not configurable. The daemon creates it automatically on first start in any non-local mode.

Password dashboard login is enabled when either SIGNET_ADMIN_PASSWORD, SIGNET_ADMIN_PASSWORD_HASH, or auth.login.password.passwordHash is set. The username defaults to admin and can be overridden with SIGNET_ADMIN_USERNAME or auth.login.password.username. Plaintext passwords are only accepted from the environment; persisted config should use a pbkdf2-sha256$... hash.

HTTP Headers

Authorization: Bearer <token> — Required in team mode. Optional in hybrid mode for localhost requests (validated if present, not required). <token> may be either a named sig_sk_... API key or a legacy signed token. The dashboard stores the session token client-side and sends it as this header on daemon API requests.

x-signet-actor-type: operator|agent — Actor type hint used by certain policy decisions (e.g. repair action gating). Optional.

Deployment Guide

Single user, local machine — Use the default local mode. No configuration needed. The daemon listens on localhost:3850 and any local client has full access.

Shared team server or CI — Use team mode. Configure an admin dashboard password via environment, then generate tokens for each consumer with appropriate roles:

SIGNET_ADMIN_USERNAME=admin \
SIGNET_ADMIN_PASSWORD='choose-a-long-random-password' \
signet daemon
# Generate an operator token for a CI pipeline
curl -X POST http://localhost:3850/api/auth/token \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <your-admin-token>" \
  -d '{ "role": "operator", "sub": "ci-pipeline" }'

# Generate a readonly token for a monitoring script
curl -X POST http://localhost:3850/api/auth/token \
  -H "Authorization: Bearer <your-admin-token>" \
  -d '{ "role": "readonly", "sub": "monitor" }'

Developer machine with remote access — Use hybrid mode. Local tools (the CLI, dashboard, AI assistants) work without credentials. Remote tools like CI jobs, teammates, or portable harness connectors need an API key. Set mode: hybrid in agent.yaml and distribute keys only to remote consumers.

Scoped agent tokens — When multiple AI agents share a daemon, issue each one a scoped agent token to prevent cross-agent memory access:

{
  "role": "agent",
  "sub": "project-assistant",
  "scope": { "agent": "project-assistant" }
}

Rotating the secret invalidates all outstanding tokens and dashboard sessions immediately. Do this if a token is leaked. The daemon will regenerate a new secret at $SIGNET_WORKSPACE/.daemon/auth-secret if the file is deleted, then restart.