Help us shape the future of Go! Take the 2026 Go Developer Survey

janus

module
v0.9.0-rc.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: MIT

README

Janus

test

Janus is a proxy that runs on your machine between Claude Code and the Anthropic API. It records what every request costs, and you can tell it to send some work to a local model in LM Studio, keep secrets and private files from leaving, and keep you working when Claude is down.

flowchart LR
  cc["Claude Code"] -->|"every request"| janus["Janus<br/>127.0.0.1:8787"]
  janus -->|"most requests"| api["Anthropic API"]
  janus -.->|"what your rules send local"| lms["LM Studio<br/>local model"]
  cc -.->|"MCP tools"| mcp["janus mcp"]
  mcp -.-> lms
  janus --> logs[("logs/<br/>ledger and captures")]

Why

Claude Code is easy to run and hard to see into. Janus shows you, request by request, what went where and what it cost, and gives you levers you can pull one at a time:

  • See the spend. A ledger line per request and janus stats break tokens and estimated cost down by kind of request, model, and backend.
  • Try a local model safely. Run it alongside Claude and only compare (shadow), let it answer but check each answer and ask Claude when it fails (local-then-escalate), or trust it outright (local).
  • Keep things private. Secrets can be swapped for placeholders before a request leaves and put back in the answer. Files and markers you name never go to Anthropic at all.
  • Ride out an outage. A rule can have the local model stand in when Claude is unreachable or rate-limited.

With nothing switched on, Janus forwards everything untouched and only keeps the books.

Quick start

You need Claude Code. LM Studio is only needed for the local-model features.

Download the archive for your machine from the releases page. The current release is the pre-release v0.9.0-rc.5:

curl -LO https://github.com/risadams/Janus/releases/download/v0.9.0-rc.5/janus_0.9.0-rc.5_darwin_arm64.tar.gz
curl -LO https://github.com/risadams/Janus/releases/download/v0.9.0-rc.5/checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing
tar -xzf janus_0.9.0-rc.5_darwin_arm64.tar.gz
cd janus_0.9.0-rc.5_darwin_arm64

Other builds are darwin_amd64, linux_amd64, linux_arm64, and, as .zip, windows_amd64 and windows_arm64. The binaries are not signed: a copy downloaded in a browser on macOS is quarantined, which xattr -d com.apple.quarantine janus clears. Or build it with Go 1.27 or later:

go install github.com/risadams/janus/cmd/janus@latest

Check the setup, then start it (drop the ./ if you used go install):

./janus doctor
./janus

On first run Janus writes janus.config.json in the directory you run it from, and prints the command that points Claude Code at it. In another terminal:

ANTHROPIC_BASE_URL=http://127.0.0.1:8787 ENABLE_TOOL_SEARCH=true claude

Work as usual, then see where the tokens went:

./janus stats -since 24h

The quick start guide goes on to the first local route.

What happens to a request

flowchart TD
  req["Request from Claude Code"] --> policy{"Holds local-only<br/>content?"}
  policy -->|"withheld, or none"| plan{"Plan: model alias,<br/>override, or rule"}
  policy -->|"action: local"| local
  plan -->|"upstream"| prep["Repair history, apply transforms,<br/>replace secrets"]
  plan -->|"local or local-then-escalate"| local["Local model answers"]
  local --> check{"Answer usable?"}
  check -->|"no, or LM Studio down"| prep
  prep --> api["Anthropic API"]
  api -->|"down, and the rule has ifDown"| local
  check -->|"yes"| relay["Restore secrets,<br/>relay the answer"]
  api --> relay
  relay --> ledger[("Ledger line<br/>and capture")]

A request the local-only policy keeps local never takes a path to Anthropic: if the local model cannot answer it, it is refused. Every step taken is recorded in the ledger, and what Janus changes lists each one.

Features

Everything is off until you switch it on in janus.config.json.

Feature Switch What it does Guide
The books always on A ledger line per request; full captures of requests and responses, kept 7 days Ledger
Routing features.route and routing rules Sends chosen requests to a local model: shadow, local-then-escalate, or local Local first, subagents
Outage stand-in a rule's ifDown The local model answers when Claude cannot Outage
Guard features.guard, guard.redact, guard.localOnly Finds secrets, swaps them for placeholders, and keeps named files and markers off Anthropic Guard, privacy
Worker tools janus mcp Lets Claude hand reading to the local model, and fetch originals of digested output Workers
Compression features.compress Replaces large tool results with local-model digests. Off by default: it measured at about 1.5% saved Config, evaluation

Every setting is in the configuration reference.

Commands

Command Does
janus Runs the proxy
janus doctor Checks the config, port, upstream, LM Studio, and log directory, and says what to fix
janus stats Tokens and estimated cost from the ledger; -since 24h, -json
janus audit What left this machine, request by request; -verify re-reads what was sent
janus route Where a saved request would go, and why
janus replay Runs saved requests on the local model and compares the answers with Claude's
janus mcp The MCP server for Claude Code's worker tools
janus version Prints the version

Each one takes -h.

Good to know

  • Your credentials pass through untouched, and Janus never stores or logs them. It listens on loopback only, and refuses requests from web pages.
  • Captures hold your prompts and code in full, readable by you only, for 7 days by default. Set logging.capture to false to stop them.
  • One header changes on the way out: on Messages requests Janus asks for gzip rather than brotli or zstd, so it can read the answer.
  • Local models are judged, not trusted. The checks catch broken answers, not wrong ones. The measurements found one model on one Mac fit for subagent turns only.

The threat model covers the rest, and troubleshooting the common problems.

Status

Janus is in pre-release. Observing, routing, escalation, worker tools, compression, and the guard are built and tested; the 1.0 results report waits on a week of everyday use. The roadmap has the milestones, the charter the goals, and research notes the measurements behind each decision.

Diagrams for use outside GitHub are in docs/diagrams.

Development

go test ./...

The suite runs offline against fake servers. AGENTS.md has the conventions, the opt-in tests on real traffic, and the rules that keep Janus safe to change.

License

MIT

Directories

Path Synopsis
cmd
janus command
Command janus is a proxy between Claude Code and the Anthropic API.
Command janus is a proxy between Claude Code and the Anthropic API.
internal
anthropic
Package anthropic holds the Messages API wire types: requests, content blocks, and stream events.
Package anthropic holds the Messages API wire types: requests, content blocks, and stream events.
backend
Package backend defines the interface every model backend satisfies, and its two implementations: the upstream Anthropic API and local LM Studio.
Package backend defines the interface every model backend satisfies, and its two implementations: the upstream Anthropic API and local LM Studio.
capture
Package capture writes the readable per-request logs: a Markdown render, the raw request and response, and the request as it was sent when Janus changed it.
Package capture writes the readable per-request logs: a Markdown render, the raw request and response, and the request as it was sent when Janus changed it.
config
Package config loads and validates the config file, layered with flags and environment overrides.
Package config loads and validates the config file, layered with flags and environment overrides.
digest
Package digest is the transform that replaces an oversized tool result, on its way upstream, with a digest the local model writes.
Package digest is the transform that replaces an oversized tool result, on its way upstream, with a digest the local model writes.
guard
Package guard keeps secrets and protected content from leaving the machine.
Package guard keeps secrets and protected content from leaving the machine.
guard/guardtest
Package guardtest holds the corpus of invented secrets that the guard's tests, and the leak tests, are run against.
Package guardtest holds the corpus of invented secrets that the guard's tests, and the leak tests, are run against.
ledger
Package ledger writes one decision record per request to an append-only log, and computes the cache-weighted cost figures reported by janus stats.
Package ledger writes one decision record per request to an append-only log, and computes the cache-weighted cost figures reported by janus stats.
mcp
Package mcp is a small Model Context Protocol server: enough of the protocol to offer tools to a client over standard input and output.
Package mcp is a small Model Context Protocol server: enough of the protocol to offer tools to a client over standard input and output.
proxy
Package proxy is the HTTP front door.
Package proxy is the HTTP front door.
route
Package route names each request's class and decides which backend answers it: the upstream, or a local model by alias or by rule, with the fallbacks and checks a rule asks for.
Package route names each request's class and decides which backend answers it: the upstream, or a local model by alias or by rule, with the fallbacks and checks a rule asks for.
shadow
Package shadow runs a request a second time on the local model, after the real answer has gone out, and records how the two compare.
Package shadow runs a request a second time on the local model, after the real answer has gone out, and records how the two compare.
transform
Package transform rewrites the content of requests bound upstream, to send fewer tokens, without breaking prompt caching.
Package transform rewrites the content of requests bound upstream, to send fewer tokens, without breaking prompt caching.
validate
Package validate decides whether a local model's answer is fit to give to the client, or should be thrown away in favour of asking Claude.
Package validate decides whether a local model's answer is fit to give to the client, or should be thrown away in favour of asking Claude.
worker
Package worker holds the tools janus offers Claude for handing work to the local model: reading that would otherwise fill Claude's context, done locally, with only a short answer going back.
Package worker holds the tools janus offers Claude for handing work to the local model: reading that would otherwise fill Claude's context, done locally, with only a short answer going back.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL