v0.1 Pre-release out · open source, Apache 2.0

Your AI browses.
You hold the key.

Let your AI agents read the web without letting them post to it behind your back.

lockhttp is the proxy every outbound HTTP(S) request of an AI agent goes through. Reads of trusted hosts pass. Anything that sends data, triggers an action or reaches an unknown host waits for a human, in a console the agent cannot reach. Every decision is traced to the container, the agent and the session that asked.

  • Linux host, agents in lea containers
  • HTTP and HTTPS (CONNECT + SNI, or per request with TLS decryption)
  • Built on lockkit
  • Apache 2.0

Pre-release. lockhttp v0.2.0 is out as a pre-release and runs in Docker only (one lockhttp launcher, a cosign-signed image): command names are fixed, flags may still move. TLS decryption ships in it and is optional, wired by lea: without a local CA and the signer (--ca-cert, --signer), HTTPS is decided at the domain. Design decisions: docs/decisions.md, contract with lea: docs/lea.md.

See it run

Claude Code, in a lea container, fetches a page of pkg.go.dev, a trusted host: the lockhttp gate lets the GET through at once. It then POSTs a build report to hooks.example.net, an unknown host: the gate holds the request, and the lockhttp console, in its own account, shows the approval screen with the body preview masked (the email as j***(20), the token as <secret>). The human answers n token in body; the agent gets a 403 E_DENIED, and the gate's audit records both decisions.

Swipe sideways to see both terminals.

Claude Code (left, in a lea container) reads pkg.go.dev at once: a trusted host, a read. Its POST to an unknown host stops at the gate until you answer in the lockhttp console (right, account lockhttp), which shows the request decrypted, its body masked. You type n token in body: the agent gets a 403 E_DENIED with your reason, and the gate's audit keeps both decisions.

Why lockhttp?

An agent with network access can read documentation, and it can also upload your code, post to a webhook, open an issue or call an API with your token. A plain allowlist cannot tell those apart: once a host is allowed, it is allowed for reads and writes. lockhttp decides per request, and keeps the decision out of the agent.

A domain allowlist compared with lockhttp
PropertyDomain allowlistlockhttp
Unknown host: ask a human instead of failingNoYes
Writes to a trusted host still need a humanNoYes (with decryption)
Approval runs outside the agent, in another accountNoYes
Claude API checked as a relay (mcp_servers, server tools, files)NoYes (with decryption)
Credential sent outside its own host is deniedNoYes
Policy loosening needs a human confirmationNoYes
Every request traced to container, agent and sessionNoYes

Three verdicts

  • FREE

    GET, HEAD or OPTIONS without a body to a trusted host, or a request matching a free rule. It passes, and the gate logs it.

  • ASK

    An unknown host, a POST/PUT/PATCH/DELETE, an upload, a message. The request is held (60 s) and shown in the console; you answer y or n <reason>.

  • DENY

    The network floor (IP literals, DNS over HTTPS, private ranges, other ports), a secret leaving its host, a policy rule. The agent gets a 403 with a stable E_* code and what to ask of you.

Highlights

  • Human in the loop

    The console runs in its own container, under its own lockhttp account, the only one with the policy. The agent never gets its socket.

  • Fail closed

    No console: an ASK is refused at once (E_NO_CONSOLE). An error while deciding, an unknown source, a policy that does not decode: deny.

  • Progressive auto mode

    Every screen is NeverAuto by default. Only a rule with verdict = "auto", approved by you and scoped to a container, can be answered by auto mode.

  • Identical, not similar

    A decision is shared only by requests of identical fingerprint: container, method, URL, significant headers, normalised body.

  • Masked previews

    The approval screen shows a masked preview of the body (PII detectors and terminal sanitising, first 1 KiB), never the clear body.

  • CA key out of the data plane

    For TLS decryption a separate lockhttp signer holds the CA key and issues leaf certificates; the gate never sees the key.

  • Shadow mode

    mode = "shadow" logs what would have asked or been denied and lets it pass, so you can tune the policy first. The floor and secret checks stay enforced.

  • Refusals the agent can act on

    Every refusal carries a stable code (X-LockHTTP-Code), a request id and a retry rule: retry, ask the user, or stop.

  • Full audit

    FREE and DENY in the gate’s daily JSONL log (30 days), ASK in the console’s audit log, joined by request_id. Never a body or a secret.

Quick start with lea

lockhttp is built for lea, which runs agents in containers whose only way out is its proxy container. The setup, from the lea integration contract:

# 1. Install the launcher: every lockhttp command runs in Docker
V=vX.Y.Z
curl -fsSLO "https://raw.githubusercontent.com/DavidGodefroid/lockhttp/$V/scripts/install.sh"
less install.sh && LOCKHTTP_VERSION=$V sh install.sh

# 2. Write the policy (trusted read-only hosts)
lockhttp init

# 3. In a terminal you keep in view: the console
lockhttp console

# 4. In another terminal: turn lockhttp on in lea (opt-in)
export LEA_LOCKHTTP=1

lea then runs lockhttp gate in a container beside its proxy container, in front of squid. Ask your agent to read the docs of a trusted site: it goes through. Ask it to post somewhere: the request waits for your y in the console. lockhttp doctor checks the setup.

Install and setup details

The Lock family

lockhttp is the fourth member of the Lock family, all built on lockkit: same console, same separation of accounts, same audit, same refusal codes.

  • locksql

    Your AI writes the query. You hold the key. SQL databases.

  • lockmq

    Your AI reads the queue. You hold the key. RabbitMQ brokers.

  • lockfile

    Your AI asks about the file. It never reads it.

  • lockhttp

    Your AI browses. You hold the key. Outbound HTTP(S).

lockfamily.dev

Know the limits

Without TLS decryption, lockhttp decides HTTPS on the host name (CONNECT and SNI): opening a host opens its reads and its writes, and the approval screen says so. Per-request decisions on HTTPS need decryption: a local CA trusted only inside lea’s containers, and lockhttp signer holding its key. Hosts that pin their certificates stay decided at the domain.

A slow, low-bandwidth channel to a trusted host (which packages are fetched, timing) is not something a proxy can close; mirrors and quotas are the answer, later.

Read the security model