Installing Janus
This page takes you from nothing to Claude Code talking to Anthropic through Janus. It covers macOS, Linux, and Windows, setting up LM Studio for the local-model features, making the Claude Code setting stick, running Janus in the background, and updating or removing it.
You need Claude Code working already. LM Studio is optional: without it Janus still keeps the books and runs the guard. Before you start lists what each feature needs, and the settings that differ from the defaults.
1. Get the binary
Janus is one file with no dependencies. Pick one way to get it.
With Go
If you have Go 1.27 or later:
go install github.com/risadams/janus/cmd/janus@latest
The binary lands in $(go env GOPATH)/bin, usually ~/go/bin. Make sure
that folder is on your PATH, then check:
janus version
macOS and Linux, from a release
Each release on the releases page has an archive per platform:
| Machine | Archive ends in |
|---|---|
| Mac with Apple silicon (M1 and later) | _darwin_arm64.tar.gz |
| Mac with an Intel processor | _darwin_amd64.tar.gz |
| Linux on x86-64 | _linux_amd64.tar.gz |
| Linux on ARM64 | _linux_arm64.tar.gz |
These commands download the newest release for a Mac with Apple silicon
and check it. For another machine, change darwin_arm64 in the first and
last lines.
url=$(curl -s https://api.github.com/repos/risadams/Janus/releases | grep -m1 -o 'https://[^"]*_darwin_arm64\.tar\.gz')
curl -LO "$url"
curl -LO "${url%/*}/checksums.txt"
shasum -a 256 -c checksums.txt --ignore-missing # on Linux: sha256sum -c checksums.txt --ignore-missing
tar -xzf "${url##*/}"
cd janus_*_darwin_arm64
The checksum line should say OK. The newest release may be a release
candidate; Janus has no 1.0 yet.
If you have the GitHub CLI, you can also check that the archive was built by this repository's release workflow, from the tagged commit:
gh attestation verify "../${url##*/}" --repo risadams/Janus
Move the binary somewhere on your PATH:
mkdir -p ~/.local/bin && mv janus ~/.local/bin/
janus version
On macOS, a copy downloaded in a browser rather than with curl is
quarantined, because the binaries are not signed by Apple. If macOS says
it cannot check the file for malicious software, clear the quarantine:
xattr -d com.apple.quarantine ~/.local/bin/janus
Windows, from a release
Download the _windows_amd64.zip (or _windows_arm64.zip on an ARM
machine) and checksums.txt from the
releases page. In
PowerShell, in the download folder:
Get-FileHash .\janus_*_windows_amd64.zip -Algorithm SHA256
Select-String windows_amd64 .\checksums.txt
The two hashes should match (checksums.txt is lower case; compare the
characters). Then unpack it and check the binary runs:
Expand-Archive .\janus_*_windows_amd64.zip -DestinationPath $HOME\janus
cd $HOME\janus\janus_*_windows_amd64
.\janus.exe version
Windows SmartScreen may warn that the file is from an unknown publisher, since the binaries are not signed. Choose "More info", then "Run anyway".
2. Give Janus a folder
By default Janus reads janus.config.json from the folder you start it
in, and writes its ledger and captures to a folder per user (macOS ~/Library/Application Support/janus, Linux $XDG_STATE_HOME/janus (or ~/.local/state/janus), Windows %LocalAppData%\janus), so they are in one place whichever folder that is. Give the
config a folder of its own:
mkdir -p ~/janus && cd ~/janus
Every janus command takes -config to name the file instead, so you can
keep it anywhere and run commands from any folder.
3. Check, then start
janus doctor
doctor checks the config, the port, that Anthropic answers, LM Studio,
and the log folder, and says what to fix. It changes nothing. Its last
two lines say when the ledger last recorded a request and whether Claude
Code is pointed at Janus; until you have used Janus, claude warns that
it isn't. Before you
set up LM Studio it will warn about it; that is fine for now.
janus
The first run writes janus.config.json with the defaults, then prints
where it is listening, where it writes, and the command for the next
step. Leave it running.
4. Point Claude Code at Janus
In another terminal:
ANTHROPIC_BASE_URL=http://127.0.0.1:8787 ENABLE_TOOL_SEARCH=true claude
In PowerShell:
$env:ANTHROPIC_BASE_URL="http://127.0.0.1:8787"; $env:ENABLE_TOOL_SEARCH="true"; claude
Your login is passed through untouched, so there is nothing to sign in to. Ask Claude anything; Janus prints a line for each request. If it prints nothing, Claude Code is not using it.
ENABLE_TOOL_SEARCH=true matters. When its base URL is not Anthropic's,
Claude Code turns tool search off and sends every tool's definition with
every request. Setting it keeps requests the size they would be without
Janus.
Making it stick
Typing both variables every time gets old. Three ways to make it permanent:
janus claude starts Claude Code through Janus, setting both
variables for that session only. Run it in your Janus folder, or pass
-config with the file Janus runs with:
janus claude # instead of claude
janus claude --resume # Claude Code's own arguments follow
janus claude -config ~/janus/janus.config.json -- -p "hello"
It first checks that Janus is answering on its listen address. If it
isn't:
- With the guard on (
features.guard), it refuses and says to start Janus. Going straight to Anthropic would send what the guard keeps back. - With
-require, it refuses too. - Otherwise it says so and starts Claude Code going straight to Anthropic, so the session works but is not in the ledger.
Janus's own flags (-config, -require) come first; the first argument
that isn't one of them, or everything after --, goes to Claude Code
unchanged. janus claude -h is Janus's help; janus claude -- -h is
Claude Code's. Don't combine it with the settings file below: janus claude cannot change what that file sets, so a stopped Janus would still
cut Claude Code off.
A shell alias keeps plain claude going straight to Anthropic, and
gives you a second command that goes through Janus. In ~/.zshrc or
~/.bashrc:
alias claude-janus='ANTHROPIC_BASE_URL=http://127.0.0.1:8787 ENABLE_TOOL_SEARCH=true claude'
Claude Code's settings file sends every session through Janus. In
~/.claude/settings.json:
{
"env": {
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8787",
"ENABLE_TOOL_SEARCH": "true"
}
}
With the settings file, Claude Code cannot reach Anthropic at all while Janus is stopped, so either keep Janus running in the background (below) or remember to remove the setting.
5. Set up LM Studio (optional)
Routing, the outage stand-in, the worker tools, and compression all need a local model. Janus uses LM Studio for that.
-
Install LM Studio and download a model. Janus is tested with
qwen/qwen3.8-27bat 4-bit, on a Mac with 36 GB of memory, where it handles one request at a time comfortably; two at once took it past 31 GB. Small models (around 4 billion parameters) could not drive Claude Code's tools in testing; their answers came back empty or rambling. -
Start LM Studio's local server, from the app. It listens on port 1234. To use LM Studio on another machine, turn on serving on the local network in its server settings; Before you start has the settings, and how to check it can be reached.
-
Tell Janus where it is, in
janus.config.json:"local": {"baseUrl": "http://127.0.0.1:1234","models": { "janus-local": { "model": "qwen/qwen3.8-27b" } }}Always set
baseUrl. The default,http://studio.local:1234, is a placeholder: it is the Janus developer's own LM Studio machine. On the same machine as Janus usehttp://127.0.0.1:1234; on another, its address and port. Themodelmust be the id LM Studio shows, exactly;janus doctorlists the ids it found. -
Run
janus doctoragain. Thelocalline should beokand name the model.
You don't have to load the model in LM Studio yourself. When a request
needs it and it isn't loaded, Janus has LM Studio load it, with a
65,536-token context unless you set contextTokens.
Nothing goes to the local model until you turn on a feature that uses it. Configuring Janus has complete configs for each.
6. Run Janus in the background (optional)
Running Janus in a terminal is fine for trying it. To have it start when you log in, and again if it stops, run it as a service. From your Janus folder:
janus service install # or: janus service install -config ~/janus/janus.config.json
janus service status
janus service uninstall
install writes the unit with the full paths of the janus binary and
the config file, since a service starts in no particular folder, and
starts it. It refuses a config file that does not exist, or has
problems, and a binary from go run, which is deleted when it exits:
install janus first. Run install again after moving either.
On macOS it writes a launchd agent,
~/Library/LaunchAgents/com.github.risadams.janus.plist, and the
console lines Janus would print go to janus.log in its log folder; the
command prints how to follow them. On Linux it writes a systemd user
unit, ~/.config/systemd/user/janus.service, and the console goes to
the journal: journalctl --user -u janus -f.
To write one yourself instead, the plist runs the binary with -config
and the config's full path, with RunAtLoad and KeepAlive set, and is
loaded with launchctl bootstrap gui/$(id -u) <plist>; the systemd unit
has ExecStart=<binary> -config <config> and Restart=on-failure, and
is started with systemctl --user enable --now janus.
Windows
Run it from a terminal you keep open, or create a Task Scheduler task
that runs at log on, with janus.exe as the program and
-config C:\Users\you\janus\janus.config.json as its arguments.
Only one Janus runs per log folder. If a second one starts, it stops and names the first, so a service and a copy in a terminal cannot both write the same ledger.
Updating
janus version -check says whether a newer release is out, and how to
upgrade the copy you have; janus doctor says the same in one line. Only
stable releases are offered unless you add -pre, or set
update.prerelease in the config, to count pre-releases too. The check
asks GitHub for the list of releases and sends nothing else, and only
when you run one of these commands.
To hear about a new release without asking, set "update": {"check": true} in the config. Janus then looks when it starts, at most once a
day, and prints a line on the console if a newer release is out. It
never updates itself.
janus update upgrades a copy installed from a release archive. It
downloads the newest release for your machine, checks the signature on
its checksums.txt against the key built into the Janus you have and
the archive against those sums, and only then replaces the binary. If
any check fails, it says why and leaves the binary as it was. If janus service install set Janus up to run in the background, janus update
restarts it; the old Janus finishes the requests it has in flight, for
up to a minute, before the new one starts. Otherwise, restart Janus
yourself. -pre and update.prerelease count pre-releases, as they do
for janus version -check.
A copy from go install or Homebrew is refused, since those upgrade it
themselves: run the go install command again, or brew upgrade janus.
You can also download the new release as in step 1 and replace the
binary by hand. Your config, ledger, and captures are left as they are. janus version says which version you
have, and the console prints it at the top when Janus starts.
Removing Janus
- Stop Janus, and remove its service if you made one (
janus service uninstall). - Remove the setting that points Claude Code at Janus: the alias, or the
enventries in~/.claude/settings.json. - If you added the worker tools,
claude mcp remove janus. - Delete the binary, the Janus folder, and the log folder
(
logging.dir;janus doctornames it). The log folder holds the captures, which contain your prompts and code in full, andredact.key, the key for the guard's placeholders.
Next: configuring Janus.