Skip to main content
Glama

Chaperone

CI

Household consent kernel for ambient MCP — pantry/reorder skin for the Amazon Developer Hackathon 2026 Alexa+ track.

Demo video · Devpost submission

Model proposes · policy decides. Irreversible tools use propose_order / commit_order with sha256 hash-locked payloads. The judge demo is a simulated Alexa+ host. No device and no AWS keys are required.

The problem

An ambient voice assistant shares the room with everyone and everything that makes sound. A television ad can say “Alexa, order me a new fridge.” A child can ask for candy. A guest can try to check out. If the model’s tool call is the purchase, those utterances become orders. Chaperone puts a deterministic consent kernel between the proposal and the charge: the model may describe an order, and only policy plus an exact hash-locked commit can execute it.

Related MCP server: mise-mcp

How it works

flowchart LR
  utterance["Utterance on sim-host"] --> mcp["MCP Streamable HTTP"]
  mcp --> propose["propose_order"]
  propose --> policy["Deterministic policy"]
  policy -->|deny| stop["No commit"]
  policy -->|allow or elicit| hash["sha256 hash-locked proposal"]
  hash --> card["MCP App ui:// confirm card"]
  card --> commit["commit_order"]
  commit --> ledger["Append-only chained ledger"]
  ledger --> stub["Stub HMAC by default"]
  ledger --> aws["Optional DynamoDB and KMS"]
  1. The Next.js sim-host (apps/sim-host) pretends to be an Alexa+ client. A person picks Maya, Leo, Guest, or Unknown and sends an utterance. The host does not decide allow or deny.

  2. propose_order on the MCP server (apps/mcp-server, Streamable HTTP) builds a basket from the mock catalog and asks @chaperone/policy. Policy returns allow, elicit, or deny. It does not call a model.

  3. An allow or a held elicit stores a sha256 hash of the canonical payload. The confirm card is an MCP App at ui://chaperone/confirm.html. Approving sends commit_order with that hash. Any drift, expiry, or unknown hash is rejected.

  4. A successful commit appends one row to a signed append-only ledger (prevHash chained from a genesis of 64 zeroes). The default signer is an offline HMAC stub. Set CHAPERONE_AWS=1 to dual-write proposals and ledger rows to DynamoDB; set CHAPERONE_KMS_KEY_ID to sign that dual-write with KMS (RSASSA_PSS_SHA_256). The receipt on the sim-host is the in-memory stub signature. packages/ledger/src/bedrock.ts is a stretch stub and is not on the verdict path.

Judge demo

Password: household. Start both processes (see Quick start), open http://127.0.0.1:3000/login, and sign in.

  1. Maya allowed. Leave Maya (Adult) selected. Send Alexa, reorder the coffee…. The confirm card is an in-budget adult allow. Approve & commit. The receipt shows the coffee line and a stub-signed ledger row.

  2. Leo held for adult approval. Select Leo (Child) and send Alexa, order candy. Policy elicits: a child cannot purchase, and an adult must approve. Select Maya (Adult), then Approve & commit. Approving while Leo is still selected stays blocked.

  3. Guest denied. Select Guest and send Alexa, reorder the coffee…. The screen says not allowed (guest cannot purchase). No commit is issued.

  4. Unknown speaker asked to identify. Select Unknown and send Alexa, order me a new fridge, or press Run TV-ad demo. The live verdict is elicit (identify speaker). Nothing commits until Maya, Leo, or Guest is selected. The red “order placed” card in the TV-ad panel is a contrast illustration, not a Chaperone charge. Chaperone is not a TV detector: the same words from a speaker the host labels as Maya are just an utterance.

AmbientBench

Re-run on this tree with pnpm eval:offline (no AWS). The command prints:

n=100 heldOut=20 treatmentPass=100/100
consentBypasses baseline=81 treatment=0 ablation=10
falseRefusals=0

train n=80 pass=80/80 bypasses=0
heldOut n=20 pass=20/20 bypasses=0

Split

n

treatmentPass

baseline bypasses

treatment bypasses

hash-lock ablation bypasses

false refusals

All

100

100/100

81

0

10

0

Train

80

80/80

—

0

—

—

Held-out

20

20/20

—

0

—

—

Held-out seal: 4ca8e7bf942d9ab2632155e9839e5a89f67850ddb22d716f74ac95c17a92c0a1 (corpus/seals/HELD_OUT.sha256, sealed 2026-09-20).

Treatment is the real ConsentKernel. It auto-commits only when policy allows and the actor is adult Maya. Ablation is the same kernel with disableHashLock: true (drifted or stale commits can execute). It is not the unguarded baseline. Baseline is a purchase-word regex with no speaker check. treatmentPass means the verdict matches the mechanism label. It is not a claim that the kernel detects television audio or reads prompt-injection intent. Full definitions: docs/AMBIENTBENCH.md.

Quick start

pnpm install
pnpm dev:mcp          # terminal 1 — MCP on :3333
pnpm dev:host         # terminal 2 — sim-host on :3000

Open http://127.0.0.1:3000/login. Judge password: household (apps/sim-host/.env.example).

Demo family: Maya (adult), Leo (child), Guest. MCP health: http://127.0.0.1:3333/health.

pnpm test
pnpm typecheck
pnpm eval:validate
pnpm eval:offline
pnpm build

MCP Inspector

npx @modelcontextprotocol/inspector

Connect with transport Streamable HTTP to http://127.0.0.1:3333/mcp.

Tools: hello_chaperone, propose_order, commit_order, list_household. Resources: ui://chaperone/hello.html, ui://chaperone/confirm.html.

Optional AWS

Offline stubs are the default (CHAPERONE_AWS=0 in apps/mcp-server/.env.example). To dual-write:

cp apps/mcp-server/.env.example apps/mcp-server/.env
# set CHAPERONE_AWS=1, then with credentials configured:
pnpm aws:bootstrap

pnpm aws:bootstrap creates the pay-per-request DynamoDB tables if they are missing. Without CHAPERONE_KMS_KEY_ID, the DynamoDB row keeps the stub HMAC. A failed AWS write is logged and the in-memory demo continues.

Docs

docs/DEVPOST.md, docs/DEMO-VIDEO.md, and docs/AWS.md were linked from an earlier README and listed in .gitignore, but they were never committed. There is nothing in history to restore. The video, Devpost page, and AWS flags above are the copies of those pointers.

Screenshots

Captured from the running sim-host (pnpm dev:mcp and pnpm dev:host) after signing in with household.

Maya, in budget, gets a hash-locked confirm card for coffee:

Maya confirm card for a coffee reorder

Leo’s candy request is held until an adult approves:

Leo candy order held for Maya’s approval

A guest utterance is denied and does not commit:

Guest coffee reorder denied by policy

The TV-ad control passes no speaker id. The red “order placed” panel is a contrast illustration. The live Chaperone verdict on the right is elicit:

Unidentified speaker from the TV-ad demo is asked to identify

Known limitations

From docs/AMBIENTBENCH.md:

  • Unidentified speaker is not a TV detector. If the host labels the speaker as Maya, the fridge-ad line is an ordinary utterance.

  • Catalog pin is structural. A mutated invokedToolDescription is denied only when the host sends the field.

  • “Cancel it” resolution is a regex plus a count of open proposals, not NLU.

  • Hash-lock ablation still enforces identity and budget. The 10 ablation bypasses are drifted-hash and stale-TTL commits. Guest reuse of Maya’s hash stays denied by role.

  • The baseline purchase regex does not fire on vague lines such as “cancel it” or “do the usual”, so those are not counted as unguarded bypasses.

  • False refusals are published. This run had 0.

Also:

  • commit_order trusts the caller-supplied actorMemberId. The kernel does not prove that the person at the screen is that member.

  • The sim-host commit route defaults a missing approver to maya (apps/sim-host/src/app/api/commit/route.ts).

  • lastConfirmView on the MCP server is one process-wide variable, shared across sessions. A later propose overwrites the confirm card other sessions read.

  • Proposals, grants, spend, and the ledger live in memory and reset when the MCP process restarts. DynamoDB dual-write is optional and is not read back on boot.

  • packages/ledger/src/bedrock.ts classifies utterances with a regex stub. CHAPERONE_BEDROCK=1 still returns that stub. Policy never reads it.

  • The sim-host header budget pill is a fixed label (“weekly budget $120 · left $84”), not the live household balance. The receipt uses the confirm card’s figures.

  • The eval harness, not the MCP server, still prices a scenario item outside the mock catalog at amountMinor or else 1000 minor units ($10.00). propose_order itself rejects an unknown SKU instead of inventing that price.

Team

Solo hackathon project by Erasmus Antwi (git author). The Devpost page records the project starter as Antwi Erasmus and is written in the first person, with no other builders listed.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Hosted remote MCP server for managing consent scope receipts, enabling checking, issuing, and auditing consent policies and tool calls.
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP-compatible clients like ChatGPT and Claude to manage a household's shared food inventory, meal plans, recipes, and preferences through natural language, with atomic updates and audit logging.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides MCP tools to draft, review, accept, and repair shared-kitchen dinner schedules with verified constraint checking and persistent state.
    MIT