# Architecture | Chainsaw

> Reference architecture for Chainsaw, the install-path firewall. Component map, four topologies (SaaS, VPC, on-prem multi-proxy, air-gapped), dataflow, identity flow, latency budget, HA model, telemetry surfaces, and security baseline.

Source: https://chain305.com/architecture/

---

Reference architecture

# Four topologies, one binary, identical policy.

An install-path firewall between your developers, CI and clusters and the sixteen registries they pull from.

[Book a 30-min architect review](https://cal.com/chain305/30min)

Dataflow

## Client → proxy → verdict

Cache key: `(registry, package, version, bundle-digest)`.

1.  01 · Client
    
    `npm, pip, docker, kubelet`
    
    resolves the registry host to the proxy
    

3.  02 · chainsaw-proxy
    
    -   authenticate, identify workspace
    -   25 signals beyond CVE
    -   Rego bundle
    -   Trivy on OCI layers
    
    cache miss: upstream fetch, then evaluate · hit: verdict from memory
    

5.   Structured refusal
    
    signed audit row written
    
     Bytes streamed
    
    signed audit row written
    

K8s admission runs the same bundle against the pod spec and returns admit, warn or deny.

Component map · HA

## What runs, and what breaks if it dies

One binary; topology decides where each piece sits.

Component

Owns

If it dies

chainsaw-proxy

Evaluates the Rego bundle; forwards, serves from cache or refuses

Stateless; restart costs the cache warm-up

Billy, approval queue

Exception approvals, written as signed rows

Read-only; pending requests never auto-approve

Postgres

Audit rows, exceptions, policy versions, RBAC

Replica failover; block mode refuses rather than emit unrecorded decisions

Blob store

SBOMs, scan artefacts, bundle payloads

SBOM browsing degrades; enforcement continues

NATS policy-bus

Optional cache invalidation between replicas

Falls back to a 15s cache TTL; verdicts unchanged

K8s admission webhook

Same bundle against pod and image specs

failurePolicy Ignore in warn, Fail in block

Intel-bundle store

Digest-verified intel bundle, loaded from CHAINSAW\_INTEL\_BUNDLE\_PATH at start

Failed verification: previous bundle stays

Topologies

## SaaS, VPC, on-premises, air-gapped

Same binary, Rego bundle and audit-row schema across all four.

-   ### SaaS, Chainsaw-hosted
    
    Figure 1 · SaaS
    
    Chainsaw runs the control and data plane.
    
-   ### VPC-peered
    
    Figure 2 · VPC-peered (dashed: out-of-band control plane)
    
    Data plane in your cloud; no inbound vendor connection.
    
-   ### On-prem, one proxy per BU
    
    Figure 3 · On-prem, one proxy per BU
    
    A deployment pattern you run: each proxy verifies the same signed Rego bundle, which you distribute.
    
-   ### Air-gapped
    
    Figure 4 · Air-gapped
    
    Zero outbound: CHAINSAW\_OFFLINE=1.
    

Identity flow

## Okta / Entra → SCIM → Rego input

Policies predicate on the directory without re-querying the IdP at evaluation time.

1.  01 · Identity provider
    
    Okta or Entra
    
2.  02 · SCIM push
    
    Users and groups into Postgres
    
3.  03 · Rego input
    
    `input.actor.groups`

Latency budget

## Targets on the hot path

Targets, not measurements. Deployments publish observed p50 / p95 to your Prometheus.

Path

Target

Note

Cache hit, policy unchanged

p50 target < 8 ms

No network egress on the hot path

Cache miss, upstream fetch + 25 signals + Rego eval

p95 target < 450 ms

Dominated by upstream registry latency

K8s admission decision

p99 target < 250 ms

Ledger write is async

Air-gapped operational lifecycle

## Sideload, verify, restart

A failed verify loads nothing; the previous bundle stays.

```
# manifest, hashes and digest binding (--strict adds Sigstore authenticity)
chainsaw bundle verify ./chainsaw-intel-bundle-2026-05-25.tar.gz
# point the proxy at it and restart; providers pick it up on the next refresh tick
CHAINSAW_INTEL_BUNDLE_PATH=./chainsaw-intel-bundle-2026-05-25.tar.gz
# which registries the loaded bundle can adjudicate
chainsaw doctor --offline
```

`CHAINSAW_OFFLINE_FAIL_MODE` is advisory: it refuses nothing on its own, and each policy condition applies its own outage fall-back.

Telemetry surfaces

## Scrape, trace, ship to SIEM

OpenTelemetry (OTLP)

traces and metrics

Prometheus

latency by verdict, cache hit ratio, bundle age

Golden-signal alerts

5xx rate, p99, burn rate, bundle age, NATS lag

Audit-row schema

stable JSON for Splunk, Sentinel and QRadar

Security baseline

## Defaults you don't negotiate

Container identity

uid 10001, non-root, read-only root filesystem, distroless

Verified binary

published SHA-256 checksum; Sigstore signing and SLSA provenance on the roadmap, not yet live

Signed policy bundle

Sigstore-verified at load; disabling it needs an explicit, audited workspace flag

HTTP security headers

CSP, X-Frame-Options, Referrer-Policy on every admin response

Scope boundary

## What Chainsaw is not

It refuses on the install path and composes with the categories below.

-   [Not a SAST →](https://chain305.com/vs-sca/)
-   [Not a secret-at-rest scanner →](https://chain305.com/vs-sca/)
-   [Not a CI posture auditor →](https://chain305.com/vs-sca/)
-   [Not an endpoint agent →](https://chain305.com/vs-artifact-managers/)

Architect review

## Walk an engineer through your topology

30 minutes with a Chainsaw engineer. Bring your network diagram; leave with a placement plan.

[Book the review](https://cal.com/chain305/30min) [Talk to sales](https://cal.com/chain305/30min)

---

## Long form

The full text behind this page, including detail the page itself leaves out.

Reference architecture

### Four topologies, one binary, identical policy.

Chainsaw is an install-path firewall between your developers, CI, and Kubernetes clusters and the sixteen package registries they pull from. The same Rego that fires on install fires byte-identical on publish, at K8s admission, and at runtime.

[Book a 30-min architect review](https://cal.com/chain305/30min)

Component map

#### What runs, what it owns, what breaks if it dies

Chainsaw ships as one binary. The components below are processes and dependencies that binary either is or relies on. Every Chainsaw deployment runs the same set; topology decides where each one sits.

-   ### chainsaw-proxy
    
    **Responsibility.** Single Go binary. Terminates the registry-bound TLS connection from the dev / CI / K8s client, evaluates the Rego bundle against the requested artifact, and either forwards to the upstream registry, serves from cache, or refuses with a structured error. Workers are leader-elected via Postgres advisory locks; every replica can serve traffic, but only one runs each background job at a time.
    
    Failure mode
    
    If every proxy replica is unreachable, clients fail according to CHAINSAW\_FAIL\_MODE (open or closed, per workspace). The proxy is stateless on the request path — restart cost is the cache warm-up only.
    
-   ### Billy — approval queue
    
    **Responsibility.** Out-of-band approval surface for exception requests. Developer hits a refusal at install time, links to Billy, justifies the package; an owner approves or denies inside the SLA window. Approvals write a signed exception row that the proxy reads on its next bundle refresh.
    
    Failure mode
    
    If Billy is unreachable, the exception API degrades to read-only on the proxy side. Pending requests queue locally; nothing fails-open silently. mode: warn shifts to advisory; mode: block keeps refusing.
    
-   ### Postgres
    
    **Responsibility.** System of record. Holds signed audit rows, exception state, policy versions, RBAC, SCIM-synced groups, and the admission\_decisions\_shadow ledger. Logical replication supported; partitioned on event\_time for the audit table.
    
    Failure mode
    
    Streaming replica + automated failover. Proxy holds a small write-behind buffer for audit rows during primary cutover; on extended outage, mode: block proxies refuse rather than emit unrecorded decisions.
    
-   ### Blob store
    
    **Responsibility.** Holds SBOMs, scan artefacts, and signed intel bundle payloads. S3-compatible interface (S3, GCS, MinIO, R2). Reaper worker garbage-collects orphan blobs against the Postgres manifest.
    
    Failure mode
    
    Reads cache locally on the proxy for the hot bundle. A blob-store outage degrades SBOM browsing and historical exports; the enforcement path keeps running off the cached bundle.
    
-   ### NATS — policy-bus
    
    **Responsibility.** Optional cache invalidation between replicas of one deployment. Subjects are namespaced per org; messages carry no bundle and no payload.
    
    Failure mode
    
    Replicas fall back to a 15s policy cache TTL. NATS clustering (3+ nodes) tolerates a single-node loss. Policy correctness is unaffected; freshness lag widens.
    
-   ### K8s admission webhook
    
    **Responsibility.** ValidatingAdmissionWebhook served by the same binary. Evaluates the same Rego bundle against pod / image specs, writes the verdict to admission\_decisions\_shadow, and either admits, warns, or rejects per workspace mode.
    
    Failure mode
    
    failurePolicy: Ignore for warn mode, Fail for block mode. Webhook is horizontally scaled behind the cluster's service; loss of all replicas surfaces as an admission timeout the cluster handles per its own policy.
    
-   ### Intel-bundle store
    
    **Responsibility.** Signed, Sigstore-verified OPA bundle containing Rego policies plus the 25 supply-chain signals layered beyond CVE. Distributed as chainsaw-intel-bundle-YYYY-MM-DD.tar.gz, hot-swappable at runtime without restart.
    
    Failure mode
    
    Verification is enforced — an unsigned or signature-invalid bundle is rejected and the previous bundle stays loaded. In air-gapped mode, sideloaded bundles use the same verification chain.
    

Topologies

#### SaaS, VPC-peered, on-prem, air-gapped

The deployment model is a placement decision, not a product decision. Same binary, same Rego bundle, same audit-row schema across all four. Pick the posture your network and compliance teams can sign; migrate later without re-papering the contract.

-   ### SaaS — Chainsaw-hosted
    
    Figure 1 — SaaS. Chainsaw operates the control plane. Customer keeps identity-provider integration.
    
    Control plane and data plane both run in Chainsaw's environment. Customer dev tools point at a per-workspace hostname; TLS terminates at the proxy. Suited to teams with no residency or air-gap constraint who want the shortest deploy path.
    
-   ### VPC-peered — customer cloud, Chainsaw-managed control plane
    
    Figure 2 — VPC-peered. Data plane in customer VPC. Control plane operates as managed service (dashed: out-of-band).
    
    Data plane (proxy, Postgres, blob store) runs in the customer's cloud account. Control plane (policy authoring, signed bundle distribution, support tooling) runs on Chainsaw infrastructure. Connection is outbound-only from customer to Chainsaw for bundle pull and telemetry; no inbound vendor connection. Same Rego, same audit row schema as SaaS.
    
-   ### On-prem, one proxy per BU — customer owns everything
    
    Figure 3 — On-prem, one proxy per BU (a deployment pattern you run). Each proxy verifies the same signed Rego bundle at load.
    
    A deployment pattern you run: each proxy verifies the same signed Rego bundle, which you distribute. There is no central hub, no inheritance and no rollup; for estates with several business units, we scope the rollout with you.
    
-   ### Air-gapped — signed intel bundle sideload, zero outbound
    
    Figure 4 — Air-gapped. Zero outbound. Signed intel bundle sideloaded via `chainsaw bundle apply`.
    
    No outbound network from the proxy. Operator transfers chainsaw-intel-bundle-YYYY-MM-DD.tar.gz across the boundary on the cadence the diode allows, runs chainsaw bundle verify, then chainsaw bundle apply for hot-swap. CHAINSAW\_OFFLINE=1 disables every phone-home path; chainsaw doctor --offline reports what the loaded bundle can and cannot adjudicate.
    

Dataflow

#### Registry → proxy → cache → client

A client (npm, pip, mvn, gradle, cargo, go, docker, kubelet pulling an image, etc.) resolves the registry hostname to the proxy. The proxy authenticates the request, identifies the workspace, and consults its in-memory cache keyed on `(registry, package, version, bundle-digest)`.

**Cache hit.** Verdict returns from memory. The proxy serves the cached artefact bytes (or refusal) and writes a signed audit row to Postgres. No upstream call.

**Cache miss.** The proxy fetches from the upstream registry, runs the 25 supply-chain signals beyond CVE, evaluates the Rego bundle, then either streams bytes to the client or returns a structured refusal. Signed audit row written on the way out. For Docker / OCI artefacts, Trivy runs inline against the layer set before the verdict commits.

**K8s admission.** The webhook receives the AdmissionReview, evaluates the same bundle against the pod spec and its image references, writes the verdict to `admission_decisions_shadow`, and returns admit / warn / deny per workspace mode.

Identity flow

#### Okta / Entra → SCIM → Rego input

SCIM provisions users and groups from Okta or Entra into Postgres on the standard push schedule. Group membership flows into the Rego input as `input.actor.groups`, so policies can predicate on the directory without re-querying the IdP at evaluation time.

Browser sessions use HTTP-only, SameSite=Lax cookies bound to the workspace. Programmatic clients (CI runners, CLI) authenticate with bearer tokens via the `Authorization` header. Token secrets are bcrypt-hashed at rest and fronted by an in-process LRU+TTL cache so a hot-path token validation does not hit Postgres on every request.

Latency budget

#### Targets on the hot path

Targets, not measurements. Real numbers vary by upstream registry health, signal set enabled, and replica placement. Production deployments publish observed p50 / p95 to the customer's Prometheus.

Path

Target

Note

Cache hit, policy unchanged

p50 target < 8 ms

In-process bundle eval + memory-cached upstream response. No network egress on the hot path.

Cache miss, upstream fetch + 25 signals + Rego eval

p95 target < 450 ms

Dominated by upstream registry latency. Signal evaluation runs in parallel with the fetch where possible.

K8s admission decision

p99 target < 250 ms

Webhook returns within the cluster's default 10s timeout with substantial headroom; ledger write is async.

HA and failure modes

#### What happens when each component falls over

**Proxy.** Single binary, multiple replicas behind a TCP / HTTPS load balancer. Stateless on the request path; cache rebuilds on warm-up. Background workers (orphan-blob reaper, SBOM snapshotter, exception-expiry reminder, feedback tuner, ownership SLA timer, bypass-history snapshotter, datacleanup retention, policy-bus subscriber, deploy-correlation) are leader-elected via Postgres advisory locks; only the leader runs each one, and leader loss triggers re-election within seconds.

**Postgres.** Primary + streaming replica with automated failover. During cutover, the proxy buffers audit rows in a bounded write-behind queue. If the queue saturates and workspace mode is `block`, the proxy refuses rather than emit unrecorded decisions. In `warn` mode it logs and continues.

**NATS.** 3-node cluster minimum for production. Single-node loss is transparent; full-cluster loss falls back to a 15s policy cache TTL, which widens freshness lag but does not change verdict correctness.

**Billy.** Approval surface decoupled from enforcement. If Billy is unreachable, the proxy keeps refusing on the policy that is loaded; pending requests do not auto-approve. Workspaces in `mode: warn` degrade to advisory; workspaces in `mode: block` remain fail-closed.

Telemetry surfaces

#### What you can scrape, trace, and ship to your SIEM

-   ### OpenTelemetry (OTLP)
    
    Traces and metrics export over OTLP/gRPC or OTLP/HTTP. Resource attributes tag workspace, replica, and bundle digest.
    
-   ### Prometheus scrape endpoint
    
    /metrics on the admin port. Histograms for request latency by verdict, counters for cache hit ratio, gauges for bundle age and policy-bus lag.
    
-   ### Golden-signal alerts (shipped)
    
    5xx rate > 1%, p99 latency > 1s, error-budget burn rate, bundle-age over threshold, NATS subscriber lag, leader-election flap.
    
-   ### Audit-row schema
    
    Stable JSON schema for every decision. Documented for Splunk, Sentinel, and QRadar feeds; replayable from Postgres or the WORM blob copy.
    

Air-gapped operational lifecycle

#### Sideload, verify, apply, audit

**Bundle.** Chainsaw publishes `chainsaw-intel-bundle-YYYY-MM-DD.tar.gz` signed via Sigstore. The bundle contains the Rego policies, the 25 supply-chain signal datasets, and a manifest. Operator transfers the tarball across the boundary on the cadence the diode allows.

**Verify.** `chainsaw bundle verify ./chainsaw-intel-bundle-2026-05-25.tar.gz` checks the Sigstore signature against the pinned trust root before the bundle is permitted to load. Verification failure is terminal — the previous bundle stays in place.

**Apply.** `chainsaw bundle apply` hot-swaps the live bundle without a restart and emits a `policy.bundle.applied` audit row carrying the new digest.

**Doctor.** `chainsaw doctor --offline` prints the per-provider matrix: which of the sixteen registries the current bundle can adjudicate, freshness per dataset, and Sigstore trust-root expiry.

**Fail mode.** `CHAINSAW_OFFLINE_FAIL_MODE=condition-default|open|closed` records the posture you intend for remote-only providers and is reported per provider by `chainsaw doctor --offline`. It is advisory — it does not refuse installs on its own. Each policy condition applies its own outage fall-back.

Security baseline

#### Defaults you don't have to negotiate

-   ### Container identity
    
    Runs as uid 10001, non-root, read-only root filesystem. No `NET_ADMIN`, no `SYS_ADMIN`. Distroless base.
    
-   ### Verified binary
    
    Every release ships with a published SHA-256 checksum you verify before install. Sigstore signing + SLSA provenance are on the roadmap (release-signer bot), not yet live.
    
-   ### Signed policy bundle
    
    OPA bundle Sigstore-verified at load time. Verification is enforced by default; disabling it requires an explicit, audited workspace flag.
    
-   ### HTTP security headers
    
    CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy set on every admin response. Cookies HTTP-only and SameSite=Lax.
    

Scope boundary

#### What Chainsaw is not

Chainsaw refuses on the install path. It is not a replacement for the categories below — it composes with them.

-   ### Not a SAST
    
    [Comparison →](https://chain305.com/vs-sca/)
    
-   ### Not a secret-at-rest scanner
    
    [Comparison →](https://chain305.com/vs-sca/)
    
-   ### Not a CI posture auditor
    
    [Comparison →](https://chain305.com/vs-sca/)
    
-   ### Not an endpoint agent
    
    [Comparison →](https://chain305.com/vs-artifact-managers/)
    

Architect review

#### Walk an engineer through your topology

30 minutes with a Chainsaw engineer. Bring your network diagram; leave with a placement plan for SaaS, VPC, on-prem, or air-gapped.

[Book the review](https://cal.com/chain305/30min) [Talk to sales](https://cal.com/chain305/30min)
