Security model

Security built into the architecture, not bolted on after.

Every guarantee below is enforced by the code path, not by a policy document. Credential headers are stripped before any I/O. PII is scrubbed before any persistence. Row-level isolation runs inside the database, not just inside the application. The full list of structural invariants is below.

Defense in depth

Application-layer tenant filters are still the primary enforcement for data isolation — they should be. Everything on this page is the second layer: the assurance that if an application bug ever disagrees with the intent, the architecture wins. Forgotten WHERE clauses don't leak rows. Misconfigured replay targets don't probe the metadata service. Oversized POSTs don't allocate gigabytes of memory. Wrong-but-close tokens don't leak timing.

Guarantees, by construction

Credential headers stripped before any I/O

A non-negotiable floor of 16 authentication and session headers — Authorization, Cookie, every X-Api-Key / X-Auth-Token / X-Amz-Security-Token variant — is removed at the moment the proxy reads the request. Removed BEFORE recording. Before logging. Before any disk write. The floor is hard-coded, not configurable down; per-deployment add-ons can only EXPAND it.

  • ·16 headers redacted unconditionally on every request
  • ·Operator-configured headers add to the floor; they cannot shrink it
  • ·Applies to every tier, every mode, every code path

Body PII scrubbed on every persisted record

Request and response bodies pass through a structural scrubber before they reach Postgres or disk. Token formats, payment cards, government IDs, contact information, and cloud credentials are all caught by pattern; sensitive JSON keys (password, secret, api_key, refresh_token, cvv, mfa_code …) are caught by name and their values fully redacted regardless of content.

  • ·19 regex categories: JWTs, Bearer/Basic auth, every well-known API key prefix (Stripe, AWS, OpenAI, Anthropic, GitHub), payment cards (Visa/MC/Amex/Discover/Diners), US SSN, email, phone (US + E.164), Bitcoin/Ethereum addresses, IBAN, IPv4
  • ·22-element sensitive-key allowlist — values redacted by name, never inspected
  • ·Mock rows carry a scrubbed_at seal: the sync layer refuses to overwrite a scrubbed row with an unscrubbed one

Row-level isolation in the database, not just the app

Every tenant-scoped table is protected by a Postgres Row-Level Security policy. The request-bound database role has no BYPASSRLS privilege, so a forgotten WHERE clause in application code cannot leak rows. The session GUC is bound per-request — fresh connection, fresh binding. Service-account workloads (cron, sync) use a separate role.

  • ·22 Postgres tables enforce tenant_isolation policies
  • ·Two roles: request-bound (no BYPASSRLS) and service-account (BYPASSRLS, separate credential pool)
  • ·GUC bound via SET LOCAL — transaction-scoped, no leak between requests

Webhook replay can't probe internal infrastructure

When an operator replays a captured webhook, the target URL is resolved and every IP it points to is classified before any socket opens. Loopback, link-local (including cloud instance metadata), RFC1918 private space, carrier-grade NAT, and IPv6 link-local are all blocked. Blocked ports — SSH, SMTP, every common database driver — are rejected regardless of host. The threat: an authenticated tenant aiming captured POSTs at 169.254.169.254 or a peer service on the same VPC. The fix: refuse before connecting.

  • ·Resolved IPs classified against private/loopback/link-local/CGNAT before httpx opens a socket
  • ·AWS / GCP / Azure instance-metadata endpoints unreachable from replay
  • ·Database, message-broker, and remote-shell ports blocked even on public targets

Request bodies bounded, secrets compared in constant time

Every inbound request is rejected if its body exceeds the configured ceiling — at the layer, before the handler buffers it into memory. A hostile or buggy 10 GB POST cannot force a 10 GB allocation. Authentication tokens — API keys, capture tokens, session-token hashes — are compared in constant time. A wrong-but-close token does not leak its prefix via timing.

  • ·Body-size limit enforced at the streaming layer, not after allocation
  • ·Constant-time comparison on every API-key, capture-token, and session-token check
  • ·Production startup refuses to bind when the infrastructure key is unset

Authentication: password, SAML 2.0, OIDC

Three identity-provider backends are wired and pluggable. Password authentication uses bcrypt at cost factor 12 with a 72-byte input cap. SAML 2.0 supports IdP-initiated and SP-initiated flows, JIT user provisioning, and group-to-role mapping. OIDC ships with JWKS validation, authorization-code flow, configurable scopes, and the same JIT + group-mapping affordances.

  • ·bcrypt cost factor 12; SHA-256 hashing on API keys; constant-time digest comparison
  • ·SAML auto-provisioning of users on first login (configurable)
  • ·OIDC group claim → role mapping (owner / admin / member / viewer)

Four-role RBAC and an append-only audit trail

A four-rank role model — viewer < member < admin < owner — gates every state-changing endpoint. Authentication events (login, logout, SSO, password-reset, session-revoke) and activity events (mutations on services, mocks, repair proposals, scrub configs) are written to dedicated tables. The owner rank carries a hard invariant: the system refuses to demote the last owner of a tenant.

  • ·viewer / member / admin / owner ranks enforced server-side, not client-side
  • ·auth_audit_events: every login / logout / SSO event with IdP kind, IP, user agent, success flag
  • ·activity_log: every service, repair, scrub, and configuration mutation

License validation with offline grace

The agent verifies its license JWT on a five-minute refresh cycle and caches the validated features for four hours of platform unreachability. A regional outage on Gostly's side does not silently downgrade a customer's stack — they continue with their licensed tier until the grace window closes, at which point degradation is explicit and logged.

  • ·RS256 JWT signed by Gostly platform (KMS-backed key), verified via JWKS
  • ·5-minute fresh TTL · 4-hour stale grace · structured warning when serving stale
  • ·After grace: explicit degradation to free-tier features, never silent

What leaves your infrastructure

The captured traffic on your proxy stays on your proxy. Request bodies, response bodies, full URLs, authentication headers, cookies, API keys, session tokens, raw service identifiers, and customer IP addresses are never transmitted to Gostly servers and never read by any process outside your stack.

The agent does report opt-out-able usage telemetry — counts of recorded mocks, served mocks, generation success/failure, feature gate hits — for product analytics. Identifiers in that stream are salted SHA-256 hashes; raw identifiers and bodies never appear. The full schema is published at /telemetry along with the single environment variable that disables it.

On the platform side: account data, license validation requests, and Stripe billing events are the only cloud-resident state. The traffic library that powers your mocks is yours — on disk, on your Postgres, on your infrastructure.

Compliance posture

Gostly is engineered against the controls regulated buyers ask for: structural redaction, tenant isolation, role-based access, audit trail, SSO. Formal SOC 2 Type II and ISO 42001 attestations are in progress; we share security artifacts (architecture diagrams, the sub-processor list, deletion procedure) under NDA on request. Talk to us at sales@gostly.ai if you need a Trust Pack to walk into procurement with.

Want to see how the pieces fit together?

Read the architecture overview