Skip to main content
View Markdown ↗

Copy this page

Select and copy the Markdown below, then paste it into your LLM.

On this page

Configure the model gateway

The model gateway, hardened-gateway, is the only way a managed Badger session reaches a model. It forwards POST /v1/chat/completions (or, if configured, POST /v1/responses) from a Unix socket to one HTTPS endpoint and one model that you choose. The session never sees the provider credential.

Status: available on main, in testifysec/gateway/. It does not claim FIPS 140-3 validation.

Build

With a Go toolchain already installed (the gateway uses only the standard library):

cd testifysec/gateway
CGO_ENABLED=0 go build -trimpath -o /protected/bin/hardened-gateway .
go test ./...

Write the configuration

The configuration, the credentials and the audit spool belong to the gateway's own service user. The gateway refuses a configuration or credential file that is not a regular file readable only by its owner, in a directory whose mode is exactly 0700. Keep credentials off persistent disk: the example takes the provider token over an inherited pipe and reads the collector token from a memory-backed file system.

{
  "schema": "testifysec.hardened-opencode.gateway/v1",
  "socket": "/run/hardened-session/gateway.sock",
  "spool": "/var/lib/hardened-session/audit",
  "providerURL": "https://model.example.invalid/v1/chat/completions",
  "providerTokenFd": 3,
  "collectorURL": "https://collector.example.invalid/v1/logs",
  "collectorTokenFile": "/run/hardened-session/collector.token",
  "model": "approved",
  "tenant": "example-tenant",
  "session": "example-session",
  "agent": "spiffe://platform.example.invalid/tenant/example/agent/example",
  "profileDigest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "maxRecords": 1000,
  "maxOfflineSeconds": 300,
  "timeoutSeconds": 120,
  "developmentLoopback": false,
  "platformConfigFile": "/etc/hardened-session/platform.json"
}
KeyMeaning
socketThe Unix socket the gateway listens on. Mount this one file into the session at /run/hardened-gateway/gateway.sock, the path the managed build is compiled with.
spoolDirectory for the audit spool. One gateway per spool; a lock refuses a second.
providerURLThe model endpoint. The protocol path may sit under a prefix, such as /broker/v1/chat/completions.
protocolOptional. chat-completions (default) or responses, which accepts only the stateless subset with store: false.
providerTokenFile or providerTokenFdWhere the provider credential comes from. See below.
providerCertSha256Optional. Lowercase hex SHA-256 of the provider's SubjectPublicKeyInfo, for a provider no system root signs.
collectorURL, collectorTokenFileOTLP/HTTP log endpoint for audit events, and its bearer token. An empty token path is only for a collector whose TLS boundary already authorizes the gateway.
modelThe exact model name sent to the provider.
tenant, session, agent, profileDigestRecorded in every audit event.
maxRecords, maxOfflineSecondsWhen the spool holds this many undelivered records, or delivery has failed this long, new requests get 503 and no provider call.
timeoutSecondsUpper bound on one provider call.
developmentLoopbackAllows cleartext to literal loopback addresses, for the test harness only.
platformConfigFileThe TestifySec platform session check. Required unless developmentLoopback is true; see the platform page.
signedProfileFile, admissionExecutable, admissionSha256The signed profile and the pinned admission executable that verifies it before every provider request.

Unknown keys are refused. Production URLs need TLS with normal certificate and hostname checks, and the gateway ignores proxy environment variables. There is no switch to turn TLS verification off; install your enclave's roots in the host trust store, or pin the provider's key.

Provide the credentials

Provider token. With providerTokenFd, the gateway reads the token from an inherited file descriptor (3 or higher) that the process starting it, such as the session broker, holds open: each line replaces the token in memory, an invalid line or a closed pipe clears it, and with no token the gateway makes no provider call. The token never touches disk, the command line or the environment. providerTokenFile exists for test harnesses; a plaintext token file on persistent storage does not meet the hardening requirement.

Collector token. collectorTokenFile is read from a file. Put it on a memory-backed file system such as /run, in a 0700 directory owned by the gateway's user, and write it at service start from your secret store. Do not leave it as a plaintext file under /etc.

Run it

hardened-gateway -config /etc/hardened-session/gateway.json

Run one gateway per session. The socket is a bearer capability: anyone who can open it can send model requests as that session, so never share one between sessions or expose it through a network listener.

What the audit records

Before each provider call, the gateway writes a start record to the spool and syncs it; the terminal record replaces it when the call ends. Records hold IDs, timestamps, the tenant, session, agent, profile and model, the outcome, status and byte counts. They never hold prompts, source code, response bodies, headers or credentials. Denied requests are recorded too. Records are delivered to the collector at least once; deduplicate on event.id.

Collector templates for Loki and OpenSearch are in testifysec/gateway/. They are starting points, not tested deployments.

Check it

python3 uat.py --go /absolute/path/to/go --report /tmp/gateway-uat.json compiles the gateway and runs it against synthetic provider and collector services on loopback: streaming, credential isolation, denied-request auditing, offline bounds, restart recovery. The report is an unsigned local observation.

Reference generated from the product documentation. Match commands and support details to your installed release.