Skip to content

Releases: aryan597/kiri-gate

v0.2.1: fixes from an outside review

Choose a tag to compare

@aryan597 aryan597 released this 29 Sep 10:14

Someone reviewed the code and found real gaps. These are the fixes.

  • Secrets redacted. Arguments and results are redacted before they reach the decision log, the ledger file, the terminal prompt, the approval page or a scorer. Secret-looking names (password, token, api_key, card_number, ...) and known key formats (sk_live_, ghp_, AKIA, Bearer, JWTs) become [REDACTED]. Add your own with @gate.tool(..., sensitive={"field"}). The tool still gets the real values, and an edit that leaves [REDACTED] keeps the real value.
  • Scope. Gate(scope="user-123") or gate.goal(text, scope=...) puts the user in the dedupe key, so two users sending the identical email through one shared ledger are two calls. No scope set means the same keys as v0.2.0.
  • Expiry. Ledger rows and in-memory results older than the 24 hour window are now deleted (every 100 writes, or Ledger.prune()), not just ignored.
  • Docs say what's measured. The README has a "What we know" table: each claim, its evidence, whether it's measured, true by construction or untested, and its limits. "Can't happen twice" wording is gone: it catches identical calls, and never retries an uncertain one automatically.
  • 12 new tests (61 in total).

What's measured, what's true by construction and what's untested: see "What we know" in the README.

v0.2.0: MCP proxy and double-charge protection

Choose a tag to compare

@aryan597 aryan597 released this 28 Sep 18:52

v0.2.0: MCP proxy and double-charge protection

Doing things twice

In experiments/chaos, a LangGraph agent charged a real Stripe test card twice in 5 of 5 runs when the response was lost after the charge. kiri-gate now stops that.

  • IRREVERSIBLE and EXTERNAL tools get a key from the tool, its arguments and the goal (make_key), kept in a Ledger (in memory, or SQLite with Ledger("kiri.db")) for 24 hours
  • An identical call that already succeeded returns the saved result and doesn't run or ask
  • One that failed may have happened anyway: it's checked with the tool's reconcile, or asked about. Never run blindly
  • An identical call still running is refused
  • NotExecuted: raise it when nothing happened, and a retry runs normally
  • A tool with an idempotency_key parameter gets the key passed in, to forward to Stripe or similar
  • @gate.tool(cls, dedupe=True/False) to opt in or out
  • With the fake Stripe from the chaos study, the kiri_gate condition charges once under LangGraph's default retry policy

MCP proxy

Put the gate in front of any stdio MCP server, with no code changes to the client or the server.

  • kiri-gate mcp -- <server command> (or python -m kiri_gate mcp -- ...): passes every message through and gates tools/call
  • kiri.toml holds each tool's class. Only [tools] is trusted. Anything else, including tools the server adds later, is IRREVERSIBLE and always asks
  • On first run it writes a draft kiri.toml, suggesting classes from readOnlyHint, destructiveHint and openWorldHint, all unconfirmed
  • A denied call comes back as a normal tool error; the server never sees it
  • Duplicates: a repeat of a call that succeeded gets the saved result and isn't sent again. After an error, a repeat always asks

Approval page

  • http://127.0.0.1:8766: allow, deny or edit the arguments, with a countdown and a log view
  • Claude Desktop cancels tool calls at 60s, so an unanswered ask is denied at 55s (--ask-timeout) and the model is told it did not run
  • Several servers share one approval page, one decision log and one ledger (~/.kiri/kiri.db)
  • The page only listens on 127.0.0.1 and refuses requests from other sites

Also: Gate.run(name, args) for callers that already have the arguments as a dict.

Install: pip install git+https://github.com/aryan597/kiri-gate@v0.2.0

Next: asking through MCP elicitation where the client supports it (v0.2.1).

v0.1.0: the core gate

First release. No dependencies, Python 3.9+.

  • Gate with four action classes: READ, UNDOABLE, IRREVERSIBLE, EXTERNAL
  • IRREVERSIBLE and EXTERNAL always ask, with no score, setting or model override
  • A tool's class is fixed when it's registered and can't be registered twice
  • UNDOABLE actions run if a scorer says they're likely fine; a failing scorer means ask
  • Scorers for local models (LM Studio, Ollama, any OpenAI-compatible server) and Claude
  • DecisionLog: every decision in SQLite, ready for calibration
  • Terminal approval, edit-before-approve, and Denied for rejected calls

Why this rule: in ask-or-act, four models from a local 9B to Claude Opus 5 each let 4 to 6 unrecoverable actions through when deciding alone. This rule took that to zero on all four.

Install: pip install git+https://github.com/aryan597/kiri-gate@v0.1.0

Next: the MCP proxy (v0.2). See ROADMAP.md.

v0.1.0: the core gate

Choose a tag to compare

@aryan597 aryan597 released this 25 Sep 01:18

v0.1.0: the core gate

First release. No dependencies, Python 3.9+.

  • Gate with four action classes: READ, UNDOABLE, IRREVERSIBLE, EXTERNAL
  • IRREVERSIBLE and EXTERNAL always ask, with no score, setting or model override
  • A tool's class is fixed when it's registered and can't be registered twice
  • UNDOABLE actions run if a scorer says they're likely fine; a failing scorer means ask
  • Scorers for local models (LM Studio, Ollama, any OpenAI-compatible server) and Claude
  • DecisionLog: every decision in SQLite, ready for calibration
  • Terminal approval, edit-before-approve, and Denied for rejected calls

Why this rule: in ask-or-act, four models from a local 9B to Claude Opus 5 each let 4 to 6 unrecoverable actions through when deciding alone. This rule took that to zero on all four.

Install: pip install git+https://github.com/aryan597/kiri-gate@v0.1.0

Next: the MCP proxy (v0.2). See ROADMAP.md.