Command gating cannot see HTTP operations performed inside Helm, Terraform providers, k9s, SDKs, or another long-lived process. Guard's API proxy terminates a local TLS connection, parses each request into a typed operation, applies policy and evaluator judgment, and re-originates allowed traffic with an upstream credential held only by the daemon.
Kubernetes is the reference protocol. GitHub and Vercel adapters exercise the same protocol-independent gate and are example integrations.
guard server start \
--gate consequence \
--kube-proxy 127.0.0.1:8443 \
--kubeconfig /etc/guard/kubeconfig \
--api-policy /etc/guard/api-policy.yaml \
--brokered-kubeconfig-out /run/guard/kubeconfig
KUBECONFIG=/run/guard/kubeconfig kubectl get pods -n devThe input kubeconfig belongs to the daemon and may contain a bearer token or client certificate. Exec and auth-provider plugins are rejected. The brokered kubeconfig points only to Guard, trusts its local CA, and contains no upstream credential.
--brokered-kubeconfig-out produces read-only anonymous access. A permissive
API policy cannot turn that kubeconfig into mutation authority. Guard does not
export caller-scoped session credentials through the public CLI. Kubernetes,
Helm, and credential-backed API access grants use approved typed command verbs,
preserving request-scoped admission counts.
The operator-generated kubeconfig carries only the anonymous placeholder
bearer guard-anonymous, which identifies nothing and is stripped by the
proxy; client-go refuses to send requests from a config whose user has no
credential field at all. Anonymous requests can read policy-permitted objects
but cannot create, update, patch, or delete them. Incoming client authorization
is never forwarded to the upstream. A Guard-session authorization header that
does not resolve to live internally integrated state fails closed; the public
CLI does not create that state. Session-attributed mutations carry the session
fingerprint and immutable authority revision into request summaries, audit
records, and rollback envelopes.
Each request also binds the complete API policy and evaluator-intent generation before classification. A hot reload that changes any policy field or evaluator intent invalidates every in-flight request before its next upstream operation. The client submits a fresh request under the new authority.
Load a hot-reloaded YAML policy with --api-policy; absence is default deny.
examples/api-policy.yaml documents the schema.
Rules match typed protocol fields such as operation verb, resource, namespace,
and subresource. Actions are:
| Action | Behavior |
|---|---|
allow |
Route through the consequence floor and forward if eligible. |
deny |
Reject without contacting upstream. |
hold |
Park in the shared approval queue; without that queue, deny. |
evaluate |
Ask the evaluator under policy and live-session intent. |
Explicit policy denies and protocol hard-denies are absolute. A readonly
listener is the default. Every Kubernetes mutation requires a live attributable
session even on an explicitly configured policy-mode listener. Policy allow,
operator approval, and evaluator judgment cannot grant anonymous mutation
authority. Evaluated traffic cannot override those absolute boundaries.
Kubernetes interactive subresources (exec, attach, portforward, and
proxy), pods/ephemeralcontainers, and Secret watches are hard-denied. Writes
to other subresources require an explicit matching subresource rule. Allowed
Secret reads redact values regardless of policy wording, and an unparseable
secret-bearing response fails closed. Raw upgraded streams cannot be inspected,
bounded, or redacted after the protocol upgrade. An operator-approved typed verb
with a fixed noninteractive command shape is the sanctioned path for Kubernetes
container diagnostics.
A successful session-attributed read of one named Kubernetes object records its
UID, resourceVersion, and a digest of the object state. The observation is
bound to the endpoint, session fingerprint, complete session revision, API
group and version, resource, subresource, namespace, name, and UID. Successful
mutation responses refresh only that same session's observation. Anonymous
reads, lists, and watches establish no write authority.
Before update, patch, or delete, Guard fetches the live object and returns HTTP
409 unless the same session observed the same UID and a compatible version.
Guard then adds a Kubernetes-native atomic precondition to the forwarded
request: updates and merge, strategic, or apply patches carry
metadata.resourceVersion; JSON Patch begins with UID and resourceVersion
test operations; delete carries UID and resourceVersion preconditions. A race
after Guard's comparison therefore resolves as an upstream conflict rather than
an unguarded overwrite.
For a parent-object write, Guard ignores status and server-managed
managedFields while comparing object state. This permits controller status
updates to advance resourceVersion without falsely treating the desired state
as changed; the forwarded request still uses the latest live version as its
strict precondition. Status-subresource writes include status in the comparison.
Changes to spec or user-managed metadata conflict.
Observations are process-local and bounded. A daemon restart, registry eviction,
object recreation, unreadable object, collection mutation, unsupported patch
media type, or response without UID and resourceVersion requires a fresh
named-object read or fails with HTTP 409. Secret redaction does not establish an
observation. Typed command verbs are a separate deterministic authority path;
when they bypass the API proxy, concurrency behavior comes from the invoked tool
and verb contract rather than this observation registry.
--api-endpoints <yaml> hosts multiple named listeners, including multiple
instances of one protocol. Each endpoint owns its listen address, mode,
protocol, upstream, credential reference, policy, CA output, and optional
brokered kubeconfig output.
endpoints:
- name: cluster-readonly
listen: 127.0.0.1:8443
protocol: kubernetes
mode: readonly
kubeconfig: /etc/guard/cluster.kubeconfig
policy: /etc/guard/cluster-policy.yaml
brokered_kubeconfig_out: /run/guard/cluster.kubeconfig
- name: github-automation
listen: 127.0.0.1:9443
protocol: github
upstream: https://api.github.com
token_file: /etc/guard/github-token
policy: /etc/guard/github-policy.yaml
ca_out: /run/guard/github-ca.pemEndpoint identity binds policy, generated coverage, history, upstream credential selection, and persisted rollback. A plan created on one listener cannot run through another listener, even when both use the same protocol.
Listeners bind loopback only and the operator-generated client has no network
identity credential. Expose the proxy only inside a trusted local or
single-tenant boundary. API proxy mode is incompatible with --exec-as-caller.
Under consequence gating, recoverable writes snapshot the prior state or record the newly created object before forwarding. The protocol constructs a plain HTTP revert plan:
- update or patch restores the prior object;
- create deletes the server-named object;
- faithfully recreatable delete restores a sanitized snapshot;
- side-effect-only operations without a faithful inverse hold.
The persisted plan binds endpoint, protocol, canonical target, attribution, and
upstream credential identity. A create and its cleanup are correlated only
inside the same connection and attribution context, and explicit policy deny
still wins.
Every successful contained write returns X-Guard-Provisional: <handle> and an
HTTP Warning naming guard confirm <handle>. Kubernetes clients display the
warning on standard error, and automation can read the header before treating
the write as durable.
guard provisionals, guard confirm, and guard revert manage API and command
envelopes through the same interface.
Evaluate-routed traffic can produce exact verb coverage cells. Each cell binds
endpoint, attribution when present, typed operation fields, namespace,
value-free body shape, evaluator regime, and expiry. Value-bearing mutations
remain evaluator-routed. Generated coverage cannot displace operator policy.
Use guard verb coverage list and guard verb coverage clear for inspection
and reset.
The API judge has configurable global concurrency, endpoint/session token buckets, error circuits, and a reserved session slot. Limits bound spend and failure amplification. Exhaustion and circuit-open states fail closed, and cumulative admission counters enter the audit stream.
Guard forwards only safe response headers. Credential-bearing authentication headers are stripped unless a protocol explicitly permits one. Response bodies are inspected and redacted before delivery, including across transport chunks. Compressed upstream responses are rejected unless the negotiated representation can be inspected. Unsafe cross-origin redirects and paths with ambiguous encoded separators, dot segments, or NULs fail closed.