SCRIPTMASTERLABS · AGENT INFRASTRUCTURE · SEP 26, 2026

SPD: The Permit Layer for AI Agents — Send / Pay / Delete (v1)

Jev decides. SPD permits. Receipt proves the source said it. Chat is out of scope.

A brake in front of irreversible actions. Before an agent sends a message, pays money, or deletes data, it must pass this gate. The gate returns allow | stop | human in under half a second — and logs everything: the question, the options, the scores, the state hash, the decision.

HONESTY BOX — READ THIS FIRST

What v1 is: a local, zero-dependency Node module. decide() runs the gate, receiptCheck() runs the evidence check, ledger.mjs appends every decision to JSONL. 9/9 gate test scenarios pass; 7/7 MCP server checks pass. Latency is asserted under 500ms in tests. Public on GitLab.

What v1 is not: a model. SPD never calls a model directly — it calls a scorer interface, and the shipped scorer is a heuristic (receipt coverage × action-risk rules), not intelligence. Receipt is keyword overlap, not semantics: it is a tripwire, not a proof.

What nobody should claim: there are no users, no revenue, no production deployments. Nothing below has been battle-tested against a real agent stack yet. The weaknesses are documented in the open, in the code comments and the limits section.

The three verbs

verbriskgate behavior
sendmessages a humanunsupported claim → human
payspends moneyunsupported claim → stop
deletedestroys dataalways ≥ human, even when the claim is supported — irreversible

How the gate decides

Hard rules first, scorer only in the ambiguous middle — explicit and boring on purpose:

  1. No sources → unverifiable. pay/delete: stop. send: human.
  2. Claim unsupported by sources → pay/delete: stop. send: human.
  3. Supported but delete → human. Irreversible is never auto-allowed.
  4. Supported + reversible → the scorer decides: top option allow with confidence ≥ 0.6 → allow, else human.

Receipt — the first wedge

receiptCheck(claim, sources) answers one question: is this claim evidenced by the provided sources? It returns { supported, coverage, missing } — the fraction of the claim's key terms found anywhere across the sources.

RECEIPT'S HONEST LIMITS (NON-NEGOTIABLE)

Catches: an agent asserting a claim that appears nowhere in the sources it was given — the classic "lying about evidence in context" failure. Claims made with zero sources at all.

Does not catch: invented world-facts with no source to check against ("unsupported" means not evidenced here, never false). Claims that ARE in the sources when the sources themselves are wrong, planted, or attacker-controlled — whoever controls the sources controls the verdict. Paraphrase and synonyms (keyword overlap, not semantics — false stops are the price). Negation ("not" is a stopword, so "authorized" and "NOT authorized" look the same).

In production this heuristic is a tripwire, not a proof. Pair it with a real decision model and treat every stop as "unverified", never "disproven."

The scorer — plug in a real model

The gate never calls a model directly. It calls a scorer:

scorer.score({ question, options, context })
// → { scores: { allow: 0.7, human: 0.25, stop: 0.05 }, confidence: 0.8 }

context carries { action, coverage, stateHash }. A Jev-style decision model (typed choices and scores, no text) plugs in as a thin adapter — see scorer.mjs. The shipped default is the heuristic; swap it without touching gate.mjs.

MCP — use it from any agent stack

mcp-server.mjs is a stdio MCP server (zero dependencies) exposing two tools:

// MCP client config
{
  "mcpServers": {
    "spd-gate": { "command": "node", "args": ["/path/to/spd-gate/mcp-server.mjs"] }
  }
}

The general pattern

SPD is the general form of a pattern ScriptMasterLabs already runs in one domain: decision-gated machine payments — a decision model's confidence gating whether an x402 machine payment fires. That page is the payments instance; this page is the general pattern. The gate is the product, not the model: Jev, Laya, whatever comes next plugs into the same gate.

The first integration target is GatePay: SPD as the permit layer in front of its x402 payment flow, verifying payment claims against provided sources before money moves.

Code, public: gitlab.com/timothy.walton45/spd-gate — gate.mjs, receipt.mjs, scorer.mjs, ledger.mjs, mcp-server.mjs, tests. Zero dependencies, no network calls, no credentials.

FAQ

What does SPD stand for?

Send / Pay / Delete — the three irreversible verbs an AI agent can perform. SPD is the gate that must permit each one before it happens.

Is SPD a model?

No. SPD never calls a model directly. It calls a scorer interface; v1 ships with a heuristic scorer (receipt coverage × action-risk rules). A real decision model — e.g. a Jev-style typed decision model — plugs in as an adapter without touching the gate.

What does Receipt actually verify?

Whether the claim behind an action is evidenced by the sources the agent was given. It catches an agent asserting something that appears nowhere in its sources. It does not verify world-facts, does not detect wrong or planted sources, and does not understand paraphrase or negation — v1 is keyword overlap, a tripwire, not a proof.

What happens when the gate can't verify a claim?

Explicit rules: no sources or unsupported claim → stop for pay/delete, human for send. Delete always escalates to human even when supported, because it's irreversible. The scorer only decides the ambiguous middle: supported + reversible + confidence ≥ 0.6 → allow, else human.

Who is SPD for?

Agent developers and agent-stack builders first — anyone whose agents send messages, spend money, or delete data. The first integration target is GatePay, ScriptMasterLabs' x402 payment flow, where SPD acts as the permit layer in front of machine payments.

Where's the code?

Public on GitLab: timothy.walton45/spd-gate — gate.mjs, receipt.mjs, scorer.mjs, ledger.mjs, the stdio MCP server (mcp-server.mjs), and tests (9/9 gate scenarios + 7 MCP checks passing).

JEV DECIDES. SPD PERMITS. RECEIPT PROVES THE SOURCE SAID IT.