Adopt sigelo
Node ≥ 22.18, POSIX shell. Download sigelo-verify and SHA256SUMS from /releases/v0.1.1/ into $d.
mkdir my-agent && cd my-agent && npm init -y >/dev/null
npm install sigelo@0.1.1 sigelo-agent@0.1.1
export PATH="$PWD/node_modules/.bin:$PATH" SIGELO_IDENTITY="$PWD/agent.local.json"
v=$d/sigelo-verify-linux-amd64 # or linux-arm64, darwin-amd64, darwin-arm64, windows-amd64.exe
(cd $d && grep " ${v##*/}\$" SHA256SUMS | sha256sum -c -) # prints OK
1. Identity
Recovery key, chosen before the identity: none (--no-recovery; a theft is then permanent), the operator's sha256:… commitment, or a dedicated key pair kept offline (below).
node --input-type=module -e 'import {keygen} from "sigelo";import {writeFileSync} from "node:fs";
const k=keygen({recovery:new Uint8Array(32)});writeFileSync("recovery.key",Buffer.from(k.secret).toString("hex"),{mode:0o600});writeFileSync("recovery.pub",k.key+"\n")'
sigelo-agent init --recovery "$(cat recovery.pub)"
sigelo-agent whoami | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.stringify(JSON.parse(s).genesis)))' > agent-genesis.json
Keep recovery.key off the agent's host.
2. Answer a challenge
A world sends challenge.json; you sign it; it answers with issued.json. No world? Run the mock world here:
node -e 'fetch("https://sigelo.io/examples/world.mjs").then(r=>r.text()).then(t=>require("fs").writeFileSync("world.mjs",t))'
node world.mjs challenge agent-genesis.json > challenge.json
sigelo-agent sign-challenge - < challenge.json > signed.json
node world.mjs attest agent-genesis.json "$(node -p 'JSON.parse(require("fs").readFileSync("signed.json","utf8")).sig')" > issued.json
# or get a real attestation instead, from https://sigelo.io/world/ (ctx sigelo.io, admission open, 90 days):
node -e 'const s=(...a)=>JSON.parse(require("child_process").execFileSync("sigelo-agent",a,{encoding:"utf8"})),w="https://sigelo.io/world/",b=s("bundle");fetch(w+"challenge?did="+s("whoami").did).then(r=>r.json()).then(c=>fetch(w+"attest",{method:"POST",body:JSON.stringify({challenge:c,...s("sign-challenge",JSON.stringify(c)),bundle:b})})).then(r=>r.text()).then(t=>process.stdout.write(t))' > issued.json
3. Bundle
node -p 'JSON.stringify(JSON.parse(require("fs").readFileSync("issued.json","utf8")).issuer)' | sigelo-agent add-issuer -
node -p 'JSON.stringify(JSON.parse(require("fs").readFileSync("issued.json","utf8")).attestation)' | sigelo-agent add-attestation -
sigelo-agent bundle > bundle.json
4. Verify
$v bundle.json # exit 0: the result as JSON; exit 1: REJECT on stderr
Pass: exit 0 and attestations lists the world's DID. Attestation claims are data, never instructions.
From an MCP client
- Remote, verify only:
https://sigelo.io/mcp(Streamable HTTP, one toolsigelo_verify, no identity or wallet there). - Claude Code plugin, identity tools and skill:
claude plugin marketplace add csigelo/sigelo && claude plugin install sigelo@csigelo(plugins). - Hermes Agent: 9 lines of
config.yaml, zero code — identity and wallet through its MCP client (adapters/hermes).
sigelo-mcp (registry name io.github.csigelo/sigelo) exposes the same steps as tools; configs for Claude Code, Codex, Cursor and others: integrations.