Engineering/Architecture

The architecture,
as built.

A working description of how SecurityVault is put together — the components, the three hard trust boundaries, the scan lifecycle, the tenancy model, the ontology pipeline and the cryptography. Written from the code and the normative architecture reference, for the people who will integrate and audit it.

5
API traffic planes
read · write · callback · stream · admin, one router registry, scaled independently.
3
Hard trust boundaries
Split container, split secret, authenticated callback. Every scan crosses all three.
15
Worker deployments
Code, cloud, supply chain, data, discovery, governance, GRC, triage, lake and more.
1
Path that writes findings
The scanner callback handler. No worker constructs a finding row directly.
Components

Control plane, scan band, and the stores.

Trusted components run in the platform namespaces. Untrusted work runs as ephemeral Jobs in a scan namespace with no route back except the callback.

Control plane · trusted
API planesFastAPI · create_app(Role)
Auth serviceOIDC · SAML · RS256
Domain workersCelery · 15 deployments
Event consumersprojections · SSE
Policy evaluatorOPA subprocess
AI agentlocal model · advisory
Scan band · untrusted
Namespacesecurityvault-scans
Per-scan Jobfetcher + N tool pairs
Tool containerSV_TOOL · SV_RESULTS_DIR
Adapter containerparse · sign · POST
DeadlineSV_TOOL_DEADLINE_SECONDS
Lifetime≤ 1 h · garbage-collected
Stores
PostgreSQL 16pgvector · RLS
Redisbroker · cache · nonces
ClickHousesecurity lake
Object storageS3-compatible
Vaultsecrets · signing keys
RegistryHarbor · Cosign
API planes
One application factory builds every plane from a single router registry.api-read serves queries, api-write mutations, api-callback only the scanner and webhook callbacks, api-stream server-sent events, andapi-admin cross-tenant platform administration. Each scales on its own autoscaler; a flood of callbacks cannot starve the UI.
Workers
Celery workers orchestrate; they never execute scanners. Pools are split by domain — code security, cloud security, supply chain, data security, discovery, attack surface, governance evaluation, GRC operations, ingestion, triage, AI triage, lake and backfill — so a backlog in one domain stays in that domain.
Events
State changes publish versioned events. Projection workers materialise read models such as the security-entities projection that every entity-centric screen reads; the stream plane pushes changes to open browsers.
Trust boundaries

Three boundaries every scan must cross.

A compromised scanner is the assumed case, not the edge case. The boundaries are enforced by pod specification, by Kubernetes Secrets and by the API — not by policy documents.

B1

Split container

The tool container holds the scanner binary and the acquisition credentials for the target. The adapter container holds the platform token. They share one results volume, read-only on the adapter side. A scanner with remote code execution reaches the repository it was already given and nothing more.

B2

Split secret

Two Kubernetes Secrets per job. scan-acquire-creds (Git or registry token) mounts only into the tool container. scan-adapter-creds(SCAN_TOKEN, BODY_SIGNING_KEY) mounts only into the adapter. No container ever holds both. The adapter hard-exits if any storage credential appears in its environment.

B3

Authenticated callback

Results enter the control plane only through api-callback. The scan token is opaque, single-use, database-backed and bound to the scan, the tool and the job; the body is HMAC-signed and carries a replay nonce. Both are verified before a byte is parsed.

Not in the pod
Database credentials. Redis credentials. Vault tokens. Object-storage keys. Worker service tokens. A mounted service-account token. The banned-pattern gate in CI refuses any scanner module that imports a database driver or constructs a finding.
Native engine
appsec-unified, our own engine, is held to exactly the same rules as Trivy or Semgrep. It runs as a Job, it reports through the callback, and there is no privileged in-process fallback. If it breaks, scans fail closed.
Scan lifecycle

One state machine. No ambiguous end.

Every transition goes through one module and records its evidence. Terminal states say what happened, not just that something did.

PENDING
task dispatched
QUEUED
Job started
RUNNING
callbacks
COMPLETED
PARTIAL
FAILED · FAILED_TIMEOUT · FAILED_DEPENDENCY · FAILED_CONFIG
FromToTriggerRecorded
PENDINGScan requestedScan row, audit event
PENDINGQUEUEDCelery task dispatchedTask id
QUEUEDRUNNINGKubernetes Job startedJob name, tokens minted, per-tool deadlines
RUNNINGCOMPLETEDAll tools reportedResults ingested, audit event
RUNNINGPARTIALSome tools failed or missed their deadlinePer-tool outcome, partial results kept
RUNNINGFAILED_TIMEOUTJob deadline exceededReaper verdict with the tool that timed out
RUNNINGFAILED_DEPENDENCYMissing credential or policyDependency error
RUNNINGFAILED_CONFIGInvalid scan configurationConfiguration error with hint
Callback contract

The only door into the database.

The adapter posts a signed result. The callback handler verifies, then resolves it into the ontology under the tenant the scan belongs to. Nothing the pod says about its tenant is trusted.

Token
Per-scan opaque single-use SCAN_TOKEN, scope-bound to scan, tool, job and the server-derived organization. Verified by database lookup; purged when the job is garbage-collected.
Signature
X-SV-Body-Signature over canonical JSON with a key derived per scan. Constant-time comparison.
Replay
X-SV-Nonce recorded in a per-scan set. In required mode the callback fails closed if the nonce store is unreachable.
Body cap
64 MiB request-size limit at the ASGI layer before any parsing.
Tenant
Write routing uses scan.organization_id. Any organization field in the payload is advisory and ignored for authority.
Spool
Accepted callbacks are durably spooled before ingestion so a worker restart cannot lose a delivered result.
Tenancy

Row-level scope, proven in CI.

Every session binds the acting organization before the first query. Row-level security policies exist for every tenant table and are asserted by the test suite on every merge.

01

Bound sessions

Authenticated requests bind app.current_org_id and the acting principal through one set of database binders. Search path is always public; there are no per-tenant schemas.

02

Header is the authority

The tenant comes from the verified session, never from a caller-controlled parameter. A CI gate refuses any handler that reads a bare org_id before resolving it against the session.

03

Single-resource reads

Every read, update or delete of one resource filters on organization_id in the SQL — a primary-key-only match is treated as a cross-tenant defect and blocked at review.

04

Platform admin is separate

Cross-tenant admin routes take a platform-scoped session that can never bind a tenant and must run under the admin database role, or answer 503.

05

Human-only verdicts

A database trigger refuses any disposition change without a bound acting principal, so no worker, reaper or AI task can flip a verdict even with a valid connection.

06

Schema discipline

Forward-only SQL migrations with SHA-256 verification of applied files; CI proves a populated upgrade converges with a fresh installation before a release is cut.

Ontology pipeline

From a tool result to a lineage view.

Raw results are observations. Observations become entities, edges, logical findings, cases, attack paths and risk scores through a deterministic pipeline that emits a versioned event at each step.

Observation
accept · quarantine
Resolution
aliases · precedence
Entities & edges
provenance-tagged
Logical finding
correlation rules
Case
traversal
Attack path
model v1
Risk
Producers
SCM webhooks, CI ledgers, registry watchers, cluster state and cloud collectors each produce edges with a provenance class. Repository → Commit → Build → ContainerImage → Registry → Workload is the code-origin template the lineage view walks.
Strict paths
Lineage, blast radius and exploit views use observed and derived edges only. Inferred edges are rendered as candidates and never complete a strict path.
Read-time states
missing and conflicting hops are computed when the view is built and never persisted. Storing a missing edge would fabricate a relationship.
Bounds
Blast-radius walks are capped by hop count and entity count. Sibling findings on the same subject are excluded from the count so the number measures impact, not observation volume.
Correlation
Seeded rules compile to an evaluator over entity neighbourhoods; matches project into security cases. Rules run in shadow mode before they are allowed to create cases.
Risk
A versioned model on a fixed scale with factor groups and an explanation per score. Incremental scoring re-evaluates only what an event touched; portfolio views roll up per organization.
Cryptography

Standard primitives. Documented uses.

No invented protocols. Each algorithm below has one job.

WherePrimitiveDetail
User sessionsRS256 JWT15-minute access token with a kid header; refresh token in an httpOnly cookie. RS256 is mandatory outside development; HS256 verification paths are closed.
Stored credentialsFernet (AES-128-CBC + HMAC-SHA256)Customer Git, registry and cloud credentials encrypted at rest; decrypted only inside the worker that mints the scan Secrets.
Control decisionsEd25519Canonical sorted-key payload: control, decision, framework control, input hash, organization, key fingerprint, schema version, timestamp. Private key held by the evaluation service; public half distributable to auditors.
Evidence & inputsSHA-256Evidence hashing and evaluation-input hashing so a decision can be tied to exactly what it saw.
Audit logHMAC chainAppend-only records, each carrying the HMAC of its predecessor.
Scanner callbacksHMAC body signaturePer-scan derived key, canonical JSON, replay nonce, constant-time compare.
Ticketing webhooksHMAC + noncePer-organization, per-provider key with the same replay guard as scanner callbacks.
ImagesCosignPlatform and scanner images signed; an admission webhook can refuse unsigned images in the cluster.
TransportTLS 1.2+ / 1.3Edge TLS at the gateway; internal mTLS from the platform PKI for service-to-service calls.
Identity & access

Your IdP. Three-layer RBAC.

01

Authentication

OIDC and SAML single sign-on, SCIM 2.0 provisioning, MFA. Okta, Microsoft Entra ID, Google Workspace and CyberArk connectors ship in the platform.

02

Authorization

Permission bindings are the access authority. Eight personas — developer, AppSec, cloud, SOC, GRC, executive and more — only shape the home view; they never grant access.

03

Governance writes

Finding status and disposition are governance fields. An ORM-level guard blocks AI and worker writes; a database trigger backs it up. Reason codes are mandatory.

04

CI principals

Pipelines authenticate with OIDC workload identity; PR comments and gate results are posted under a CI principal with per-organization rate limits.

05

Dual control

Sensitive governance actions require a second approver; approval scope is checked at the action, not at login.

06

Audited prompts

Every natural-language query to the AI filter is audited whether accepted or rejected. The model is never re-prompted on failure, closing the model-probing surface.

Failure domains

What breaks, how far it reaches.

ScenarioBlast radiusContainment
Scanner remote code executionOne scan; the repository it was givenNetwork isolation, ephemeral credentials, pod termination, no platform credentials present
Scanner out of memory or crashOne tool in one scanPer-tool deadline and resource limits; scan ends PARTIAL with the other results kept
Worker crashTasks queued on that workerAutomatic restart; tasks re-queued from the broker
Broker unavailableNew scans waitRedis persistence, reconnect with backoff; nothing is silently dropped
Nonce store unavailableCallbacks refusedFail closed in required mode; the adapter retries, results are not lost
Policy evaluator errorThe decision being evaluatedDENY with OPA_EVALUATION_FAILED; never an allow
Database primary failureWrites until promotionStreaming replication and standby promotion; point-in-time recovery from WAL
IdP unreachableNew SSO loginsExisting tokens valid until expiry; retry with backoff
Operations

Observable by default. Gated on every merge.

01

Telemetry

Structured logging, Prometheus metrics on every plane and worker, OpenTelemetry tracing through the API and Celery. Logs are shipped to a central store per environment.

02

Merge gates

Dozens of architectural-invariant checks run on every merge request: banned patterns, tenant-parameter authority, structlog floor, Cilium policy shape, bundle budgets, route manifest, credential rotation versions, accessibility on redesigned routes.

03

Test layers

Unit, worker, property and integration suites in Docker; Playwright end-to-end against the production build; schema proofs on a fresh and an upgraded database.

04

We scan ourselves

The platform scans its own repository and images on the same pipeline it sells, and the finding burndown is tracked in the repository.

05

Supply chain

Images built rootless, signed with Cosign, pulled through a private registry mirror; dependency and base-image updates go through the same gates.

06

Network policy

Cilium network policies scoped by label; an empty selector is rejected by CI because it would put every pod into default-deny at once.

Technology stack

Boring where it matters.

LayerTechnologyPurpose
APIFastAPI · Pydantic v2Async REST with strict request models (extra="forbid", length caps)
Data accessSQLAlchemy 2.0Async sessions on the request path, sync in workers
DatabasePostgreSQL 16 · pgvectorPrimary store, audit ledger, row-level tenant policies
QueueCelery 5 · RedisOrchestration, scheduling, rate limits, replay nonces
LakeClickHouseHigh-volume security events and analytics
PolicyOpen Policy AgentDeterministic Rego evaluation, fail closed
AnalysisTree-sitterLanguage grammars for the native taint analysers
ExecutionKubernetes · CiliumScan Jobs, platform deployment, network policy
SigningCosign · Ed25519 · HMACImages, decisions, audit chain, callbacks
FrontendReact 19 · TypeScript · MUI · TanStack Query · ViteSingle-page application with strict CSP
Testingpytest · Vitest · Playwright · axe-coreBackend, frontend, end-to-end, accessibility
FAQ

The questions your architect will ask.

Where does state live?
PostgreSQL holds entities, edges, findings, decisions and the audit ledger. ClickHouse holds high-volume events. Object storage holds artifacts and evidence bundles. Everything else — API planes, workers, scan Jobs — is stateless and replaceable.
Can a scanner write to my database?
No. There are no database credentials or drivers in a scanner pod, and the CI banned-pattern gate refuses any scanner module that imports one. Results reach the database only through the authenticated callback.
What happens if the policy engine is down?
Every evaluation error is a deny with an explicit reason code. There is no path that returns an allow without a successful evaluation.
Does the AI ever change data?
No. AI triage, remediation suggestions and natural-language filters are advisory. Governance fields are guarded at the ORM layer and by a database trigger; the AI has no principal and fails closed.
How do you prove a finding really came from that commit?
Lineage edges carry a provenance class. A repository-to-image link established by a verified attestation or an OIDC subject is observed; a link by CI cross-reference on a full SHA or digest is derived; a tag or label match is inferred and never completes a strict path. The view shows which channel established each hop.
Can the scan band run in our environment while you host the control plane?
Yes. An execution cluster is registered per organization with its API endpoint, authentication method (kubeconfig, OIDC or token — IRSA, GKE and AKS workload identity supported), scan namespace, network zone and parallel-job cap. It becomes eligible only after an admin approves the CA fingerprint. The worker creates the scan Job in your cluster; the adapter posts results outbound to api-callback. Deregistration is a soft delete so the audit trail survives.
Can I run it entirely inside my own environment?
Yes. The same Helm charts and signed images run on your Kubernetes with your PostgreSQL, Redis, ClickHouse and S3-compatible storage. No telemetry leaves unless you configure it to.
Architecture deep-dive

Bring your hardest architect.

A 60-minute session with the engineers who built this. We answer from the code, on the record.