Documentation embedded in this build.
Protect a service with an authorizing proxy
What it is for
You have a service that knows nothing about certificates and cannot be changed to learn. Stand the proxy in front of it. The same binary and the same configuration cover three sizes: one service, many services told apart by the name they are asked for, or an entire Kubernetes cluster, where the proxy runs as the ingress controller and reads its routes off the annotations on your Services.

The caller's identity arrives already verified, the decision is made from the same policy the rest of the deployment uses, and a signed receipt rides every request. Nothing about the caller is ever trusted from a header — not who they are, not what they may do. When the proxy cannot reach a decision it refuses the request, so the worst a failure does is turn people away.
This is not an L7 ingress standing in front of AuthBox. The client certificate ends at AuthBox: the proxy terminates the handshake itself, because a hop that has already replaced the caller's certificate with its own has nothing left to decide from.
The topology
One proxy in front of the services, with the caller's certificate ending at the proxy — which decides, and only then forwards.
CALLER PROXY YOUR SERVICE
client cert --mTLS-> decides -> one service
forwards -> many services, told apart by name
signs a receipt -> one cluster, as the ingress
(routes read from Service annotations)
The three sizes are the same binary and the same configuration file. What changes is how many routes it holds and where they came from: written down for one service or many, discovered from a single annotation on a Service in the cluster case.
Stand it up
$ make scenario-proxy # one service behind one proxy
$ make scenario-proxy-kubernetes # ingress controller mode
$ make all-in-one # reference stack, app door
Each of the first two builds the images it needs, brings the topology up in containers, runs its checks and tears the topology down; make all-in-one leaves a stack running and prints where every door is. Expect the first run to spend most of its time building.
What the smoke proves
make scenario-proxy runs the Go twin TestProxyScenario against a real authboxd writer and a real authboxproxy, both in containers, fronting one plain-HTTP application that knows nothing and one application that speaks mutual TLS. It proves four consumption modes against one deployment — headers pushed to an application that has only a header parser; a receipt the application verifies offline against the proxy's own exported key set; the two-entry chain an AuthBox-aware upstream reads instead of headers; and the injected callback token, redeemed at the writer — and then pauses the writer to show the door still deciding, which is the steady-state property rather than an assertion about it.
make scenario-proxy-kubernetes runs TestProxyKubernetesDiscovery: a proxy Pod starting with no routes at all, against a real API server, picking up a route the moment a Service is annotated. There is no writer in that cluster — the door decides from a signed bundle published earlier.
make all-in-one brings the same door up as the reference stack's app door and smokes it with the enrolment, evidence and door phases.
Read on
scenario:proxypins the four consumption modes and the paused-writer property; the scenario's own notes are indeploy/scenarios/proxy/README.md.scenario:proxy-kubernetespins route discovery from a Service annotation with no static route and no writer in the cluster; seedeploy/scenarios/proxy-kubernetes/README.md.make:all-in-onepins that the door ships in the reference composition rather than only in a test.- The operator runbook is authboxproxy operations, in the operator guide: the routes, the receipt key, the reserved paths and what each refusal means.
- Living with your certificate is what a caller on the other side of that door needs to know.