diff --git a/DESIGN.md b/DESIGN.md index 25b4086..f1f9385 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -12,9 +12,9 @@ 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 version-honest.** Every signature references the *exact - version* it signed, by content hash. You can always prove what was actually - signed — the property a centralized petition site can't offer. +- **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.vote` @@ -26,68 +26,60 @@ These fall out of ATProto's grain, and everything below follows from them: | record | lives in | what it is | |---|---|---| -| `petition` | author | one immutable version of a letter | +| `petition` | author | an immutable open letter | | `draft` | author | a mutable work-in-progress that publishes a `petition` | -| `endorsement` | signer | version-pinned support | -| `objection` | objector | version-pinned dissent | +| `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`, optional `summary` / `closesAt` / `langs` / `tags` / `banner`. -Plus two structural fields: `supersedes?` (chains versions) and -`contributors[]` (DIDs the author claims as co-authors). +`createdAt`, `contributors[]` (DIDs the author claims as co-authors), and +optional `summary` / `closesAt` / `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 versions. A draft lives on the PDS, so it is *unlisted, not private*. +signable letters. A draft lives on the PDS, so it is *unlisted, not private*. -**endorsement / objection** — a CID-pinned reference to a petition version, +**endorsement / objection** — a CID-pinned reference to a petition, `createdAt`, an optional reason-for-signing `comment`, and a `listed` flag (a display hint; the signer's DID is always on the record). The record key is -derived from the signed version, so a signer holds at most one of each per -version and re-signing is idempotent. Holding both on the same version is -resolved in the AppView by recency. +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 version they affirm. A co-author is *confirmed* only where the -author's `contributors[]` and the co-author's `contribution` agree — a -two-sided handshake, version by version. - -## Versioning - -A published `petition` is never edited in place. To change a letter, the -author publishes a *new* `petition` whose `supersedes` points back at the -prior version, forming a chain. The genesis version's URI is the petition's -stable identity (what you share); the AppView walks the chain to find the -current head and to resolve forks, cycles, and dangling links. Because every -version is its own live record, anyone can fetch and verify exactly what was -signed — no trusted archive needed. - -Amendments are staged in a draft seeded from the current version. Signatures -pin a version, so a material amendment never silently carries old support -forward — the AppView shows who signed which version and can invite -re-affirmation. Deleting a version tombstones it; deleting the genesis -withdraws the petition. - -`supersedes` is our convention, not an ATProto primitive — the protocol sees -unrelated records, so the chain's meaning and its validation live in the -lexicon and the AppView. +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. -- **Integrity over stickiness.** Re-affirming after a material amendment is - friction by design — better than faking continuity of support. +- **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 - chain/head/count/confirmation semantics are ours. Interop requires others to - adopt `pub.openletter`, which is why the schemas are published (resolvable - via `_lexicon` DNS). + count and confirmation semantics are ours. Interop requires others to adopt + `pub.openletter`, which is why the schemas are published (resolvable via + `_lexicon` DNS). ## Deferred past v1 -First-class amendments (an amendment as its own endorsable record), +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 `goal`s, structured target responses, and real-time co-editing. Each is a clean additive extension; none requires reworking the above.