A replicable template for a data tank: a durable, append-only, encrypted-at-rest log that Tell feeds with digests of what has happened since its last delivery. Fork it, deploy it (a public repo is the intended default), and only you can read what it holds — until you choose to prove it to everyone.
- What & why:
CONSTITUTION.md(the law) and the why-shaped map inAGENTS.md. - The interface with Tell:
CONTRACT.md— the crypto model, the feed-branch protocol, and the handshake. - Where it's going:
ROADMAP.md. What's deferred for the whole constellation lives in one place, the workspace'sOPEN-QUESTIONS.md. - The whole poll lifecycle, step by step — where this template fits, from copying it through the
owner decoding the answers:
civic-node/docs/PIPELINE.md.
Tell encrypts each digest to your committed age public key, hash-links it into a signed
manifest, and publishes it on its own domain. Your repo pulls it, verifies it, and stores it
— Tell never reaches into your repo. You can decrypt it (you hold the key); the public cannot. If
you ever want to go public, you publish a ratchet checkpoint and anyone can decrypt the committed
blocks and confirm they match the signed record — proving the data is real and unaltered.
Tell ──(age-encrypt + sign + hash-link)──▶ /piles/<id>/feed/* (Tell's own domain)
│ your repo PULLS (no credential)
bin/ingest → bin/verify → your feed/tell branch + state/
│
bin/decrypt (owner) · bin/report · bin/prove (go public)
- Use this template to create your own repo (public is the default).
- Run the
setupworkflow once. It generates anagekeypair, commits the recipient key tokeys/pile.age.pub, stores the private identity as the repo secretPILE_AGE_IDENTITY, and fills inpile.yml. - Run the
handshakeworkflow. It opens a registration PR on Tell with yourage_recipientand pickup location — no write access to your repo is requested. Then pin Tell's published signer key intokeys/tell.signers+pile.ymlby hand (confirm the fingerprint out-of-band — seekeys/README.md). - From then on, Tell publishes your encrypted feed at
/piles/<id>/feed/*. Theingestworkflow pulls, verifies, and folds it into your ownfeed/tellbranch on a cadence.
Or in one gesture: bin/pile-new collapses steps 1–3 — it creates the repo from this template,
fills pile.yml + keys/pile.age.pub, and prints the handshake entry. Two postures: --recipient age1…
(Mobile: mint the identity on your device with
anecdote's age-mint.mjs;
no identity exists host-side, no SETUP_TOKEN, no secret at all) or --keygen (Computer:
local age-keygen, identity stored as the new repo's secret, never printed). Needs PILE_NEW_TOKEN —
a credential that can create repos under --owner, held by whoever runs the gesture.
Rented: the provision workflow runs the same gesture for someone — they hand over only a
device-minted recipient (there is deliberately no keygen input, and bin/pile-new hard-refuses
--keygen with --provisioner), and the pile is stamped provisioner: so a managed pile says so
(the attestation, minimal form). The rework this opens is civic-node
OPEN-QUESTIONS.md §P,
shaped in docs/TENANCY.md.
A third party that manages piles speaks this repo's spec — or has its homebrew attested in the
pile's metadata when anything talks to it.
| Command | Who | What |
|---|---|---|
bin/verify |
anyone | Verify the chain, the signature against the registered signer, and ratchet commitments. |
bin/ingest |
cron | Pull each source's feed from its Tell gateway url, verify, persist into your own feed/* branch + state/. |
bin/decrypt |
owner | age-decrypt a block or range (needs PILE_AGE_IDENTITY). |
bin/report |
owner | Build reports from verified state. Aggregation is yours to define. |
bin/prove |
owner | Publish a ratchet checkpoint (or the identity) so others can verify. |
Judging happens at your Tell, not here. Tell applies the per-poll constitution you delegate to it
before it seals a digest — it judges the public Issue plaintext, so no key is involved — and every
record arrives carrying its governed verdict (accept / reject / needs-judgment / held) and
the constitution_sha that produced it. After bin/decrypt you read those verdicts and may
re-judge by hand at your boundary. The pile holds no judging round and no poll definitions; those
live in constitutions/ on your Tell.
- The committed
keys/pile.age.pubcan only encrypt. Your private identity is never committed. - Run it private if you want; the encryption is the same. Public is the default because the design is built so a public tank still leaks nothing until you decide to prove it.