This guide describes the current Decision Gate OSS container contract and provides operator steps for building and running the MCP server image.
The container image is a server artifact. It runs decision-gate serve
and is suitable for production-like integration and qualification work. It is
not evidence that the permanent commit, recovery, or multi-node assignment profile is
launch-qualified.
Current Container Contract
- Entrypoint:
decision-gate - Default command:
serve --config CONFIG_PATH --allow-non-loopback, where the container preset uses /etc/decision-gate/decision-gate.toml - Config mount: /etc/decision-gate/decision-gate.toml
- Transport: HTTP (SSE optional)
- Auth: required bearer authentication; caller-controlled certificate or proxy-subject headers are never identity authorities
- TLS: terminated upstream by default (
server.tls_termination = "upstream") - Persistence: no durable run-state store by default; SQLite is an explicit local-persistence configuration
- Runtime: non-root, minimal privileges, stdout/stderr logs
- Writable paths:
/var/lib/decision-gate(only when SQLite enabled)
The absence of a local store does not make evaluator mutations stateless or
safe for same-RunKey multi-writer routing. Current code has exact-head/idempotency
mechanisms, but they have not been refined against the PF-03 accepted-run model
and the process-lifetime profile has no restart-durability claim. A hosted
deployment must route mutation for one RunKey(namespace, run_id) through one
resolved process and pin identical scenario-law bytes for that identity. This
is an operator restriction, not durable authority transfer or failover.
Pinning an entire namespace to one process is conservative current routing, not
the permanent serialization key. The assignment-stable multi-node target
routes each RunKey
to one co-located evaluator/accepted-run authority and consumes exact external
platform witnesses; see the
deployment integration standard.
The current initial-profile configuration cannot represent network evidence acquisition, remote MCP evidence sources, subprocess/custom execution, or remote evaluation. HTTP/SSE server transport carries tool requests to the DG node and does not enable those excluded acquisition families.
Build the Image
Local build:
docker build -t decision-gate:dev .
Multi-arch build (amd64 + arm64):
IMAGE_REPO=ghcr.io/your-org/decision-gate IMAGE_TAG=dev \
scripts/container/build_container.sh
Push multi-arch:
IMAGE_REPO=ghcr.io/your-org/decision-gate IMAGE_TAG=dev PUSH=1 \
scripts/container/build_container.sh
Notes:
IMAGE_REPO=ghcr.io/your-org/decision-gateis a placeholder. Replaceyour-orgwith your GitHub org or user (for example,ghcr.io/decision-gate/decision-gate).IMAGE_TAG=devis a local/dev example. For releases, use a version tag (for example,vX.Y.Z) and optionally publishlatest.
Tags and Release Policy
Local/dev:
decision-gate:devfor ad-hoc testing.- Local/dev tags are not policy-grade release artifacts.
Release:
- No official GHCR image is currently published from this repository.
- Operators who want a registry image should build and push their own image to an org-controlled registry and retain their own provenance trail.
- Release workflows still emit supply-chain evidence for source-first tagged releases and local release-parity validation.
Configuration
The container expects a config file at /etc/decision-gate/decision-gate.toml.
Use the container preset as a baseline:
configs/presets/container-prod.toml.
Key requirements:
server.bindmust be non-loopback (e.g.,0.0.0.0:8080).server.auth.modemust bebearer_tokenormtls.server.tls_termination = "upstream"when TLS is terminated outside the container.
Run the Container
Minimal run (bearer token auth, upstream TLS termination):
docker run --rm -p 8080:8080 \
-v "$(pwd)/configs/presets/container-prod.toml:/etc/decision-gate/decision-gate.toml:ro" \
decision-gate:dev
Notes:
- Replace the demo token in the preset before production use.
--allow-non-loopbackis part of the default container command. If you override the command, include--allow-non-loopbackor setDECISION_GATE_ALLOW_NON_LOOPBACK=1.
In-Container TLS (Optional)
If you need TLS inside the container, set:
[server]
tls_termination = "server"
[server.tls]
cert_path = "/etc/decision-gate/tls/server.crt"
key_path = "/etc/decision-gate/tls/server.key"
Mount the certs and update your container runtime accordingly.
Durable Mode (SQLite)
By default, the container preset uses in-memory stores.
To enable SQLite durability, update the config:
[schema_registry]
type = "sqlite"
path = "/var/lib/decision-gate/schema-registry.db"
[accepted_run_store]
type = "sqlite"
path = "/var/lib/decision-gate/decision-gate.db"
busy_timeout_ms = 5000
Run with a writable volume:
docker run --rm -p 8080:8080 \
-v "$(pwd)/configs/presets/container-prod.toml:/etc/decision-gate/decision-gate.toml:ro" \
-v decision-gate-data:/var/lib/decision-gate \
decision-gate:dev
Auth Expectations
Bearer token example:
curl -sS -X POST http://127.0.0.1:8080/rpc \
-H "Authorization: Bearer dg-container-demo-token" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Caller-asserted certificate-subject headers are intentionally unsupported. When a proxy or ingress terminates TLS, it must strip untrusted forwarding headers and pass the reviewed bearer credential unchanged. Server-terminated TLS likewise protects the connection but does not create a separate application-identity mechanism.
Health Endpoints
Decision Gate exposes standard Kubernetes probes:
GET /healthzfor livenessGET /readyzfor readiness
These endpoints are intentionally unauthenticated and return minimal status
only. /readyz performs lightweight readiness checks (state store + schema
registry) and returns HTTP 503 with {"status":"not_ready"} if dependencies
are unavailable.
curl -sS http://127.0.0.1:8080/healthz
curl -sS http://127.0.0.1:8080/readyz
Both endpoints return HTTP 200 with a JSON payload.
Kubernetes Example
apiVersion: apps/v1
kind: Deployment
metadata:
name: decision-gate
spec:
replicas: 1
selector:
matchLabels:
app: decision-gate
template:
metadata:
labels:
app: decision-gate
spec:
containers:
- name: decision-gate
image: ghcr.io/your-org/decision-gate:your-tag
ports:
- containerPort: 8080
securityContext:
runAsNonRoot: true
runAsUser: 10001
readOnlyRootFilesystem: true
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 2
periodSeconds: 5
volumeMounts:
- name: config
mountPath: /etc/decision-gate/decision-gate.toml
subPath: decision-gate.toml
readOnly: true
- name: data
mountPath: /var/lib/decision-gate
volumes:
- name: config
configMap:
name: decision-gate-config
- name: data
emptyDir: {}
For SQLite durability, replace emptyDir with a persistent volume claim.
Supply-Chain Artifacts
Decision Gate release workflows generate and verify supply-chain artifacts for source-first tags and local parity checks. The commands below remain useful for manual verification of operator-built images.
Container SBOM (example using syft):
syft packages decision-gate:dev -o spdx-json > decision-gate.sbom.spdx.json
Blob or artifact signing (cosign):
cosign sign-blob decision-gate.sbom.spdx.json
Provenance statement signing:
cosign sign-blob decision-gate.provenance.intoto.json
Release policy blocks when:
- Any High/Critical vulnerability is present.
- Any known-exploited CVE is present.
- Signature or provenance verification fails.