Verifiable handoffs between agents.
Padlock is an append-only, hash-chained locker for passing work between agents. One agent writes a structured handoff, seals the pad, and hands off a read ticket. Another agent reads it back and verifies the chain end-to-end before the content ever reaches a model's context.
What Padlock does
Padlock gives two agents a shared, ordered record of a handoff — and a way for the receiver to check that nothing in that record was altered after it was written.
-
01 · WRITE
Write the handoff
The sending agent appends blocks to a pad. Block 0 is a strict
locker.handoff.v1envelope: task id, from/to agents, constraints, artifacts, budget. -
02 · SEAL
Seal the pad
Sealing freezes the pad and revokes further writes. The pad's
head_hash— the hash of its latest block — becomes fixed. -
03 · HANDOFF
Pass a read ticket
The receiver gets a scoped read ticket, not the write key. A
read_onceticket opens a short read lease; aread_unlimitedticket can be minted for a durable audit. -
04 · VERIFY
Verify the chain
The client walks the chain from the genesis hash to
head_hash. If every link matches, the record is internally consistent — the receiver knows the blocks it is about to use are the blocks that were written.
An example handoff
Each block carries prev_hash and curr_hash,
so verification is self-contained: a reader needs only the block list.
locker.handoff.v1
prev_hash 00000000…00000000 (genesis) · curr_hash 9c1f4a…e7b2
{
"schema": "locker.handoff.v1",
"task_id": "refactor-auth-42",
"from_agent": "planner",
"to_agent": "worker",
"constraints": ["no schema changes", "tests must stay green"],
"artifacts": [{ "name": "plan.md", "size": 2048 }],
"budget_usd": 1.25
}
{
"task_id": "refactor-auth-42",
"from_agent": "worker",
"to_agent": "reviewer",
"status": "completed",
"canonical_ref": { "repo": "owner/name", "branch": "main", "commit_sha": "…" },
"verification": { "test_command": "pytest -q", "exit_code": 0, "tests_passed": 12, "tests_failed": 0 },
"environment": { "runtime": "python 3.11.8", "dependency_manifest": "requirements.txt", "dependency_hash": "sha256:…" }
}
Block 0 is schema-validated and rejected unless every field is present and correctly typed. Block 1 and later blocks are free-form — the daemon does not validate them. The attestation above is a recommended shape, not an enforced one.
Install and configure
Padlock has two parts. lockermcp is the MCP client your agent
talks to; lockerd is the daemon that stores pads. The client
is published on PyPI; the daemon you run yourself. Each is a normal Python package.
Three ways to use Padlock — and what each one actually is
1 · Self-host the published package Recommended
The full API on infrastructure you control, including
POST /v1/pads/<id>/tickets. This is the path for real work:
your pads never leave your machine unless you expose the daemon.
2 · Inspect the public read-only demo
A seeded, sealed pad served from the hosted instance. No account, no writes, no ticket — for looking at a real hash chain and the response shape before installing anything.
3 · The hosted API — read-only
A sample instance, not a service. Three paths are public — health, the demo pad's
blocks and its manifest — and only GET/HEAD.
Every other path and method returns 403 at its edge, writes included;
the API schema and other pads are not public. It reports version
0.1.0, has no /tickets endpoint, and
carries no support commitment. Use it to look, not to work.
1 · The MCP client (published package)
uvx lockermcp runs the client straight from PyPI without installing it.
This is the published client command — it is not a standalone hosted service.
uvx lockermcp
2 · Register it with an MCP client
The client's only required configuration is LOCKER_URL — the base URL of
the daemon it should talk to.
{
"mcpServers": {
"lockermcp": {
"command": "uvx",
"args": ["lockermcp"],
"env": { "LOCKER_URL": "http://127.0.0.1:8000" }
}
}
}
3 · Run your own daemon
The daemon binds 127.0.0.1:8000 by default and stores pads in a single
SQLite file. Use the same variable, LOCKER_URL, to point the client at it.
uvx --from lockermcp lockerd
# or, in a virtualenv:
pip install lockermcp
LOCKER_DB_PATH=locker.db LOCKER_MODE=open lockerd
Six tools are exposed over stdio: locker_deposit (create + append + seal
in one call), locker_create, locker_append,
locker_seal, locker_manifest, and
locker_read_blocks. Reads verify the chain in Python and return an
integrity result — the model never computes a hash itself.
api.padlockspace.org is read-only: exactly three paths
are public (/health, the demo pad's
blocks and manifest), only
GET/HEAD, and everything else is
403 — so a client pointed at it cannot deposit a handoff. It reports
version 0.1.0 and has no
POST /v1/pads/<id>/tickets endpoint — both of which the published
package provides. The discovery configuration on this site therefore defaults
LOCKER_URL to your own daemon,
http://127.0.0.1:8000.
201 and still not persist: GET /health ran its own
transaction on the same database connection the append was using, and that rollback discarded the
in-flight block while the append went on to commit the pad's head and byte count. Health no longer
touches transaction state, every route rolls back on any failure or cancellation, creating a pad and
its first read ticket is one transaction, and there is a 4,096 byte limit on JSON request
bodies for pad creation and ticket minting, enforced before parsing. Upgrading stops the loss; it
does not restore writes already lost. The hosted demo is
unaffected — it is read-only and accepts no writes.
Practical uses
Padlock is a primitive, not a platform. It fits wherever one automated step hands work to another and the receiver benefits from a checkable record.
Multi-agent coding pipelines
A planner hands a scoped task to a worker; a reviewer receives the change and its test result as one sealed record.
Review and approval gates
Use allowed_paths to declare the permitted file scope, so a reviewer
can flag any change that lands outside it.
Tamper-evident audit trails
Seal a pad and keep its head_hash yourself. Any later rewrite of the
pad no longer matches the head you retained.
Reproducible attestations
Peg a run to an exact dependency snapshot with environment.dependency_hash
so an independent re-run reproduces the result instead of whatever upstream installs today.
Padlock does not claim integrations it doesn't have. It speaks MCP over stdio, and the repository README documents how to register it with an MCP client.
Integrity guarantees — and their limits
The chain is a precise tool with a precise scope. It is worth stating both halves plainly.
Establishes What the chain proves
- Internal consistency. Each block's hash is bound to its predecessor, so a
single mutated payload breaks verification:
locker_read_blocksandlocker_manifestreturn averification_failederror and no payloads at all. The check runs in the client before blocks enter model context. - Change detection against a trusted head. If a reader retains a pad's
head_hashindependently, it can detect any later rewrite of that pad.
Does not What the chain can't prove
- It does not authenticate the writer.
from_agent/to_agentare client-supplied strings. Holding the write key proves possession of the key, not identity. - It does not make an attestation true. Block 1 is free-form and unvalidated:
exit_code,tests_passedandcommit_shaare claims the writer makes about itself. - It does not defend against a malicious operator. Whoever runs the daemon can rewrite both the chain and the head it serves. It is not a substitute for decentralized consensus against a hostile host.
Inspect a live pad
The hosted daemon seeds a permanent read-only pad, so you can look at a real hash chain without creating anything or paying. It is read-only: writing to it is refused.
curl https://api.padlockspace.org/v1/pads/demo-pad-v1/blocks
Or open the demo endpoint directly. This page does not fetch it in your browser — you can read the raw JSON yourself.
HTTP 200 with three blocks, and the instance accepts only
GET/HEAD — writes are refused. It is an older
daemon build than the published package (version 0.1.0, no
/tickets endpoint), so treat it as a sample of the data shape and of a live
chain — not as a feature-complete service.
Discuss a paid pilot
Padlock ships as a self-hosted primitive. If you want it wired into a handoff you already run, I take on a small number of fixed-scope integration pilots.
What a pilot covers
- Integrate Padlock into one existing agent-to-agent handoff.
- Configure the agreed deployment and verification workflow.
- Provide operating instructions and a defined support window.
- Agree scope, acceptance criteria, schedule, and a fixed price before work begins.
What “done” looks like
An acceptance test adapted to your setup, for example: one agent seals a handoff, another retrieves and verifies it, altered payloads fail verification, and your team can repeat the workflow independently.
Scope, acceptance criteria, schedule, and a fixed price are agreed in writing before any work starts. No outcome is promised beyond the criteria we set together.
Start a conversation
Email mccullough.sheldon@gmail.com with the subject “Padlock pilot inquiry”.
Address (selectable): mccullough.sheldon@gmail.com mccullough.sheldon@gmail.com
Tell me which agent tools you use, how you hand work off today, and what breaks. That is usually enough for me to say whether a pilot is a fit.