The components
Two supported shapes
Single process
api binary, one PostgreSQL, one Redis. This is the default when you set
nothing, and it is a complete TRUSTIVAN — not a degraded mode. It is the right
answer for an evaluation, a small team, or a first on-premises install that will
not want to run more processes than the product needs.
Split processes
If the two can only run together, “a scan survives a worker restart” is a story rather than a property, and nobody discovers otherwise until production.Splitting them by default means the durability path is exercised every time anyone runs the stack. It also matches the workload’s actual shape — requests are milliseconds, scans are minutes — so the two want to scale independently. See Scaling. Both shapes run the same code.
cmd/worker and an in-process worker assemble
the same runner; only the HTTP server differs.
What the worker needs, and does not
The worker needs outbound network access to the registries you scan, a writable cache directory, and connections to PostgreSQL and Redis. It does not need a Docker socket, a container runtime, privileged mode, or any access to a local image store. The scanner resolves images from remote registries only. Mounting a runtime socket into the process that handles untrusted input would be root on the host, and consulting a local image store would let a caller-supplied reference resolve to another tenant’s image. That absence is a real security property, and it simplifies placement: the worker is an ordinary unprivileged process that makes outbound HTTPS calls.Network position
TRUSTIVAN does not terminate TLS. Put it behind a reverse proxy that does.
One constraint that catches people: sessions are
HttpOnly cookies with
SameSite=Lax, so the API and the dashboard must share a registrable domain.
api.example.com and app.example.com work; example.com and example.net do
not. The default arrangement proxies the API under the dashboard’s own origin,
which is same-site trivially. A genuinely cross-site deployment must use API
keys, which the middleware accepts and which are not ambient.
DASHBOARD_URL and WEBSITE_URL are the CORS allow-list, and CORS permits
credentials. There is deliberately no wildcard option, which is what makes that
safe.
What the deployment tooling includes, and what it does not
Stated plainly, because discovering either later is worse. Compose on a single host is the recommended first deployment, and it is a gated sequence rather than oneup -d: preflight (refuses on anything unsafe),
backup, migrate as a one-shot job, start, health, smoke, and a rollback that
restores the previous images and leaves the schema alone.
Kubernetes is the growth path and it exists. Kustomize base plus a DEMO and
a PRODUCTION overlay, with make k8s-validate, make deploy-demo, make deploy-prod, make status, make logs and make rollback. The DEMO overlay is
verified end to end on a real k3s cluster; the PRODUCTION overlay has never been
applied, and cannot be until images are published to the registry it pins by
digest.
Terraform provisions a cluster, optionally. It provisions the machine and
never deploys TRUSTIVAN, so a cluster that already exists needs none of it. Only
Hetzner is implemented, and it has never been applied to a real account.
What genuinely does not exist:
- No Helm chart. Kustomize, deliberately: the decisions encoded in the manifests are the valuable part, and a templating language would replace them.
- No AWS, Azure or GCP Terraform modules. Not implemented and not stubbed.
-
No
make deploy-stagingormake deploy-production. Those names do not exist. The Kubernetes targets aremake deploy-demoandmake deploy-prod; the Compose sequence isinfra/scripts/deploy.sh, run on the target host. There is deliberately no singlemake deploy— a deploy targets an environment that owns its own secrets. - No CLI. Every operation a person or a pipeline performs goes through the HTTP API.
-
No SLSA provenance. Every published image is signed — the release
workflow signs each digest with keyless cosign and attaches the image’s
CycloneDX SBOM as a signed attestation, both stored beside the image as
referrers so the digest you pin does not change — but nothing records how
the image was built beyond what the signing certificate says (that
workflow, at that tag, at that commit). Verify a release before pinning it:
Use cosign v3 (the workflow pins v3.1.3). Keyless signing writes to Sigstore’s public transparency log: each entry names the repository, the workflow, the tag, the commit and the image digest.
- TRUSTIVAN verifies no signature on anything it scans. Signing its own releases is not the same as checking yours: no image, SBOM or artifact you point a scan at is signature- or provenance-verified.
A production checklist
ENVanything other thandevelopment— unset included — means missing secrets are fatal rather than generated. A typo fails closed.DATABASE_URLmust not name the schema owner. Tenant isolation is enforced in the database as well as in the application, and the schema appliesFORCE ROW LEVEL SECURITY, so an owner connection is still filtered — but the owner holds the privilege to disable that in one statement, and to drop the tables. The owner belongs inMIGRATION_DATABASE_URL, which only the migration step uses. No role in any connection string should be a superuser or holdBYPASSRLS; those bypass the policies outright, and outside development the backend refuses to start with one inDATABASE_URLorJOB_DATABASE_URL.JOB_DATABASE_URLis required wherever the worker runs: a login role grantedtrustowl_job. Without it the worker’s cross-tenant queue queries would run as the application role and see no queued scan, so the worker refuses to start rather than accept scans it can never run.- Persist
SCANNER_CACHE_DIRon a volume, or every restart re-downloads the vulnerability database. - Leave
SCAN_ALLOW_PRIVATE_REGISTRIESandWEBHOOK_ALLOW_PRIVATE_DESTINATIONSatfalse. The second is refused at startup outside development anyway. - Point your orchestrator’s liveness probe at
/healthand its readiness probe at/ready. They are different endpoints for a reason.
Next
- Scaling — adding replicas, and why no leader election is involved.
- Health and metrics — what to monitor.
- Upgrades — migrations, backups, and rollback.

