Proxy and policy.

The brokered path is the one worth using. Your program holds a token that is worthless anywhere else, and the real key is decrypted only after the request has already been approved.

Not for production secrets yet. There is no public installable release; everything below builds from source and is pre-release. Do not put a credential you care about into it.

What a Locker token is#

A token looks like skl_tok_v1_… and carries a public identifier and a secret half. The daemon stores a keyed verifier rather than the token itself, so the stored form is not usable as a credential even if you read it. Bindings, expiry, and policy version are covered by a message authentication code, so none of them can be edited after issue.

locker tokens create --name web-app --bind openai=OPENAI_API_KEY --ttl 3600
At least one --bind is required. Without --ttl the daemon defaults to 24 hours.

What a token can be pinned to#

Eleven dimensions, all deny-by-default. An empty list means nothing is allowed, not everything.

Policy dimensions enforced on every brokered request.
DimensionEnforced as
providerExact match against the provider segment of the request path.
secret_aliasesThe set of vault entries this token may cause to be decrypted.
methodsAn explicit set of HTTP methods.
pathsExact paths or prefixes.
headersAn allow list and a deny list applied to client-supplied headers.
cidrSource ranges, or loopback-only.
time_windowA wall-clock window outside which the token does not validate.
expires_atAn absolute expiry timestamp.
egressAllowed hosts, schemes, and ports for the outbound call.
limitsPer-token and per-provider request rates.
concurrencyA ceiling on in-flight requests for the token.

The policy a token actually gets today#

Rather than describe the policy engine in the abstract, here is what the shipped daemon configures for an agent token:

  • Provider openai, bound to the named secret alias.
  • Methods POST, GET, and DELETE.
  • Source must be loopback.
  • Five path prefixes and nothing else: chat completions, responses, embeddings, models, and files.
  • Egress pinned to the parsed provider URL. A non-HTTPS scheme is refused.
  • Denied headers include authorization, proxy-authorization, cookie, and the common API-key headers.
  • 120 requests per minute per token, 600 per minute per provider, 16 concurrent.

Sending a request#

curl http://127.0.0.1:$PORT/v1/proxy/openai/v1/chat/completions \
  -H "X-Slifer-Locker-Token: $LOCKER_TOKEN" \
  -H "X-Slifer-Local-Session: $LOCAL_SESSION" \
  -H "content-type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hi"}]}'
Both headers are required. The port and the session secret are read from the runtime directory, which is mode 0700.

The Locker token alone is not enough. Every route also requires the local-session header, whose value comes from a 0600 file in the runtime directory. A caller therefore needs the token and filesystem access as your user. That is intentional, and it is why a stolen token is not usable from another account on the machine.

What the broker checks, in order#

  1. The peer address is loopback and the Host header is a loopback literal.
  2. A browser Origin header is present, in which case the request is refused.
  3. The token header is well-formed, and the token is not supplied through any other channel.
  4. Client headers are filtered to a short allow list; authorization and cookie headers are stripped.
  5. The token validates against the vault's authorization index, and a binding matches the provider.
  6. The policy kernel evaluates every dimension above.
  7. Only now is the upstream key decrypted, and attached to the outbound request.

The ordering is the security property. A request that fails at any earlier step never causes a decryption, so a probing client cannot make the vault do work on its behalf.

The outbound hop#

The broker opens its own TLS connection using the operating system's trust roots. If no trust roots can be loaded it fails rather than falling back to a bundled set. DNS answers are validated against the egress policy and then pinned, so the address that was checked is the address that gets connected to. Redirect following is refused, and a redirect target would be re-checked through the same egress controls anyway.

Two hops, and only one of them is TLS#

Worth being blunt about, because it is easy to assume otherwise. The hop from your program to the broker is plain HTTP over 127.0.0.1. It is not encrypted, because it never leaves the machine and never touches a network interface. The hop from the broker to the provider is TLS 1.3, or 1.2 if the provider requires it.

If someone can read loopback traffic on your machine as your user, they can already read the process memory of the program making the call. Encrypting that hop would look reassuring without changing what an attacker in that position can do.

When something is denied#

A denied request returns a flat policy_denied to the client. Locally you get the specific rule that stopped it, drawn from nineteen stable outcome codes, so you can tell a rate limit from a path mismatch from an expired token. The client is told less than the operator on purpose: a precise denial reason is a probing oracle.

What the proxy does not do yet#

  • One provider. The adapter layer supports other authentication shapes, but only OpenAI is registered and any other provider segment is rejected.
  • Proxied requests are not written to the persisted activity log. That log covers secret, token, break-glass, backup, and recovery events.
  • Budgets are request rates, not spend. Geographic policy, provider health, and anomaly detection exist as library code and are not wired into the daemon.