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/callneedsAuthorization: Bearer …; missing or unknown tokens get401. 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=Strictcookie (__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=trueso 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:anduser: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.
readermembers cannot write to a team scope; such writes fail with aforbiddentool 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-teamcommand 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_viewerandsuperadmincan 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 is0600; backup copies are written0600too. - 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_revisionrejects an update based on a stale copy with aconflicterror 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
Origindoes not match theHostare 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 (429beyond that). - MCP responses carry
Cache-Control: no-store. The web page is served with a restrictive Content-Security-Policy,X-Frame-Options: DENYandnosniff. /healthzreports 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.keyorTAM_TEAM_MASTER_KEY) in a secrets manager, separate from backups. - New in 14.6.0 Set
TAM_TEAM_TRUST_PROXY=trueonly when your own proxy overwritesX-Forwarded-ForandX-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.