primarypath

Let AI agents act.Keep the proof.

Human approvals for consequential AI-agent actions.

Let agents investigate and handle routine work. Require an authorized person's approval before they move money, change production or disclose sensitive data, and keep a record of the decision that anyone can verify.

Open source and self-hosted: an approval inbox, policy controls and signed audit evidence for the agents you already run.

  1. 1Agent asks to refund €840
  2. 2Reviewer reads the evidence
  3. 3Refund rejected
  4. 4Decision recorded

What the reviewer sees

refund-ORD-91277:b5 · via drangue

Proposed action high risk · irreversible

refund

€840.00

reason
item not received (claimed)
order_id
ORD-91277

Policy

No rule lets refunds run on their own, so this one waits for a person. It is irreversible and high risk, so it is decided individually, never in bulk.

Agent's reasoning

Claim is item-not-received on a delivered order with a signed handoff (carrier scan 2026-06-29 14:02). Account is 22 days old, 1 prior order, address changed after purchase: 3 fraud markers. Amount is 8x the autonomous cap and the small path refused it. Policy requires senior review; evidence does NOT support settlement. Recommend: deny and route to the fraud team, or approve only against a returned-goods scan.

Decision

rejected by j.keller: “signed delivery + 3 fraud markers; routing to fraud team”

The agent is told why and carries on. The refund never runs.

Then try to change it

Change the record, and the chain shows where.

This is the log the two demos wrote; the refund above is records 12 and 13. Your browser has just recomputed every hash and checked the signature against the pinned key.

Rewrite the refund's amount the way someone with database access would, and the check fails at the record that changed. The offline verifier, primarypath-verify, does the same on an auditor's own machine.

A real audit log, checked by your browser just now

bundle.json

Recomputing every hash…

    Each record holds the SHA-256 of the one before it, and the chain heads are signed with Ed25519.

    Only what matters waits

    Routine work runs. The rest waits for the right person.

    You decide, action by action, what needs a person and which people may decide. Everything else proceeds on its own and is still recorded.

    As an action builds a clean track record, the console recommends loosening its rule: from every case reviewed, to a random sample, to none. An action with no rule at all waits for a person.

    An example configuration, not a default
    SituationOutcomeSet by
    Routine work: reading logs, looking up an order, a refund under €100 through a capped small-refund toolProceeds on its ownautonomous
    Anything above that: a refund through the full refund tool, like the €840 aboveA reviewer from the finance group decidesassisted, approver group
    Particularly sensitive: a wire transferTwo different people must approvedual control
    An action whose budget for model spend is used upRefused, or held for a personbudget

    Rules apply to an action, the tool the agent calls. A limit on an amount is two tools, a small one with the cap in its code, as in the refund demo. Use guide

    The problem

    An agent that acts can be wrong once and cost you the quarter.

    Refunds, rollbacks, record exports: most teams guard them in one of three ways, and each one breaks in a predictable place.

    • Let it act, watch the logs

      Works until the one refund that was fraud, or the rollback of the deploy that was fine. The log tells you afterwards. It can't stop anything.

    • Ask in chat

      "OK to roll back checkout?" gets a thumbs-up from someone who saw one line, not the agent's evidence. Six months later nobody can prove who approved what.

    • Build an approval step per agent

      Every team writes its own pause, queue and audit table. The table is rows in a database the operator controls: to an auditor, a promise rather than proof.

    How it works

    One control between the agent and the action.

    1. 01

      Propose

      The agent sends the action it wants to take: tool, exact arguments, and its reasoning.

    2. 02

      Pause

      The run waits, durably. The agent or the server can restart and the case is still there.

    3. 03

      Decide

      A person with the right role approves, rejects with a reason, or sends it back, in the inbox, Slack or Teams.

    4. 04

      Resume

      The agent gets the decision. An approval carries a signed, single-use grant bound to that exact action.

    5. 05

      Prove

      Every step is a hash-chained, signed record. Anyone can verify an export offline, and tampering names the record.

    It doesn't run your agent and doesn't care what it is written in. Any agent that can make two HTTP calls can be governed: one to propose, one to learn the decision.

    For LangGraph, the OpenAI Agents SDK, the Claude Agent SDK and drangue there is a package that uses the framework's own pause. For tools over MCP, a gateway sits in between and the agent needs no changes. Integrations

    # 1. propose: the run pauses
    POST /api/approvals
    {"run_id": "run-42", "call_id": "step-7",
     "tool": "send_wire",
     "arguments": {"amount": 90000, "to": "ACME GmbH"},
     "reasoning": "Invoice INV-311 matches the PO."}
    
    # 2. poll until a person decides
    GET /api/approvals/run-42:step-7
    {"status": "granted", "reviewer": "m.okafor", ...}

    See it run

    Two agents, two decisions, one verifiable record.

    The refund that should not be paid

    A support agent gets a claim for an €840 espresso machine that "never arrived". It was signed for at the door, and the account is 22 days old. The agent's small-refund tool refuses anything over €100 in code, so it escalates. The reviewer rejects it with a reason, and nothing is paid.

    pip install primarypath-demos
    primarypath-demo refunds

    The rollback that should run once

    An on-call agent works an incident: error rate, logs, recent deploys, the runbook. It can investigate alone, but rolling back production waits for the on-call lead, who sees the whole investigation and approves. The approval comes with a signed grant for that exact version.

    primarypath-demo sre

    Run it

    Two commands to a working inbox.

    You need Docker. The compose file pins the signed image by digest and starts Postgres beside it, on this machine only.

    Everything else: Helm, the Python and TypeScript packages, and how to verify what you download.

    curl -fsSLO https://github.com/om-er/primarypath-releases/releases/latest/download/docker-compose.yml
    docker compose up -d --wait
    # open http://127.0.0.1:8123
    python3 -m venv demo && . demo/bin/activate
    pip install primarypath-demos
    primarypath-demo refunds

    Runs natively on Intel and ARM, Apple Silicon Macs included. Install covers production.

    Built to be checked

    Trust it because you can verify it.

    Runs in your network
    One container and your Postgres. Case data goes nowhere else unless you route it to your own Slack, Teams or SIEM.
    No phone-home
    No telemetry, no license server, no update check. The test suite runs with outbound network blocked.
    Verifiable offline
    primarypath-verify re-proves an export on an air-gapped machine against a key you pinned.
    Signed releases
    Image, chart, packages and compose files are signed with Sigstore; check them before you run them.
    Honest about its limits
    An agent holding its own credentials can act without asking. The threat model says so, and says what to do about it.
    Free, Apache-2.0
    No tiers and no license keys. Report security issues privately.