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_sessioncookie. 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
subis 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
localhostonly outside prod), canonical public origins, and the temporary legacy-cookie deletion scope (dev.threada.aiorthreada.ai). Live session cookies are unconditionally Secure and never setDomain. Google is the only self-service signup provider. Travel remains an explicit guest-only surface ontripsly.aiuntil a signed one-time cross-domain session handoff or same-site BFF exists; it does not weakenSameSite=Strictor expose a non-executable sign-in action. Production startup also rejects a missing, placeholder, or shorter-than-32-byteJWT_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. EveryAudienceRestrictionmust contain the configured SP entity ID; responseDestinationand bearerRecipientmust exactly equal the configured ACS URL;InResponseTomust 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 omittedKeyInfoare 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
unknownbucket. 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_KEYis configured; otherwise it emits the explicitlog-hash-key-unconfiguredsentinel, 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 moderateandcargo auditare run on every build. CI blocks moderate-and-above findings.- Root
pnpm-workspace.yamloverrides,onlyBuiltDependencies,patchedDependencies, andallowBuildsentries are treated as supply-chain exceptions: each entry must be represented incargo run --quiet -p quality-runner -- suppression-policywith 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.