Governed action boundary · for tool-using AI systems

Before your agent calls a risky tool, ask KIFF.

Allowed, approval required, or blocked — before execution.

KIFF sits between an agent's proposal and the real-world action: refunds, emails, database updates, infrastructure changes, or any other tool call you do not want running unchecked.

proposal gate · curl
curl https://api.kiff.dev/v1/proposals/decide \
  -H 'authorization: Bearer kiff_live_<tenant>_<random>' \
  -H 'content-type: application/json' \
  -d '{
    "id": "toolcall_01J8Z9",
    "entity_id": "ord_002",
    "entity_type": "order",
    "action_name": "ISSUE_REFUND",
    "actor_id": "support-agent",
    "parameters": {"amount": 4900, "reason": "customer request"}
  }'
decide · response
{
  "proposal_id": "toolcall_01J8Z9",
  "outcome": "approval_required",
  "reasons": ["approval_required"],
  "message": "request approval before executing"
}

Your app still owns execution. KIFF owns the decision boundary and the evidence trail: what was proposed, why it was allowed or blocked, and which receipt proves it later.

How it works · pre-execution decision

The agent proposes. KIFF decides. Your app executes.

KIFF is not the agent runtime and not the tool. It is the layer your workflow calls before a risky action touches the real system.

  1. 1

    Proposal

    Agent wants to issue a refund, send an email, update a row, or call an MCP tool.

  2. 2

    Decision

    KIFF checks the active domain: entity state, action contract, permissions, and approval rules.

  3. 3

    Branch

    Your app executes only when allowed, pauses for approval, or refuses to call the tool.

  4. 4

    Evidence

    The proposal, decision, approval, and receipt become the audit trail.

One refund tool. Three governed outcomes.

  • $49 refund allowed safe to execute
  • $4,900 refund approval_required pause for review
  • $7,000 refund blocked do not call Stripe
customer app · branch
decision = ask_kiff(proposal)

if decision.outcome == "allowed":
    call_real_tool()
elif decision.outcome == "approval_required":
    pause_for_human_review()
else:
    do_not_execute()

Problem · the wrong layer

Where AI operations fail.

Probabilistic systems are being connected to deterministic consequences. The issue is not that the model is useless. The issue is that every new layer adds more authority without adding a shared execution contract.

  1. ambiguous

    Prompt

    The instruction sounds right, but it is not an enforceable contract.

  2. side effect

    Tool call

    The agent can now order, refund, send, update, or delete.

  3. drift

    Workflow

    Steps multiply, retries happen, each path handles risk differently.

  4. fragmented

    State machine

    Each team invents its own lifecycle, approvals, and exceptions.

  5. unprovable

    Audit

    Logs say something happened, but not why it was allowed.

Wrong layer. Prompt rules are easy to change and hard to prove.

Missing boundary. The model can propose and execute through the same path.

No common shape. Agents, tools, and workflows do not share one decision record.

Worked example · same domain, different outcomes

One risky action. Five honest endings.

The refund example is here to make the boundary concrete: low-risk actions can proceed, risky actions can pause, and invalid actions stop before the executor runs.

Small refund

executed

Low-risk refund on a paid order. Auto-execute, audit, done.

propose · curl
curl https://api.kiff.dev/v1/entities/ord_001/actions/AUTO_REFUND/execute \
  -H 'authorization: Bearer kiff_live_<tenant>_<random>' \
  -H 'content-type: application/json' \
  -d '{
    "entity_type": "order",
    "actor": {"id": "ops-agent", "type": "agent", "roles": ["operator"]},
    "parameters": {"amount": 49.0, "reason": "customer request"}
  }'
decide · response
{
  "result": {
    "action_name": "AUTO_REFUND",
    "entity_id": "ord_001",
    "status": "succeeded",
    "executed": true,
    "message": "auto-refund of $49.00 issued"
  }
}
Audit trail · 7 records
timekindactorsummary
09:41:02.123event_ingestedstripeORDER_PLACED
09:41:02.124state_changedstripeCREATED → PAID
09:42:11.001decision_recordedops-agentpropose AUTO_REFUND
09:42:11.002action_validatedops-agentAUTO_REFUND ✓
09:42:11.005action_executedops-agentAUTO_REFUND
09:42:11.006event_ingestedops-agentORDER_REFUNDED
09:42:11.007state_changedops-agentPAID → REFUNDED

rebuild: materialized state matches replay ✓

Order is PAID. Amount is below the auto-execute threshold. AUTO_REFUND requires no approval. KIFF runs it.

Large refund

approval required

High-risk refund. KIFF holds execution and asks a human.

propose · curl
curl https://api.kiff.dev/v1/entities/ord_002/actions/REFUND_ORDER/execute \
  -H 'authorization: Bearer kiff_live_<tenant>_<random>' \
  -H 'content-type: application/json' \
  -d '{
    "entity_type": "order",
    "actor": {"id": "ops-agent", "type": "agent", "roles": ["operator"]},
    "parameters": {"amount": 999.0, "reason": "customer request"}
  }'
decide · response
# 422 Unprocessable Entity
{
  "error": "action requires approval",
  "action": "REFUND_ORDER",
  "approval_id": "appr_xyz"
}

# next: POST /v1/entities/ord_002/actions/REFUND_ORDER/approvals
# (open the approval, then a human reviewer grants or denies it)
Audit trail · 4 records
timekindactorsummary
09:48:02.001event_ingestedstripeORDER_PAID
09:48:02.002state_changedstripePAID
09:50:18.430decision_recordedops-agentpropose REFUND_ORDER (high)
09:50:18.431approval_requiredops-agentREFUND_ORDER awaiting human

rebuild: materialized state matches replay ✓

REFUND_ORDER is high-risk. ApprovalRequirement is required. KIFF blocks execution and opens a review record.

Approval granted

approval granted

A reviewer signs off on the held refund. KIFF executes the original proposal.

propose · curl
curl https://api.kiff.dev/v1/approvals/appr_xyz/grant \
  -X POST \
  -H 'authorization: Bearer kiff_live_<tenant>_<random>' \
  -H 'content-type: application/json' \
  -d '{
    "actor": {"id": "ops-human", "type": "human", "roles": ["ops_operator"]},
    "reason": "verified with customer"
  }'
decide · response
{
  "approval": {
    "id": "appr_xyz",
    "status": "granted",
    "action_name": "REFUND_ORDER",
    "entity_id": "ord_002",
    "reviewed_by": "ops-human",
    "reviewed_at": "2026-05-24T09:53:55.011Z"
  }
}

# Re-issue the original execute call with approval_id=appr_xyz to run it.
Audit trail · 4 records
timekindactorsummary
09:53:55.011approval_grantedops-humanREFUND_ORDER granted
09:53:55.092action_executedops-agentREFUND_ORDER
09:53:55.093event_ingestedops-agentORDER_REFUNDED
09:53:55.094state_changedops-agentPAID → REFUNDED

rebuild: materialized state matches replay ✓

Approval was granted by a human reviewer. KIFF marks the context approved and runs the original proposal.

Approval denied

blocked

A reviewer rejects the held refund. The action never runs.

propose · curl
curl https://api.kiff.dev/v1/approvals/appr_xyz/deny \
  -X POST \
  -H 'authorization: Bearer kiff_live_<tenant>_<random>' \
  -H 'content-type: application/json' \
  -d '{
    "actor": {"id": "ops-human", "type": "human", "roles": ["ops_operator"]},
    "reason": "order disputed"
  }'
decide · response
{
  "approval": {
    "id": "appr_xyz",
    "status": "denied",
    "action_name": "REFUND_ORDER",
    "entity_id": "ord_002",
    "reviewed_by": "ops-human",
    "reviewed_at": "2026-05-24T10:01:20.004Z"
  }
}

# A denied approval is terminal. Subsequent execute calls
# under the same context return state_not_allowed.
Audit trail · 2 records
timekindactorsummary
10:01:20.004approval_deniedops-humanREFUND_ORDER denied
10:01:20.005action_blockedops-agentREFUND_ORDER not executed

rebuild: materialized state matches replay ✓

A denied approval is terminal. KIFF will not run the action even if the same proposal is retried under the same context.

Unpaid order

blocked

Refund attempted on an unpaid order. State guard blocks before any executor runs.

propose · curl
curl https://api.kiff.dev/v1/entities/ord_003/actions/AUTO_REFUND/execute \
  -H 'authorization: Bearer kiff_live_<tenant>_<random>' \
  -H 'content-type: application/json' \
  -d '{
    "entity_type": "order",
    "actor": {"id": "ops-agent", "type": "agent", "roles": ["operator"]},
    "parameters": {"amount": 49.0, "reason": "customer request"}
  }'
decide · response
# 422 Unprocessable Entity
{
  "error": "state not allowed",
  "action": "AUTO_REFUND",
  "current_state": "CREATED",
  "allowed_states": ["PAID"]
}
Audit trail · 4 records
timekindactorsummary
10:14:09.220event_ingestedstripeORDER_PLACED
10:14:09.221state_changedstripeCREATED
10:15:30.118decision_recordedops-agentpropose AUTO_REFUND
10:15:30.119action_blockedops-agentstate_not_allowed

rebuild: materialized state matches replay ✓

AUTO_REFUND requires PAID. Order is CREATED. KIFF refuses to call the executor; the LLM's confident proposal does not become a state change.

These are worked examples, not your tenant data. The product path is to upload your own domain and send proposals from your app.

Trust evidence · signed receipts

Logs say something happened. Receipts say what was decided.

Every governed proposal should leave evidence: who proposed the action, what domain was active, what decision KIFF returned, and what happened next.

  • Customer trust

    Show why an AI action was allowed, held, or blocked instead of asking customers to trust a black box.

  • Audit readiness

    Keep a consistent record of the proposal, decision, policy version, approval, and follow-up action.

  • Dispute defense

    When something goes wrong, reconstruct the decision path instead of searching scattered logs.

  • Operational control

    Downstream systems can require a valid KIFF receipt before executing risky actions.

receipt contract · json
{
  "schema_version": "kiff.receipt.v0",
  "trace_id": "trace_01J8Z8...",
  "tenant_id": "ten_demo",
  "entity_id": "ord_002",
  "entity_type": "order",
  "action_name": "ISSUE_REFUND",
  "issued_at": "2026-05-26T02:00:00Z",
  "causal_chain": [
    {"kind": "proposal_recorded", "actor_id": "support-agent"},
    {"kind": "proposal_decision", "data_hash": "0x8f4a..."}
  ],
  "attestation": {
    "algorithm": "secp256k1-keccak256",
    "wallet_address": "0xb6947c...",
    "signature": "0x45e91a...",
    "anchor_tx_hash": ""
  }
}

This is the evidence shape. A real signed receipt from Kiff Cloud is rendered at /dashboard/receipts/trace-receipt-prod-1. Same idea: signed decision evidence, with anchoring shown only when the live receipt has an attestation.

Why · prompts are not policy

Free-form tool calls need a contract before execution.

Most AI features start at the prompt. They work in development, then break the first time an agent confidently does the wrong thing in production. KIFF adds the boundary the prompt cannot: a domain contract that decides before the tool runs.

without kiff · python
# the agent calls your real system directly.
# state guards live in the prompt, if they live anywhere.

def refund(order_id: str, amount: int, reason: str) -> dict:
    db.update("orders", id=order_id, status="REFUNDED")
    stripe.refund(order_id, amount=amount)
    return {"ok": True}

# llm chooses inputs. nothing checks the order is paid.
# nothing requires approval at $99,900. no audit trail.
with kiff · go
// the agent records a proposal. the runtime decides.
return action.ActionContract{
    Name:                "REFUND_ORDER",
    AllowedStates:       []string{"PAID"},
    RequiredParameters:  []string{"amount", "reason"},
    RequiredPermissions: []permission.Permission{"orders.refund"},
    Risk:                action.RiskHigh,
    ApprovalRequirement: action.ApprovalRequired,
    Executor:            refundOrderExecutor,
}

The agent does not call refundOrderExecutor directly. It records a proposal. The runtime checks current state, parameters, permissions, and approval before any executor runs. Failures return typed errors. Successes append events that drive the next state transition.

Examples · two operational stories

Two examples, two proofs.

Pick an example. Each one is a concrete situation; the runtime is the difference between confident proposals and honest endings.

Same refund agent. Different authority.

An agent confidently proposes a refund. KIFF decides whether it can actually happen.

without kiff agent calls your system directly
  • agent calls refund() directly
  • refund executes — no questions asked
with kiff agent records a proposal
  • auto-execute on small refunds
  • approval_required on large refunds
  • blocked on unpaid orders
View outcome table · 3 rows
inputoutcomenote
small refund · PAIDexecutedauto-execute
large refund · PAIDapproval_requiredhuman review
refund · CREATEDblockedstate guard

The shape · six primitives, one loop

Between proposal and execution.

KIFF puts a runtime between the agent and your real systems. Every action is a contract: allowed states, required parameters, required permissions, risk, approval requirement. The agent proposes; the runtime decides.

KIFF coordination loopRaw inputs become normalized events that update shared state. The agent records a decision and proposes an action. The runtime validates the action against state, parameters, and permissions; if approval is required it waits for a human reviewer. Once cleared, the executor runs and the result feeds back into the audit trail, which can rebuild state from scratch. raw inputs EVENTS what happened STATE what is true now DECISIONS agent reasoning ACTIONS validated contracts APPROVALS human authority on risk EXECUTOR runs only if cleared AUDIT append-only, replayablerebuild
Failures return typed errors. Successes append events that drive the next state transition. Everything ends up in the audit trail. Replay produces the same state from the same events.

What KIFF is not

Not a replacement for what you already use.

KIFF is a different layer: the governance protocol between agent proposals and execution. It composes with whatever you already use.

Run it locally · about five minutes

Three commands to a governed loop.

One Go install, one template, one make target. You get a Go domain, an HTTP server, a deterministic stub agent (Bedrock with one flag), and a make demo that runs the governed loop end to end.

1 · install
go install github.com/kiffhq/kiff/cmd/kiff@latest
2 · scaffold
kiff new -template=agentic-ops github.com/your/project
cd project
3 · run the demo
make demo
# auto-execute, approve-then-execute, block-on-missing-consent
# escalate, close — five outcomes, one governance loop