Security and privacy
This page covers only what you can do: know where traffic goes, put credentials in the right place, know where data is kept, and rotate. tansr’s internal defenses (permission engine, protected paths, hard security red lines, audit chain) are laid out in the guides for each form; here they are cited only where they touch your own actions.
Egress: where traffic goes and how to narrow it
Section titled “Egress: where traffic goes and how to narrow it”What the CLI connects to
Section titled “What the CLI connects to”| Destination | When | Can it be turned off |
|---|---|---|
The tansr platform gateway api.tansr.com |
After signing in to platform mode: model calls, bundle fetch, heartbeat, usage reporting, balance | Set TANSR_ESCAPE_LOCAL=1 for fully local mode and it is disabled as a whole |
The official distribution point bash.tansr.com |
Only when you explicitly run /update or tansr update; never checked automatically at startup |
TANSR_UPDATE_CHANNEL=none or TANSR_NO_UPDATE_CHECK=1; companies can point to a self-hosted manifest with TANSR_UPDATE_MANIFEST_URL |
| The model providers you configure | providers.*.baseUrl in fully local mode |
Your decision |
| The MCP servers / your own search and media endpoints you configure | Tool calls within a session | Your decision; project-level MCP servers must be trusted explicitly |
| Any URL the model reaches through tools (WebFetch / Http) | Within a session | Governed by permission rules; a deny rule can disable the whole tool |
The recommended baseline for unattended deployments (tansr serve / headless with permissions set to auto) is an egress allowlist: allow only necessary destinations such as the model API, and deny by default beyond DNS and already-established connections; combine it with a container or a least-privilege dedicated user, and limit filesystem writes to the working directory. Reference templates are in the repository under deploy/serve/.
Two user-visible defenses already in place: a single command that both references a secret file (.env, keys, .aws/credentials) and performs an outbound action (curl / scp / git push …) is always refused, and no mode or rule can override that; and once a session has read a secret file, every later outbound action turns into a prompt (session taint). Coming in tansr CLI 0.5.0: all egress (WebFetch / Http / hooks / audio download) goes through one unified exit that resolves DNS first and then rejects private, loopback and link-local addresses, so fetches aimed at internal or metadata addresses are refused with a reason; and the credential environment variables the Http tool and hooks may reference are narrowed to the http.allowedAuthEnvVars allowlist — nothing outside it is sent.
What the SDK and the session service connect to
Section titled “What the SDK and the session service connect to”- With the token source,
@tansr/sdkconnects only to thebaseUrlyou pass (the platform gateway) and the MCP servers you configure explicitly; the SDK never scans the user’s home directory and never has a built-in search endpoint. - The upstream chain of
@tansr/serveisappid/appkey → POST /v1/app-tokens → GET /t1/config + POST /t1/heartbeat → POST /t1/exchange, all againstapiBaseUrl; plus the end-of-turn webhook URL you configure (outbound requests never carry any authentication header — authenticity is proven by the signature) and the optional cold-tier object storage. - The mobile SDKs connect only to your session service’s
baseUrl, never directly to the platform.
Credentials: where they go and where they never go
Section titled “Credentials: where they go and where they never go”| Credential | Represents | Right place | Never |
|---|---|---|---|
| Personal access token (PAT) | You | Generated in the console, then fed only to tansr init (masked input) or the TANSR_PAT environment variable in CI |
Shell history (--pat on the command line leaves a trace), plaintext in scripts, the repository |
| This machine’s derived app key | This device of yours | Written by tansr init to the OS keychain (macOS Keychain / Linux secret-service / Windows DPAPI); falls back to a 0600 file with a clear warning when unavailable |
You never touch it; the credential-storage section of tansr doctor shows where it landed |
| Model-provider API key (fully local mode) | Your provider account | An environment variable; the config file holds only the variable name in auth.env |
settings.json, error messages, /config output (they never carry the value) |
| App key appid / appkey | Your app | Environment variables of your backend process (a Node process or @tansr/serve) |
Client devices, logs, error responses, the repository (.env goes in .gitignore) |
Short-lived app_user token |
One of your end users | Electron main-process memory; inside the @tansr/serve process |
The renderer process, persistent storage; mobile never touches it |
| Your own login token (mobile) | Your user | Provided per request through authProvider; the SDK does not persist it |
SharedPreferences / DataStore / UserDefaults / logs |
Session-service /v1 Bearer (--token / TANSR_SERVE_TOKEN) |
The operator | An environment variable | Unrelated to the /v2 surface; do not reuse it for end users |
Webhook secret |
Your push backend | Environment variables on the session service and on the receiver, separately | Logs; the engine never prints it |
Three red lines worth repeating: the appkey never leaves your backend; the app_user token never leaves the session service; the session id is not an authentication factor.
The CLI adds one more layer of redaction discipline as a safety net: keys in reports, logs and exported transcripts are always redacted to the form sk-... plus the last 4 characters; the output of tansr sessions export can go straight into a ticket.
Data retention: where, how long, who deletes
Section titled “Data retention: where, how long, who deletes”Locally (the CLI)
Section titled “Locally (the CLI)”- Sessions, config cache, usage queue and project memory live under
<storageRoot>/(default~/.tansr, redirectable withTANSR_STORAGE_ROOTor the overall rootTANSR_USER_HOME); after signing in to the platform, new data is isolated per account under<storageRoot>/identities/<account fingerprint>/. - One directory per session:
journal.jsonl(hash chain),meta.json,attachments/, oversize artifacts inspill/; image bytes are cached under<storageRoot>/image-cache/<sessionId>/; long Shell output goes to<working directory>/.tansr/shell-output/; TTS audio goes to<working directory>/.tansr/artifacts/<sessionId>/. - Nothing is deleted automatically. Clean up with
tansr sessions prune(a dry run listing only by default;--applydeletes; the retention policy is the union of--keep-days, default 90, and--keep-last, default 500; today’s sessions and sessions in use are never deleted; corrupted directories need--include-broken).tansr logoutdoes not delete identity-store data. - The
metrics.jsonwritten at the end of a session holds only counts and ratios — no paths, commands or rule text. Outbound telemetry is off by default: the current version has no outbound reporting channel at all. - The platform does not store terminal session content; it only meters; the platform session registry has a 30-day inactivity lease, after which sessions are archived.
The session service (@tansr/serve)
Section titled “The session service (@tansr/serve)”- Session history lands in the
storedirectory you configure,<dir>/agent-sessions/<endUserKey>/sessions/<sessionId>/;endUserKeyis a hash of the end-user id, not an identity. A cold tier can be mounted on your object storage; segments are plaintext JSONL (compressed) — encryption, key management, residency and access control are yours (thepolicy.transformhook). - Short in-memory windows: terminal records are kept for 30 min, idle sessions are written to disk after 24 h;
DELETE /v2/sessions/:idcloses, it does not delete. Today the store keeps everything by default; a storage retention sweep (deleting dormant sessions by age in days and per-user count) is coming in @tansr/serve 0.7.0. Real deletion isstore.delete. - Structured log fields never contain tokens or message content; metric labels never contain sessionId / endUserId.
- Permission request frames sent down to clients never carry full tool arguments, only a target summary; webhook payloads never carry message content or credentials.
On the device
Section titled “On the device”The Android / iOS SDKs cache no credentials; view state is in memory; whether to keep the sessionId locally (for resume after a cold start) is your decision — store only the id, not content, not credentials.
Rotation and revocation: how each credential is replaced
Section titled “Rotation and revocation: how each credential is replaced”| Credential | How to rotate | Blast radius |
|---|---|---|
| PAT | Create a new one in the console, sign in again locally with tansr init --fresh, then delete the old PAT in the console |
A PAT is never revoked by tansr logout — other devices may still be using the same one |
| This machine’s derived app key | Rerun tansr init to rotate and extend (near expiry the session shows a “key about to expire” notice); tansr logout clears the local copy and does its best to deactivate it remotely; for a lost device, deactivate its device key manually in the console |
Every device holds its own key (named by hostname + device id); they never replace each other |
| App key appkey | Rotate in the console (POST /v1/apps/:id/rotate); update the backend’s environment variable and restart the process |
Cuts everything off as a last resort: the old appkey is invalidated immediately |
| One end user’s tokens | Revoke per end user in the console or via the management API (POST /v1/apps/:id/app-user-revocations); all of that user’s existing tokens are rejected at once. Ending their login state in your own system also cuts supply naturally (token TTL 60–86400 s, no renewal) |
Affects only that user |
| The whole app | Deactivate the app in the console | Everything downstream is rejected |
Session-service /v1 Bearer |
Change TANSR_SERVE_TOKEN / --token and restart |
/v1 callers must switch at the same time |
Webhook secret |
The receiver accepts both the old and the new value first, then the session-service side is switched, then the old value is retired | Zero lost deliveries in between |
| Your own login token (mobile) | Belongs to your login system; on 401 the SDK re-fetches through authProvider up to 2 times, then enters a terminal state and waits for you to reconnect explicitly |
A 401 right after a refresh means revocation or misconfiguration, not jitter |
On the platform side, only one code signals that a token is dead: unauthorized (401). When the session service receives it, it evicts the cached token, re-mints and resends once within the same request; a client SDK that sees it runs the 2-attempt convergence described above. Other codes (403, 429, 5xx) do not mean the token is bad — do not re-mint because of them.
Quick self-check
Section titled “Quick self-check”tansr doctor: did the credential land in the OS keychain or a plaintext file? Does thelocalProviderssection match what you expect (platform mode / escape hatch)? Any unattended-baseline notice?grepyour repository once forTANSR_APP_KEY/sk-/tansr_sk_to confirm no real value was committed.- When the session service binds
0.0.0.0, is/metricsexposed to the internal network only? Is thestoredirectory a local disk, not shared? - Does your token mint endpoint mint only after your own login, and does it always take
endUserIdfrom the server-side session rather than trusting the request body? - Are lifecycle rules and encryption configured on the cold-tier object storage?
Was this page helpful?
Thanks for your feedback.