openletter — design#
openletter is an ATProto-native petition system: anyone can write an open
letter (a petition) and anyone can endorse or object to any number of them.
openletter.pub is the lexicon (pub.openletter.*); openletter.vote is the
AppView. See the README for the pitch.
Principles#
These fall out of ATProto's grain, and everything below follows from them:
- Account-centric ownership. A petition lives in its author's repo; each endorsement, objection, and co-author attestation lives in that person's repo. Nobody's signature is held by a central service.
- CID-pinned, so signatures are honest. Every signature references the petition by content hash, so you can always prove exactly what was signed — the property a centralized petition site can't offer.
- Single-writer. A record is signed by one account. "Collaboration" is modeled as handshakes between repos, never shared writes.
- The AppView interprets; it never owns or gatekeeps.
openletter.voteaggregates records off the network and computes the views below. Counts are eventually-consistent, and the author can't truly prevent a signature — anyone can write an endorsement to their own repo.
Records — pub.openletter.*#
| record | lives in | what it is |
|---|---|---|
petition |
author | an immutable open letter |
draft |
author | a mutable work-in-progress that publishes a petition |
endorsement |
signer | support for a petition |
objection |
objector | dissent against a petition |
contribution |
co-author | the attestation half of co-authorship |
petition — title, markdown body (CommonMark, no raw HTML),
recipients[] (each a name plus an optional ATProto identity and/or URL),
createdAt, contributors[] (DIDs the author claims as co-authors), and
optional summary / langs / tags / banner. Once published it is never
edited (see Immutability).
draft — the same content fields, mutable and edited in place. Publishing
creates a petition, so the petition collection only ever holds finished,
signable letters. A draft lives on the PDS, so it is unlisted, not private.
endorsement / objection — a CID-pinned reference to a petition,
createdAt, and a reason-for-signing comment (optional on an endorsement,
required on an objection — dissent has to say why). The signer's DID is always
on the record, so there is no "anonymous" signature. The record key is derived
from the petition, so a signer holds at most one of each per petition and
re-signing is idempotent. Holding both on the same petition is resolved in the
AppView by recency.
contribution — published by a claimed co-author: a CID-pinned reference to
the petition they affirm. A co-author is confirmed only where the author's
contributors[] and the co-author's contribution agree — a two-sided
handshake.
Immutability#
A published petition is never edited. Drafts are where a letter is worked
out; once published it is frozen, and every signature is pinned to that exact
text by CID. To change a published letter the author withdraws it (deletes the
record, which tombstones it) and publishes a new one — which starts over with
no signatures. This keeps the integrity story simple: you signed exactly this,
and it can't change underneath you. Versioning and amendments are deferred (see
below).
Tradeoffs we accept#
- No privacy at the data layer. Repos are public: drafts are unlisted not secret, and an "unlisted" endorsement only hides a name in the UI.
- No edits after publishing. Fixing even a typo means withdraw-and- republish, which resets signatures. Drafts make this rare; versioning is a later addition.
- Meaning lives in convention + AppView. The lexicon stores records; the
count and confirmation semantics are ours. Interop requires others to adopt
pub.openletter, which is why the schemas are published (resolvable via_lexiconDNS).
Deferred past v1#
Petition versioning and amendments (editing a published letter while keeping
its signatures; first-class amendments as their own endorsable records),
comments/discussion, suggestion-based collaborative editing, signature
goals, advisory close dates, structured target responses, and real-time
co-editing. Each is a clean additive extension; none requires reworking the
above.