Every Claude Code session on the dev VM goes through bikoret.ch; when the pod is not ready the session cannot reach Anthropic (2026-10-15: Doorpost drift → 503 → the transmitter locked out, recovered by hand). An `env` entry in ~/.claude/settings.json overrides the shell environment (verified), so `env -u … claude` is not a recovery while the gate is armed there. - scripts/claude-direct: start straight to Anthropic via a --settings JSON on the command line, which outranks every settings file. - scripts/claude-bikoret: probe the gate first; if it does not answer, say so and exec claude-direct (BIKORET_STRICT=1 refuses instead). - scripts/bikoret-settings on|off|status: arm/disarm the gate for every launcher; `on` writes the key (kohen only), `off` is the recovery. - KOHEN.md: the fallback and the order of recovery. Tested 2026-10-15: dead gate armed in settings → plain claude hangs, claude-direct answers; probe at a dead host → fallback + answer; live gate → answer with the fringe. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
34 lines
1.5 KiB
Python
Executable File
34 lines
1.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Arm or disarm the gate in Claude Code's settings.json (the `env` block is
|
|
# applied to EVERY launcher — desktop, background jobs, the plain `claude`).
|
|
#
|
|
# bikoret-settings on put ANTHROPIC_BASE_URL + ANTHROPIC_CUSTOM_HEADERS in
|
|
# bikoret-settings off remove them (the fallback when the gate is down)
|
|
# bikoret-settings status say which
|
|
#
|
|
# `on` writes the courtyard key into a config file, so the transmitter runs it:
|
|
# the builder is not allowed to write a credential there. `off` writes none.
|
|
import json, os, sys
|
|
p = os.path.expanduser("~/.claude/settings.json")
|
|
KEYS = ("ANTHROPIC_BASE_URL", "ANTHROPIC_CUSTOM_HEADERS")
|
|
mode = sys.argv[1] if len(sys.argv) > 1 else "status"
|
|
d = json.load(open(p))
|
|
env = d.get("env", {})
|
|
if mode == "on":
|
|
k = [l.split()[1] for l in open(os.path.expanduser("~/.config/shorashim/api_keys")) if l.startswith("courtyard")][0]
|
|
env.update({"ANTHROPIC_BASE_URL": "https://bikoret.ch/gate/anthropic",
|
|
"ANTHROPIC_CUSTOM_HEADERS": "X-Bikoret-Key: " + k})
|
|
d["env"] = env
|
|
elif mode == "off":
|
|
for key in KEYS:
|
|
env.pop(key, None)
|
|
if env: d["env"] = env
|
|
else: d.pop("env", None)
|
|
elif mode != "status":
|
|
sys.exit("usage: bikoret-settings on|off|status")
|
|
if mode in ("on", "off"):
|
|
json.dump(d, open(p, "w"), indent=2)
|
|
os.chmod(p, 0o600)
|
|
armed = all(k in d.get("env", {}) for k in KEYS)
|
|
print("gate in settings.json:", "ARMED (every launcher goes through bikoret.ch)" if armed else "off (direct to Anthropic)")
|