https://github.com/ntholm86/llm-harness-proxy
Transparent MITM proxy that intercepts LLM traffic and writes a tamper-evident, hash-chained audit ledger before releasing the response.
https://github.com/ntholm86/llm-harness-proxy
agent-monitoring ai-safety anthropic audit-trail autonomous-agents earned-autonomy fail-closed hash-chain llm mitm-proxy observability observable-autonomy openai proxy rust
Last synced: 6 days ago
JSON representation
Transparent MITM proxy that intercepts LLM traffic and writes a tamper-evident, hash-chained audit ledger before releasing the response.
- Host: GitHub
- URL: https://github.com/ntholm86/llm-harness-proxy
- Owner: ntholm86
- Created: 2026-05-07T16:41:52.000Z (about 2 months ago)
- Default Branch: master
- Last Pushed: 2026-06-20T14:33:36.000Z (9 days ago)
- Last Synced: 2026-06-20T15:24:59.547Z (9 days ago)
- Topics: agent-monitoring, ai-safety, anthropic, audit-trail, autonomous-agents, earned-autonomy, fail-closed, hash-chain, llm, mitm-proxy, observability, observable-autonomy, openai, proxy, rust
- Language: Rust
- Homepage: https://github.com/ntholm86/principles-of-earned-autonomy
- Size: 4 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# llm-harness-proxy
A transparent MITM proxy that writes a tamper-evident, hash-chained ledger of every LLM interaction — before the response is released to the caller.
The proxy enforces **Architectural Constraint**: an LLM response cannot reach the client unless its reasoning and actions have been durably persisted. This is a structural guarantee, not a behavioral one.
The ledger format is specified in [SPEC.md](./SPEC.md).
---
## Quickstart
**1. Download the binary** from the latest [CI build](../../actions/workflows/build-proxy.yml):
- `llm-harness-proxy-windows` — Windows x86_64
- `llm-harness-proxy-linux` — Linux x86_64
**2. Run the proxy:**
```sh
./llm-harness-proxy
```
The proxy listens on `127.0.0.1:8474` by default (`HARNESS_LISTEN` to override).
`HARNESS_ROOT` (the session write location) is resolved in this order:
| Priority | Condition | Session location |
|---|---|---|
| 1 | `HARNESS_ROOT` env var is set | `$HARNESS_ROOT/sessions/` |
| 2 | cwd is inside a git repo | `/.harness/sessions/` |
| 3 | no git repo found | `~/.harness/sessions/` |
When **ai-steward** manages a project it sets `HARNESS_ROOT` to the target repository root — sessions land inside that repo and can be committed alongside the code. When running standalone (as above), the proxy detects the nearest git repo automatically.
**3. Point your LLM client at the proxy:**
```python
# OpenAI / Grok
client = OpenAI(base_url="http://127.0.0.1:8474/v1", api_key="...")
# Anthropic
client = Anthropic(base_url="http://127.0.0.1:8474", api_key="...")
```
**4. Each call produces a session file:**
```jsonc
// .harness/sessions/.jsonl — one line per turn
{
"v": 1, "seq": 0, "sid": "01KRNDE2C2DBE9AWNYPXKGSD7M",
"ts": "2026-05-15T08:53:17.241Z", "model": "claude-haiku-4-5",
"in": "sha256:faf7bc...", // SHA-256 of the canonicalized request
"prev": "sha256:000000...", // genesis; subsequent turns chain here
"think": null, // extended reasoning tokens (if any)
"reason": "", // model text output
"act": { // tool call (if any)
"name": "record_result",
"input": { "status": "harness-act-verified" }
},
"transparency": { "think": false, "act": true }
}
```
---
## Supported providers
| Provider | Endpoint | Notes |
|---|---|---|
| OpenAI / Grok | `/v1/chat/completions` | Streaming + buffered. `UPSTREAM_BASE_URL` to override (default: `https://api.openai.com`). |
| Anthropic | `/v1/messages` | Streaming + buffered. `ANTHROPIC_BASE_URL` to override (default: `https://api.anthropic.com`). |
| Gemini | `/v1beta/models/*` | Streaming + buffered. `GEMINI_BASE_URL` to override (default: `https://generativelanguage.googleapis.com`). |
The proxy is a dumb pipe: all request headers (including `Authorization` / `x-api-key`) are forwarded verbatim. No credentials are read or stored.
---
## Environment variables
| Variable | Default | Description |
|---|---|---|
| `HARNESS_ROOT` | *(see below)* | Session root. If unset: git repo root → `~/.harness`. Created if absent. |
| `HARNESS_LISTEN` | `127.0.0.1:8474` | Address to bind. |
| `UPSTREAM_BASE_URL` | `https://api.openai.com` | Upstream for `/v1/chat/completions`. |
| `ANTHROPIC_BASE_URL` | `https://api.anthropic.com` | Upstream for `/v1/messages`. |
| `GEMINI_BASE_URL` | `https://generativelanguage.googleapis.com` | Upstream for `/v1beta/models/*`. |
---
## How it works
```
Client → llm-harness-proxy:8474 → Real LLM API
↓
.harness/sessions/.jsonl
(fsync'd before response forwarded)
```
Each session is one JSONL file named by a ULID (sortable by creation time). Each line is one turn. Consecutive turns are linked by a SHA-256 hash chain over RFC 8785 canonicalized entries — tampering or reordering any entry breaks the chain.
The `transparency` object records whether `think` and `act` carried content, enabling downstream analysis without content inspection.
See [SPEC.md](./SPEC.md) for the full protocol definition (entry format, hash chain, streaming continuations, failure semantics, conformance tiers).
---
## Build from source
Requires Rust stable.
```sh
cd proxy-rust
cargo test # 15 unit tests: ledger integrity, JCS canonicalization, ULID
cargo build --release
```
CI builds and uploads artifacts for Windows x86_64 and Linux x86_64 on every push to `proxy-rust/`.
---
## Design principles
- **Fail-closed.** If the ledger write fails, the response is withheld. The client sees an error. The LLM cannot act without a record.
- **Dumb pipe.** The proxy does not interpret, filter, or modify content. It captures what the model sent and what the client sent.
- **Zero client integration.** One `base_url` change. No SDK wrapping, no library import.
- **Single binary.** No runtime, no daemon, no configuration file.
---
## Documentation
- [SPEC.md](./SPEC.md) — Full protocol specification (entry format, hash chain, streaming, failure semantics)
- [docs/AAT-MAPPING.md](./docs/AAT-MAPPING.md) — Field-by-field mapping to IETF Agent Audit Trail (draft-sharif-agent-audit-trail-00)