Skip to content
threada Docs

Security

This page summarises how Threada protects customer data and the controls available to administrators. It’s the customer-facing companion to our internal threat model and captured security baselines.

Identity and access

  • GCP Identity Platform is the identity backbone. Verified Google identities support explicit self-service account creation; passwordless email links support existing or invited users. Microsoft sign-in remains disabled until its provider configuration and end-to-end validation are complete.
  • Central sign-in boundary: browser sign-in runs through Admin. Work, Studio, Invest, and Real Estate hand off with an exact configured HTTPS return origin (loopback HTTP is allowed only for local development), and session credentials remain in a Secure, HttpOnly, API-host-only __Host-threada_session cookie. Shells call the canonical BFF origin and each protected REST/gRPC method verifies the exact owning shell origin; different registrable domains require a signed server handoff.
  • Cookie-only browser credentials: passwordless, social, SAML, and development completion responses return non-secret session metadata only. Neither the reusable BFF session JWT nor an Identity Platform refresh token is exposed to browser JavaScript, memory, URLs, or web storage. JWT expiry, cookie lifetime, and the response expiry use the same configured session TTL.
  • Distributed session revocation: BFF session JWT sub is the immutable local identity user ID, with email stored separately. Each protected request checks the token’s user, tenant, and session epoch against identity-tenancy. Logout persists an incremented per-user epoch before clearing the cookie, so sessions minted on any BFF instance are rejected; storage or dependency failures fail closed instead of reporting a successful logout.
  • Auth deployment policy: Pulumi declares the exact Admin Identity Platform authorized domain (plus exact localhost only outside prod), canonical public origins, and the temporary legacy-cookie deletion scope (dev.threada.ai or threada.ai). Live session cookies are unconditionally Secure and never set Domain. Google is the only self-service signup provider. Travel remains an explicit guest-only surface on tripsly.ai until a signed one-time cross-domain session handoff or same-site BFF exists; it does not weaken SameSite=Strict or expose a non-executable sign-in action. Production startup also rejects a missing, placeholder, or shorter-than-32-byte JWT_SHELL_SESSION_SECRET.
  • IdP-initiated SAML SSO via a tenant-managed identity provider. Admins configure the IdP in Admin → Identity, then start sign-in from the IdP with the tenant ID as RelayState; no SP-initiated discovery/AuthnRequest surface is exposed. Tenant OIDC SSO and SCIM are not executable product surfaces. A signed assertion from the configured IdP treats its email as verified when the nonstandard verification attribute is omitted; an explicit false value is rejected. Every AudienceRestriction must contain the configured SP entity ID; response Destination and bearer Recipient must exactly equal the configured ACS URL; InResponseTo must be absent. The assertion itself must be signed with exactly one reference to its assertion ID and include the matching X.509 certificate. Response-only signatures and omitted KeyInfo are unsupported. The production BFF image bundles its pinned xmlsec/OpenSSL verifier and cannot be built without a valid-signature success check plus a tampered-signature rejection check; no runtime package installation or mutable host dependency is used.
  • Roles per tenant: Admin, Editor, Viewer. Invitations carry a role assignment; the Admin shell enforces role-based access to every governance surface. Create invitation persists or rotates the invite and synchronously asks Identity Platform to send the email link. Success means the provider request was accepted, not delivered. Invitation tokens are never returned through the BFF-owned browser contract. New invitations must include at least one executable authorization pair: Admin + Governance, Work + Operator, or Studio + Builder.
  • API keys are tenant-scoped, revocable, and audited. Each key is bound to a scope set so it can only call the operations its owner needs.
  • Rate limiting is enforced per IP and per tenant at the API gateway. Public social/passwordless start and completion RPCs have a shared Cloud Armor 10-request/minute IP limit plus an in-process defense-in-depth limit. The BFF keys the local limit from the client hop appended by the Google load balancer only when exactly one load-balancer attestation header matches the generated Secret Manager value. Caller-supplied alternate proxy headers are ignored. Missing, invalid, or duplicate attestation and malformed forwarding chains — including direct internal and local requests — share the fail-closed unknown bucket. Cloud Run additionally accepts public-BFF ingress only from internal sources and Cloud Load Balancing, so the generated service URL is not a second public edge that bypasses Cloud Armor.
  • Enumeration-resistant passwordless start: after origin, input, bot, and rate-limit checks, the BFF returns the same fixed-delay generic acceptance for eligible, ineligible, and Identity Platform failure outcomes. Acceptance does not assert that an identity exists or that email was dispatched or delivered; a separately bounded background worker continues slow dispatch after the response deadline, and dependency failures remain observable through PII-free server telemetry.
  • Fail-closed bot assessment: when score-based reCAPTCHA Enterprise is enabled, the assessment must contain the exact expected action and a finite score at or above the configured threshold. Missing action, score, validity, or other required response fields is a rejection, not a bypass.
  • Authenticated service-to-service calls using GCP service-account OIDC identity tokens, so intra-cluster traffic carries a verified caller identity (validated at the gateway) over TLS.

Data isolation

  • Tenant isolation is enforced at every persistence layer: every query is scoped by tenant id; cross-tenant reads are denied at the repository layer rather than the application layer.
  • Public-domain knowledge reuse is the one intentional exception: when two tenants ingest the same public website, the index can be shared to avoid duplicate crawling. This applies only to content already passed through each tenant’s allowlist, only to publicly fetched HTML, and never to customer uploads.
  • Customer uploads are always tenant-scoped, never shared.

Encryption

  • In transit: TLS on every external endpoint; internal service-to-service traffic runs over TLS with GCP OIDC identity-token authentication.
  • At rest: GCP-managed encryption on all storage by default.
  • Field-level encryption for sensitive fields uses Tink with per-record Data Encryption Keys (DEKs). DEKs are stored encrypted in Secret Manager; the Key Encryption Key (KEK) lives in Cloud KMS. KEK rotation re-encrypts DEKs lazily.

SSRF and outbound safety

  • Per-tenant outbound allowlist. Outbound fetches from knowledge-sync and integration adapters go through an egress allowlist. Internal/cloud-metadata addresses (e.g. 169.254.169.254, GCP metadata) are denied by default.
  • Per-host QPS limits and backoff on every crawler.
  • Origin validation on runtime entrypoints rejects malformed or disallowed origins at token issuance and config resolution.

Bot and abuse hardening

  • CAPTCHA / invisible-token shielding on abuse-prone public entrypoints (sign-up, password-reset, public-form intake).
  • CSP and SRI on every deployed web asset.
  • Sanitisation of AI interaction content and safe HTML → text normalization in the runtime.

Auditability

  • Per-user activity audit logs for every governed action.
  • Audit export to GCS or BigQuery on demand, configurable in Admin.
  • Compliance evidence (tombstones) for deletion actions — stores hash, actor, timestamp, scope; no PII or original content retained.

PII and retention

  • PII classification and field-level redaction in logs. Identity tenant persistence telemetry emits keyed-HMAC identifiers only when OPS_LOG_HASH_KEY is configured; otherwise it emits the explicit log-hash-key-unconfigured sentinel, never a raw identifier, persisted status, or database error.
  • Public and ops request telemetry records route labels, query-presence booleans, and keyed path hashes; raw URI paths and query strings are not written to structured request logs, HTTP spans, or incident-alert messages.
  • Public gRPC-web edge auth logs use bounded gRPC route labels plus keyed path and tenant hashes, so unsupported paths and tenant identifiers are not written in plaintext during auth and allowlist failures.
  • CSP violation report telemetry keeps report kind, presence flags, body length buckets, allowlisted directive labels, query/fragment presence, and keyed body, URL, and directive hashes. Raw report bodies, URL origins, URL paths, non-HTTP URL schemes, query strings, fragments, CSP source lists, and script samples are not written to structured logs.
  • Public contact and newsletter submissions minimize browser context before support email delivery or logging: campaign/source labels are allowlisted low-cardinality tokens, submitted website URLs plus page paths and referrers drop query strings and fragments, credentialed or non-HTTP URLs are discarded, origins keep only scheme and host, and user-agent strings are reduced to coarse browser family.
  • Shared TypeScript telemetry redacts raw URL paths in Node structured logs and browser fetch/document-load spans. URL diagnostics keep origin plus query/fragment presence and a fixed <path-redacted> marker rather than customer identifiers, emails, invite tokens, or document names embedded in paths. Telemetry bootstrap, config-fetch, shell startup/theme/uncaught-error, and shutdown failures log bounded outcome, status, or error-kind fields, not raw exception messages that can include customer URLs or tokens.
  • External HTTP diagnostics keep status, body length, and truncation metadata, but response body previews are redacted for common emails and auth credentials before structured logging.
  • Shared internal gRPC error redaction returns a generic client status and logs only a fixed redaction marker, keyed error hash or visible unconfigured sentinel, coarse length bucket, presence flag, and stable context. Raw downstream/database error strings are not written to structured logs because they can contain connection URIs, query fragments, tenant identifiers, or contact details.
  • Identity tenant persistence telemetry uses keyed hashes for tenant IDs, slugs, domains, unknown persisted subscription values, and database failure details when its HMAC key is configured; otherwise it records the safe unconfigured sentinel. Lifecycle logs retain stable operation/outcome labels; raw identifiers and database-error strings are replaced with redaction markers.
  • Tenant-configurable retention windows for WorkItem history, message content, and audit events.
  • GDPR delete-all for a tenant on request, executed across MongoDB and snapshots.
  • PII policies in Admin govern automated handling: detection thresholds, masking rules, allowed processors.

Supply chain

  • SBOM and supply-chain scanning on every build.
  • KMS-backed attestation is required for CycloneDX and SLSA provenance in promotion gates; keyless attestations are not accepted.
  • Binary Authorization is enforced in production (dry-run in non-prod).

Dependency baseline

  • pnpm audit --prod --audit-level moderate and cargo audit are run on every build. CI blocks moderate-and-above findings.
  • Root pnpm-workspace.yaml overrides, onlyBuiltDependencies, patchedDependencies, and allowBuilds entries are treated as supply-chain exceptions: each entry must be represented in cargo run --quiet -p quality-runner -- suppression-policy with owner, rationale, allowed usage, replacement condition, and review date.
  • The 2026-02-19 dependency-baseline capture reported zero known vulnerabilities at moderate or above across both Node and Rust dependency trees. Current Rust advisory exceptions, when present, are time-bound in security/advisory-exceptions.yaml; treat newer status as CI-generated evidence, not as a hand-maintained claim in this page.

Threat model

A formal threat model covering prompt injection, XSS, SSRF, data exfiltration, token leakage, and supply-chain risks is maintained internally and reviewed quarterly. For customers under NDA we can share a summary on request.

Reporting

Found something? Email security@threada.ai. We acknowledge within one business day and follow coordinated disclosure.