Tenant isolation
Telos is multi-tenant. Two independent layers would both have to fail for one organization to read another one's data.
- Application layer.Every service function runs inside a short database transaction with the caller's organization id set as a transaction-local Postgres setting. There is no other path to the database.
- Database layer. Row-level security is enabled on every table carrying an organization id, with an
org_isolationpolicy attached automatically rather than opted into per table. A missing organization context resolves to no rows, so the failure mode is a broken feature, not a cross-tenant read. - The runtime cannot bypass those policies. The application connects as a dedicated non-superuser Postgres role. In production it inspects its own role at boot and kills the process if that role could bypass row-level security.
- Checked on every deploy. Policies are re-applied by a script that fails the deploy if a qualifying table lacks its policy, or if a policy exists without its filtering expressions.
- Cross-organization work is explicit. One named escape hatch handles the few flows that need it, such as login lookups and scheduled sweeps. It requires a written reason at each call site.
Encryption
- TLS 1.2 or higher for all customer traffic. The database and object storage are encrypted at rest.
- Read-back secrets are AES-256-GCM encrypted.Anything Telos must decrypt later (your LLM provider key, integration credentials, TOTP secrets) is stored as a versioned authenticated-encryption blob under a master key held only as a deployment secret.
- Each ciphertext is bound to its own row.Additional authenticated data ties every blob to the record that owns it, so a ciphertext copied from one row to another fails to decrypt rather than quietly working. Your LLM provider key is bound to your organization id.
- No plaintext bearer credential exists in the database.Anything Telos only needs to compare (API keys, OAuth access and refresh tokens, two-factor backup codes) is stored as a SHA-256 hash. A database dump yields no usable key or token.
- Background job payloads never carry secret material, and the enqueue path asserts it.
- Production refuses to start if the master encryption key is missing or is not a valid 32-byte key.
Authentication and authorization
- Passwords are hashed with bcrypt. Login rate limits are checked before the hash is computed, so password hashing cannot be used as a denial-of-service lever.
- Two-factor authentication via TOTP authenticator apps, self-service, with ten single-use backup codes. The TOTP secret is encrypted and bound to the user row; only hashes of the backup codes are stored, and a used code is marked consumed. Disabling the factor requires the current password, so a stolen session cannot strip it.
- Authorization is a capability system rather than role-name checks, enforced server-side. Routers that are not explicitly classified fail closed, and a test makes an unclassified surface a build failure.
- Field-level access controlon sensitive financial fields (opportunity cost, salary and hours). A field outside the caller's allowlist is stripped from the response before serialization, so the browser never receives it.
- Machine access uses prefixed bearer keys and OAuth 2.1 with PKCE and refresh-token rotation. Reuse of a rotated refresh token revokes the entire token family.
Transport and browser hardening
The application sets these response headers site-wide:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadX-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: camera=(), microphone=(), geolocation=(), browsing-topics=()Content-Security-Policy: base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'
The OAuth consent screen carries a stricter policy that sets frame-ancestors 'none', so an Authorize button cannot be framed. Requests to the bare apex domain are permanently redirected to the canonical https://www.telos-app.com origin, so sessions are never split across hosts.
Abuse control and rate limits
Limits
One shared fixed-window limiter, Redis-backed so the cap holds across application instances, with an in-memory fallback that degrades rather than failing open.
- Login: 10 attempts per 15 minutes per email and 30 per 5 minutes per IP, checked before any user lookup or password hash.
- Signup, contact, waitlist: 5 per hour per IP.
- Password reset: 5 per hour per email and 15 per hour per IP to request one, plus a per-IP cap on the token-consuming call, which also runs bcrypt.
- API keys, OAuth tokens, MCP: 600 per minute per credential. OAuth callers are counted against the refresh-token family rather than the individual token, so rotating does not reset the bucket.
- Inbound connector webhooks: 600 per minute per integration, applied before the database lookup, which also bounds probing for unknown identifiers.
- File upload intents: 30 per minute per user.
Webhook verification
Inbound webhooks verify the sender's signature before any processing, write to an idempotent ledger with a uniqueness constraint, acknowledge, and then process asynchronously. Outbound webhooks Telos sends are signed with a timestamped HMAC and a 300-second replay window, so you can verify us.
The AI boundary
Telos is bring-your-own-model: you configure the provider and the key, and prompts are routed under your own agreement with that provider. That creates two trust boundaries, both enforced in code.
- Outbound requests are SSRF-guarded. Because you can point Telos at any provider URL, every model request passes through a guard that validates the scheme, rejects internal hostnames, resolves DNS, and refuses the request when any resolved address falls in a private, loopback, link-local, or reserved range, including the cloud metadata range. Redirects are refused outright, so a response cannot pivot to an internal host.
- Model output is data, never code and never authority.Responses are parsed as schema-validated structured objects. Model output is never executed and never carries an authorization decision.
- Your provider key stays yours. It is encrypted at rest with your organization id as additional authenticated data and decrypted only at request time.
Data residency and file storage
- Customer data is stored in the EU. Application servers, the Postgres database, and file storage all run in the same EU region.
- Every object key is prefixed with the owning organization id, so tenant isolation is visible at the storage layer. The download path re-checks that prefix before signing a URL, behind the service-layer check.
- Buckets are private. Downloads use presigned GET URLs valid for 15 minutes and scoped to one exact object key, so a URL cannot be edited to reach another file.
- Uploads use a presigned POST valid for 5 minutes whose policy pins the exact key, content type, byte length, and SHA-256 checksum. Bytes go straight to storage and are never proxied through the application server. A file that fails integrity or MIME validation is marked failed and no download URL is ever issued for it.
Infrastructure and residency commitments are set out in Exhibit C of the Data Processing Agreement.
Logging, monitoring, and retention
- Per-entity activity is recorded for operational and security review. Authentication events are logged.
- Errors go to Sentry, tagged with organization and job. The SDK is pinned to send no personal data: no IP addresses and no request bodies. Performance tracing and session replay are both off, so no recording of your screen exists anywhere.
- Operational log retention is swept automatically: webhook event records are deleted after 30 days and unpinned notifications after 90 days, in batches so a sweep cannot stall the database. User-facing history is deliberately not swept.
- Availability is monitored by an authenticated operations endpoint and a dead-man heartbeat, so a crashed worker cannot fail to report its own death.
- Uptime and incident history are published at status.telos-app.com.
Telos support access
Telos staff have no access to your workspace by default. There is no standing back door and no support impersonation.
- An administrator in your organization grants support access explicitly and can revoke it at any time. The active grant and its expiry are shown to every member of the workspace, not just to the admin who granted it.
- A grant is time-boxed to 7 days and modelled as an ordinary, editable role: read access to product data, excluding compensation, cost, API keys, LLM configuration, and integration and webhook settings, plus the ability to comment and post messages. Nothing else is writable.
- Expiry is enforced when the session is resolved on every request, so a revoked or lapsed grant is dead on the next request rather than at the next scheduled sweep.
Incident response
Telos maintains a written security incident runbook covering detection sources, triage and severity, containment, notification, evidence collection, and post-incident review.
On a personal data breach, Telos notifies the affected customer without undue delay and in any case within 72 hours of becoming aware, as committed in Clause 4 of the Data Processing Agreement.
Subprocessors
The current list of subprocessors, what each one processes, and where it runs, is Exhibit B of the Data Processing Agreement. It is maintained in one place so this page cannot drift out of step with the contractual list.
Telos notifies the account owner by email at least 15 days before adding or replacing a subprocessor, and you may object on reasonable data protection grounds within that window.
Reporting a vulnerability
Report suspected vulnerabilities to info@telos-app.com. Include the affected URL or endpoint, the steps to reproduce, and what you were able to access. You will get a human reply.
Please do not run automated scans against production, do not attempt to access or modify data belonging to another organization, and give us a reasonable window to fix an issue before disclosing it. Machine readable contact details are published at /.well-known/security.txt per RFC 9116.