Skip to content
Docs menu

For companies

Security model

How the team server authenticates people, separates their data, and what it deliberately does not do. Written for the person who has to approve it.

Authentication

  • Personal Bearer tokens, 32 random bytes each (URL-safe encoded), issued with tam-team token-create.
  • The server stores only the SHA-256 hash of each token. The plain token exists only in the file written at creation (mode 0600, never overwritten).
  • Every request to /mcp/ and /api/call needs Authorization: Bearer …; missing or unknown tokens get 401. Revoked tokens and tokens of disabled users stop working on the next request.
  • The author and client label of every write come from the token, never from request data.
  • There is no OAuth or single sign-on. MCP clients always use tokens.

Dashboard sign-in

New in 14.6.0

  • The web dashboard uses passwords set from a one-time invite code (100 bits, stored as a SHA-256 digest, 72-hour default lifetime, voided when a new one is issued). A new invite is also the password reset.
  • Passwords are 12–256 characters, hashed with scrypt and a per-user salt, compared in constant time. Unknown users and wrong passwords get the same error and similar timing.
  • Sessions live on the server; the browser holds a random ID in an HttpOnly, SameSite=Strict cookie (__Host- prefixed over HTTPS). They expire after 30 idle minutes and 12 hours in total. State-changing requests need a CSRF token and a JSON body.
  • Lockout: 5 failures for one user from one IP in 15 minutes lock that pair for 15 minutes; 30 failures from one IP lock that IP. Behind a reverse proxy, set TAM_TEAM_TRUST_PROXY=true so the real client IP is used (see dashboard).
  • The dashboard is served with a strict Content-Security-Policy and no inline scripts or styles.

Authorisation

  • Access is decided by scope and membership only. Tags cannot grant access; the tags scope:, team: and user: are reserved and rejected in requests.
  • Membership and token validity are checked on every request, again before work is sent to a worker process, and again before results are returned. Removing someone from a team takes effect immediately.
  • reader members cannot write to a team scope; such writes fail with a forbidden tool error (“Workspace is read-only”).
  • Shared scope is open to every authenticated user for reading and writing. Put only company-wide material there.
  • Administration (users, teams, memberships, tokens, backup, restore) is available through the local tam-team command on the server. New in 14.6.0 Superadmins can also manage users, departments, memberships, tokens and provider settings in the dashboard. Backup and restore stay CLI-only.
  • New in 14.6.0 Organisation roles: company_viewer and superadmin can read every team’s memory, but write only where they are an editor or manager. Personal memory stays private to its owner for every role, superadmins included. The server refuses to disable or demote the last active superadmin.

Isolation

  • Each personal scope, each team scope and the shared scope is a separate SQLite database with its own graph and search index, served by a separate worker process. A search across scopes runs in each allowed scope and merges the results.
  • Workspace directories are keyed by a hash of the user or team id. The data directory is created owner-only (0700) and the identity database is 0600; backup copies are written 0600 too.
  • Tools that read files on the server machine are not exposed remotely.

Integrity and history

  • An edit creates a new revision; previous versions, author, UTC time, reason and before/after state are kept, also after a soft delete.
  • expected_revision rejects an update based on a stale copy with a conflict error instead of overwriting a colleague’s change.
  • A record change and its audit entry are written in one transaction.
  • request_id (UUID) makes writes idempotent: a retried request returns the first result; the same UUID with a different payload is rejected. New in 14.6.0 Request IDs are tracked per scope, so the same UUID in another scope is a new request.
  • New in 14.6.0 A write that the worker already committed is reported as a success even if the author’s membership or token was removed while it ran; reads keep the check and are withheld.
  • After a timeout the server does not retry a write. Check the record’s history before retrying manually.

HTTP hardening

  • Browser requests whose Origin does not match the Host are rejected (403); token requests never use cookies.
  • Request bodies are limited to 1 MB (413) and must arrive within 15 seconds (408). At most 32 requests are processed at once (429 beyond that).
  • MCP responses carry Cache-Control: no-store. The web page is served with a restrictive Content-Security-Policy, X-Frame-Options: DENY and nosniff.
  • /healthz reports only status, name and version: no paths, no users.
  • The client bridge refuses plain HTTP to non-loopback hosts and refuses redirects, so a token cannot be downgraded or forwarded elsewhere.

Provider keys

New in 14.6.0

API keys entered in the dashboard are encrypted at rest (Fernet) with a master key from TAM_TEAM_MASTER_KEY or <data directory>/master.key (created 0600; the server refuses to start if it is readable by others). Keys are never sent back to the browser. Backups do not contain the master key: store it separately, or restored keys become unreadable. See The master key.

Logging and metrics

Each call writes one structured JSON log line with the tool name, status (ok, rejected, error) and duration. Memory content and tokens are never logged. The service keeps a request counter and a latency histogram per tool.

Data that leaves the server

None, unless you configure a remote LLM or embedding provider. Then the text used in those tasks is sent to that provider for all users’ scopes. Keep Ollama local or set MEMORY_LLM_ENABLED=false if content must stay on your infrastructure. See Privacy.

Your responsibilities

  • Run it behind HTTPS; keep the app port on 127.0.0.1.
  • Distribute tokens through a secure channel and keep revocation copies in a password manager.
  • Back up regularly and store backups encrypted and off the machine; they contain every scope.
  • New in 14.6.0 Keep the dashboard master key (master.key or TAM_TEAM_MASTER_KEY) in a secrets manager, separate from backups.
  • New in 14.6.0 Set TAM_TEAM_TRUST_PROXY=true only when your own proxy overwrites X-Forwarded-For and X-Forwarded-Proto.
  • Restrict network access (VPN, IP allow-list) if the server should not be reachable from the internet.
  • Report vulnerabilities as described in the repository’s SECURITY.md.

Found a mistake? Open an issue on GitHub.

Search