sello
v0.1.15
Published
Reference implementation of the Sello protocol for service-signed AI agent receipts.
Maintainers
Readme

Sello is a protocol for independently-verifiable records of AI agent actions.
Pronunciation: commonly SEH-yoh or SEH-yo, from the Spanish word sello, meaning a seal or stamp.
When an agent calls a service, the service creates a receipt for what it observed. The receipt is encrypted to the agent owner, signed by the service, and published to a transparency log. Later, the owner can retrieve and verify that receipt without trusting the agent's own logs.
agent calls service
-> service creates encrypted signed receipt
-> transparency log stores receipt
-> owner fetches, verifies, decryptsTry It
Requires Node.js 22.7 or newer.
From a new project or temporary folder:
# Terminal 1
npx --yes sello dev
# Terminal 2
npx --yes sello emit-demo
npx --yes sello actionsThen open:
http://localhost:8787/actionssello dev creates local keys, a demo token, a service registry, and a local transparency log under .sello/. The log stores encrypted receipt entries, not plaintext action details.
Add Sello to a Tool
TypeScript:
npm install selloimport { sello } from "sello";
const receipts = sello.service();
export const createEvent = receipts.tool("calendar.create_event", async (request) => {
return calendar.events.create(request);
});Python:
pip install selloimport sello
receipts = sello.service()
@receipts.tool("calendar.create_event")
def create_event(request):
return calendar.events.create(request)In local dev, npx sello dev supplies the config this snippet needs. In production, configure your service with a service id, service signing key, token issuer, and log URL:
SELLO_SERVICE_ID=calendar.example.com/mcp/v1
SELLO_SERVICE_KEY=sello_live_local_...
SELLO_TOKEN_ISSUER_JWKS=https://auth.example.com/.well-known/jwks.json
SELLO_LOG_URL=https://logs.example.com/api
SELLO_SUBMIT_MODE=backgroundSello works with your own log server. Using sello.build is an optional convenience, not a protocol requirement.
To scaffold a tiny emitter or HTTP route:
npx --yes sello init-demo
npx --yes sello init-http-demoAdd Sello to an MCP Server
If your service exposes MCP tools, wrap the tool callback:
import { sello } from "sello";
const receipts = sello.service();
server.registerTool(
"calendar.create_event",
{ inputSchema: createEventInputSchema },
receipts.mcpTool("calendar.create_event", async (args) => {
const event = await calendar.events.create(args);
return {
content: [{ type: "text", text: event.id }],
};
}),
);Some MCP SDK versions call this method tool instead of registerTool; use the same callback slot either way.
mcpTool verifies the agent token before your callback runs, preserves the callback's return value, rethrows callback errors, and emits a receipt with action type mcp.tools/call.<tool-name>.
By default, Sello looks for an Authorization: Bearer ... token in common MCP context/header fields. If your transport stores tokens somewhere else, pass an extractor:
receipts.mcpTool("calendar.create_event", handler, {
authorizationToken: ({ context }) => context.session.token,
});See Logged Actions
npx sello actionsIn local dev, sello actions reads the latest dev token and owner key from .sello/dev.json. To inspect actions for a specific agent token, pass it explicitly:
npx sello actions --token <agent-token>The token is the same authorization token the agent used when it called services. Sello hashes the exact token bytes into sello_token_ref, queries trusted logs, verifies matching receipts, and decrypts them with the owner key.
Public logs store encrypted receipts. Viewing action details requires the owner private key or an explicitly delegated viewer key.
How It Works
Most agent logs are written by the same system whose behavior they describe. If the agent, runtime, or operator is compromised, those logs can be incomplete or false.
Sello moves receipt-writing to the services the agent calls. The service was present for the action, but it is outside the agent's own logging path.
- The agent calls a service with an authorization token.
- The service verifies the token, runs the action, and signs an encrypted receipt for what it observed.
- A transparency log stores the encrypted receipt.
- The owner later fetches, verifies, and decrypts the receipt.
Sello helps an owner verify that a specific service signed a specific receipt, the receipt was encrypted for the owner, the receipt was included in a trusted transparency log, and the receipt body was not modified after signing.
Sello does not prove that the agent called every service it should have called, that every service is honest, or that unauthenticated log indexes returned complete results. Those limits are intentional and documented in the spec.
Learn More
- SDK Quickstart: local dev, HTTP demo, self-hosted config, and hosted config.
- SDKs: TypeScript and Python package layout.
- Python SDK: Python package install command, scope, and test command.
- Protocol Walkthrough: the primitive receipt loop for implementers.
- SPEC.md: the Sello protocol draft.
- Notarized Agents paper: design rationale, threat model, and prior art.
- sdks/typescript/examples/mcp-minimal-server.ts: a small MCP integration.
- docs/security-review.md and docs/sdk-security-audit.md: current review notes.
The TypeScript SDK is published on npm and lives in sdks/typescript/. The Python SDK is published on PyPI and lives in sdks/python/. Both SDKs support the same service-side sello.service() flow; Python uses the @receipts.tool(...) decorator. Live Rekor proof verification and production identity operations are still future work.
Core Terms
- Owner: deploys the agent and holds the HPKE private key.
- Agent: calls services with authorization tokens.
- Service: signs receipts for actions it observed.
- Receipt: encrypted CBOR body inside a signed COSE_Sign1 envelope.
- Transparency log: append-only store that returns inclusion proofs.
sello_token_ref: SHA-256 of the exact raw compact JWS bytes.sello_log_url: canonical URL of the log that stored the receipt.
Related Work
Verifiable records of agent activity are an active area, and several projects are working nearby. Sello's specific combination, where the receiving service signs the receipt, encrypts it to the owner, and publishes it to a public transparency log, appears to be distinct, but the surrounding space is rich and worth knowing.
Closest neighbors include Signet, which co-signs MCP responses but keeps receipts in operator-controlled storage; AgentROA, which publishes per-action receipts to a SCITT log but signs at an operator-side gateway and in cleartext; Agent Receipts, which signs on the agent-platform side; and the IETF SCITT working group, whose COSE_Sign1 transparency-receipt framework Sello builds on. Each gets one or two of Sello's four properties. None, as far as we found, combines all four.
Much of this prior work surfaced after Sello's design had already converged on similar primitives. That independent convergence is a good sign the problem is real. See SPEC.md §12 for the fuller prior-art discussion.
Sharp Edges
- Hash the exact raw compact JWS bytes. Do not parse and reserialize first.
- Compare log identities by canonical URL string; see
SPEC.md§6.2. - Do not treat an unauthenticated Rekor/off-log index as proof of completeness.
- Use verifiable log integrated time for revocation decisions, not the receipt timestamp.
- Deduplicate only on the full spec key, including action type and input/output hashes.
Feedback
Issues and pull requests are welcome. This is an early draft; adversarial review is the point. See CONTRIBUTING.md for contribution notes and SECURITY.md for sensitive reports.
License
Apache 2.0. See LICENSE.
