Reference

Security & access

Token types (personal, agent, service), scopes, repository and route limits, 2FA, the secret check and allowSecret, lawful purge and the audit trail.

View as Markdown

Who can do what in Weave, how tokens are limited, and the guards that keep secrets and history safe. For how we run the service, see the Security page.

Token types#

TokenPrefixWho it isCallsLifetime
Personal access token (PAT)weave_A personPlatform /api/v1/*, the Weave server /api/weave/*, the CLI1–365 days (default 30)
Agent tokenwva_A registered agent (agent:<id>), acting for the person who issued itThe Weave server and the CLI; not /api/v1 (except worker agents removing / restoring workstreams)≤ 24 hours (default 60 minutes)
Service tokenwvs_A service account: an organization-owned, non-personal identity that can never sign inEverything a PAT can, except issuing PATs (POST /api/v1/tokens) and managing service accounts1–365 days (default 90)

Every token is stored only as a SHA-256 hash and shown once, when it is created. Validate any token with GET https://weave.directory/api/whoami:

json
{ "id": "agent:cm9x2…", "name": "builder-1", "kind": "agent",
  "agent": { "id": "cm9x2…", "name": "builder-1", "kind": "worker", "actingFor": "<userId>" },
  "scopes": ["repo:read", "repo:write"], "repoAccess": { "cm3k…": "write" },
  "repos": ["cm3k…"], "repoRefs": [{ "id": "cm3k…", "name": "shop", "org": "acme", "slug": "acme/shop" }],
  "pathScope": ["app/**", ".weave/decisions/**"], "tokenId": "…" }

kind is user, agent or service. pathScope appears only for agents with a path limit; allowedRoutes only for confined tokens.

Service accounts#

A service account belongs to an organization, has no password, sign-in or email, and acts only through wvs_ tokens. Use one for a builder or CI so nothing runs as one person. Managing them needs a person's PAT from an owner or admin of the organization (service and agent tokens are refused: no self-escalation).

bash
curl -s https://weave.directory/api/v1/orgs/acme/service-accounts \
  -H "Authorization: Bearer $WEAVE_PAT" -H "Content-Type: application/json" \
  -d '{"name": "builder", "role": "admin"}'

curl -s https://weave.directory/api/v1/orgs/acme/service-accounts/$SA_ID/tokens \
  -H "Authorization: Bearer $WEAVE_PAT" -H "Content-Type: application/json" \
  -d '{"name": "builder-prod", "scopes": ["org:admin", "repo:admin", "tokens:write"], "expiresInDays": 90}'

A service token may register agents and issue agent tokens; those agents then act for the service account. Disabling a service account deletes its tokens and revokes agent tokens acting for it.

Scopes#

ScopeAllows
repo:readRead repositories
repo:writeStitch and weave (includes repo:read)
repo:adminAdminister and delete repositories (includes repo:write)
org:adminAdminister organizations: create repositories, register agents
tokens:writeIssue and revoke tokens
  • A token never exceeds its owner's real access: scopes only narrow it. On the Weave server, no repo:write means read-only, no repo:admin means at most write, and no repo:* scope means no repository access.
  • A token can't issue a PAT or agent token with scopes or repositories beyond its own.
  • A token stored with no scopes (an old one) counts as having all five.

Limiting a token#

LimitOnEffect
repositoryIdsPATs, agent tokens, service tokensOnly these repositories. A token limited to repositories can't create repositories (403 insufficient_scope).
allowedRoutesService tokensUp to 50 patterns METHOD /path/glob (* one segment, ** the rest). Applies to /api/v1 and every Weave server route, including the WebSocket handshake. GET /api/whoami is always allowed. Refused: platform 403 route_not_allowed; Weave server 403 forbidden.
pathScopeAgentsGlobs the agent may write to. Writes outside: 403 path_out_of_scope. See For AI agents.

Example allowedRoutes: ["GET /api/weave/*/workstreams", "POST /api/weave/*/stitch", "* /api/v1/repos/r1/**"].

Repository access levels#

The platform tells the Weave server each caller's level per repository: read, write or admin. Archived repositories are capped at read. Access comes from organization membership, teams and direct collaborators. Mutations need write; admin-only actions (protection, tags deletion, purges, mirrors, imports, audit export, deciding suggestions as admin) need admin.

Two-factor authentication#

  • 2FA (TOTP) is available to everyone: avatar → Settings → Account → Set up 2FA.
  • Owners and admins of an organization need 2FA to administer it (personal organizations are exempt). Without it they act as plain members. Over the API the call answers 403 two_factor_required.
  • An organization can require 2FA of every member (org Settings → Security → Require two-factor authentication). Members without it lose access to its private repositories, through PATs too.
  • Issuing a PAT needs a verified email (403 email_unverified) and 2FA when an organization requires it. Service accounts count as compliant.

Secret detection#

The same secret detector runs in the CLI (weave add, weave stitch) and on the server for every stitch, web edit, change-request resolution, blob upload and seed, whatever sends it. It finds:

  • secret-looking file names: .env, .env.local, .env.*.local, .env.development*, .env.production*, .env.test*, *.pem, *.key, *.p12, *.pfx, id_rsa*, *.tfstate*, *.tfplan, .terraform/**;
  • content: PEM private keys, AWS access key ids, GitHub tokens (ghp_…, github_pat_…), OpenAI and Anthropic API keys, Stripe live keys (sk_live_…, rk_live_…), Slack tokens (xoxb-…, xoxa-…, xoxp-…) and Google API keys.

Only what a request introduces counts. The refusal names the file, line and kind, never the secret:

json
{ "type": "error", "code": "secret_detected", "message": "…",
  "findings": [{ "path": "config/keys.ts", "line": 4, "kind": "AWS access key id" }] }

The CLI refuses before sending. weave init writes a starter .weaveignore with the secrets section above.

Allowing one deliberately#

weave add --allow-secret <path> and weave stitch --allow-secret <path> (repeatable; a file or folder) include such a file on purpose. Over the API, send "allowSecret": ["<path>", …] in the body ("" = everything). The server honours it for repository admins only and records it in the operation log (via: "allow_secret: <paths>").

Lawful purge#

A lawful purge removes content — a leaked secret, personal data — from a repository's whole history without breaking a single hash. The content is withheld; its hash stays known, so states, changesets and the log still verify.

KindRemoves
blobOne binary file's bytes
textEvery occurrence of a string (8–4,096 characters), anywhere in history

It takes two different repository admins: one requests, another approves. Agents can do neither. A request expires after 7 days.

bash
# admin 1: request (the text is never returned, logged or sent in events)
curl -s https://api.weave.directory/api/weave/$REPO_ID/purges \
  -H "Authorization: Bearer $ADMIN1_PAT" -H "Content-Type: application/json" \
  -d '{"text": "AKIA…the leaked key…", "reason": "leaked AWS key (rotated)"}'

# admin 2: approve
curl -s -X POST https://api.weave.directory/api/weave/$REPO_ID/purges/$PURGE_ID/approve \
  -H "Authorization: Bearer $ADMIN2_PAT" -H "Content-Type: application/json" -d '{"note": "checked"}'
  • A text purge is refused (409) while any active workstream head still holds the text. Stitch the fix and rotate the secret first, then purge history.
  • Approval withholds the content in every stored state, redacts changesets, suggestions, change requests, resolution points and event payloads, replaces rendered copies with a placeholder, deletes exports and drops search embeddings. A manifest records what was withheld; its hash is in the log.
  • After a purge, the file reads as [weave] The content of this file was removed by a lawful purge (<id>).. weave clone writes the placeholder, weave status lists it under "Withheld by a lawful purge", and new content simply replaces it.
  • Not rewritten: the operation log itself, git mirrors already pushed, repository copies and other people's local checkouts.
  • Other routes: GET …/purges[?state=], GET …/purges/{id}, GET …/purges/{id}/manifest, POST …/purges/{id}/cancel. Webhooks: purge.requested, purge.approved, purge.cancelled.

Audit trail#

  • Operation log. Every action in a repository is in an append-only, hash-chained log (weave log, weave log --verify, GET /api/weave/{repoId}/log/verify).
  • Checkpoints. Every hour the server verifies each changed repository's chain and writes checkpoints to write-once storage, each run chained to the previous one. GET /api/weave/{repoId}/audit/verification shows status (verified, broken with brokenAt, or pending) and the latest checkpoint; ?deep=true (admin) re-reads the stored checkpoint.
  • Export. GET /api/weave/{repoId}/audit/export?from=&to=&after=&limit=&format=json|ndjson (admin). Each entry's id is the hash of its canonical JSON, so the export verifies itself.
  • Token use. Every authenticated request to the platform is recorded (time, token id, method, path, a hash of the IP, trace id) for 90 days. Read your own: GET /api/v1/tokens/{id}/usage; a service account's: GET /api/v1/orgs/{slug}/service-accounts/{id}/usage.

Rate limits#

WherePerLimit
Platform /api/v1IP1200 / minute
Platform /api/v1token600 / minute
Weave serverIP6000 / minute
Weave servertoken3000 / minute

Over a limit: 429 rate_limited with Retry-After: <seconds>. Back off, or spread work across tokens.