sirdocalot — setup ================== Structured briefs you hand to your operator, and the answers back. You ask a question as a schema; a person answers it on a web page; you read the result. You need an invite code. This instance runs on one small machine, so registration is gated -- if you do not have one, ask whoever pointed you here. Running this yourself instead? Then you are the operator and the gate is self-service. Clone https://github.com/gdoteof/sirdocalot, run "just up", then "just enrol", and skip the rest of this page -- that script does all of it. Authentication is a keypair you generate. This service receives a public key and never a private one, so there is no shared secret to leak and nothing here to reissue if you lose yours. 1. Make a key ------------- mkdir -p ~/.sirdocalot && chmod 700 ~/.sirdocalot openssl genpkey -algorithm ed25519 -out ~/.sirdocalot/key.pem chmod 600 ~/.sirdocalot/key.pem 2. Register ----------- PUB=$(openssl pkey -in ~/.sirdocalot/key.pem -pubout -outform DER | tail -c 32 | openssl base64 -A) curl -sS -X POST https://sirdocalot.vteng.io/api/agents \ -H 'content-type: application/json' \ -d "{\"name\":\"YOUR NAME HERE\",\"publicKey\":\"$PUB\",\"inviteCode\":\"YOUR CODE HERE\"}" Save the agentId it returns: echo "AGENT_ID_FROM_ABOVE" > ~/.sirdocalot/agent 3. Wire it into your tools -------------------------- The MCP server is the shortest path -- your model calls tools rather than remembering curl invocations. Read it first; it is one file with no dependencies and you should not run code you have not looked at: curl -sS https://sirdocalot.vteng.io/mcp -o ~/.sirdocalot/mcp-server.mjs claude mcp add --transport stdio sirdocalot --scope user \ -- node ~/.sirdocalot/mcp-server.mjs It reads the key and agent id you just saved, and signs every request with them. The copy you just downloaded already points at this instance, so there is nothing else to configure -- set SIRDOCALOT_URL only if you move the file to talk to a different one. Then start a new session and you have: list_widgets, create_brief, await_responses, read_responses, close_brief and get_artifact. 4. Or drive it with curl ------------------------ If you would rather not use MCP, every request can be signed by hand. This helper does it for you: curl -sS https://sirdocalot.vteng.io/cli -o ~/.sirdocalot/sdl && chmod +x ~/.sirdocalot/sdl 5. Check it works ----------------- ~/.sirdocalot/sdl GET /api/widgets That lists what you can build a brief from without inventing anything. Read it before writing a brief; most of what you want is already there. Asking something ---------------- ~/.sirdocalot/sdl POST /api/briefs brief.json Returns one link per participant. Send the links, then either wait: ~/.sirdocalot/sdl GET /api/briefs//await?timeout_ms=300000 or exit and let whatever orchestrates you pick the answers up later by id. Both work; the service never resumes you, it just holds the answers. A minimal brief.json: { "title": "Which database for the new service?", "intent": { "purpose": "Pick a datastore before I start building" }, "participants": [{ "name": "Sam" }], "policy": { "closeWhen": { "kind": "all" }, "visibility": "blind" }, "blocks": [ { "widget": "summary", "props": { "title": "Context", "lead": "Two options, and I do not have enough context to choose.", "points": ["Postgres: we run it already", "SQLite: simpler, single node only"] } }, { "widget": "pick-one", "props": { "title": "Your call", "question": "Which one?", "options": [ { "value": "postgres", "label": "Postgres" }, { "value": "sqlite", "label": "SQLite" } ] } } ] } Things worth knowing -------------------- * The widget library is fixed. list_widgets is the whole vocabulary, and there is no way to add to it from here — a new widget is a pull request against the service. If none of them fit, a "raw" block renders your own HTML in a sandboxed frame; needing that twice is the argument for the pull request. * Disagreement is reported, never resolved. If two people answer differently you get both, and the field is named in "conflicts". Deciding is your job. * intent.purpose is required. Answers that arrive with no record of what they were for are useless to whoever picks them up after you are gone. * You only ever see your own briefs.