diff options
Diffstat (limited to 'docs/architecture')
| -rw-r--r-- | docs/architecture/maintainer-model.md | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/docs/architecture/maintainer-model.md b/docs/architecture/maintainer-model.md new file mode 100644 index 0000000..9aebea7 --- /dev/null +++ b/docs/architecture/maintainer-model.md | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | # Maintainer Model | ||
| 2 | |||
| 3 | How ngit handles multi-maintainer repositories: coordinate discovery, maintainer sets, and the distinction between shared metadata and personal infrastructure. | ||
| 4 | |||
| 5 | ## Coordinate Discovery | ||
| 6 | |||
| 7 | A **coordinate** is a `(kind, pubkey, identifier)` tuple that uniquely identifies a repository on nostr. The pubkey in the coordinate is the **trusted maintainer** (typically the original creator). | ||
| 8 | |||
| 9 | ngit discovers the coordinate locally from (in priority order): | ||
| 10 | |||
| 11 | 1. `nostr://` git remotes | ||
| 12 | 2. `nostr.repo` git config | ||
| 13 | 3. `maintainers.yaml` | ||
| 14 | |||
| 15 | No network access is required to find the coordinate. The coordinate may exist without a corresponding announcement event on relays. | ||
| 16 | |||
| 17 | ## Maintainer Set | ||
| 18 | |||
| 19 | Each repository announcement (kind 30617) contains a `maintainers` tag listing public keys. These form a recursive set: if Alice lists Bob, and Bob lists Carol, then {Alice, Bob, Carol} are all in the maintainer set. | ||
| 20 | |||
| 21 | Each maintainer independently decides who they list. Adding someone to your maintainers tag is an invitation to co-maintain. | ||
| 22 | |||
| 23 | ## Consuming vs Publishing | ||
| 24 | |||
| 25 | The key architectural distinction is between **consuming** repository data (fetching, cloning, listing) and **publishing** it (`ngit init`). | ||
| 26 | |||
| 27 | ### Consuming: Union Across Maintainers | ||
| 28 | |||
| 29 | When consuming repo data, `relays`, `clone` (git server URLs), and `blossoms` are **unioned** across all maintainers' announcement events. This means any maintainer can add a mirror git server or relay and all users benefit automatically. | ||
| 30 | |||
| 31 | ### Publishing: Personal Infrastructure, Shared Metadata | ||
| 32 | |||
| 33 | When publishing via `ngit init`, fields are sourced differently depending on their type: | ||
| 34 | |||
| 35 | #### Shared Metadata | ||
| 36 | |||
| 37 | Sourced from the **latest event** (by `created_at`) across the maintainer set: | ||
| 38 | |||
| 39 | - `name` | ||
| 40 | - `description` | ||
| 41 | - `web` | ||
| 42 | - `hashtags` | ||
| 43 | |||
| 44 | Rationale: these are shared identity. If any maintainer updates the project name, all subsequent re-announcements should pick it up. | ||
| 45 | |||
| 46 | #### Infrastructure (Personal) | ||
| 47 | |||
| 48 | Each maintainer has their own infrastructure preferences. When publishing, infrastructure comes from **my own announcement only**, not the union: | ||
| 49 | |||
| 50 | - **Grasp servers** -- where my git+nostr data is hosted. Each grasp server derives: | ||
| 51 | - Clone URL: `https://{server}/{npub}/{identifier}.git` | ||
| 52 | - Relay URL: `wss://{server}` | ||
| 53 | - Blossom URL: `https://{server}` | ||
| 54 | - **Additional relays, git servers, blossoms** -- beyond what grasp servers provide | ||
| 55 | |||
| 56 | Grasp-format clone URLs belonging to other maintainers are kept as additional git servers (they're part of the union for consumers) but are not treated as my grasp servers. | ||
| 57 | |||
| 58 | #### Maintainers | ||
| 59 | |||
| 60 | Sourced from **my own announcement only**. Each maintainer independently decides who they list. | ||
| 61 | |||
| 62 | If I don't have an existing announcement (first time co-maintaining), the default is `[me, trusted_maintainer]`. | ||
| 63 | |||
| 64 | #### Earliest Unique Commit | ||
| 65 | |||
| 66 | Cascade: my own event's value, then other maintainers' values, then the local root commit. A mismatch between maintainers may indicate a fork. | ||
| 67 | |||
| 68 | #### Identifier | ||
| 69 | |||
| 70 | From the existing coordinate. Cannot change without `--force` (changing it creates a new repository). | ||
| 71 | |||
| 72 | ## Init States | ||
| 73 | |||
| 74 | When `ngit init` runs, there are 5 possible states based on what exists locally and on relays: | ||
| 75 | |||
| 76 | | State | Condition | Behavior | | ||
| 77 | |-------|-----------|----------| | ||
| 78 | | **Fresh** | No coordinate found | Must provide name + server infrastructure | | ||
| 79 | | **Coordinate Only** | Coordinate exists, no announcement on relays | Requires `--force` (could be a relay/network issue) | | ||
| 80 | | **My Announcement** | Announcement exists, I'm the trusted maintainer | Re-publish/update, no force needed | | ||
| 81 | | **Co-Maintainer** | Announcement exists, I'm listed as maintainer | Publish own announcement, no force needed | | ||
| 82 | | **Not Listed** | Announcement exists, I'm not in maintainer set | Requires `--force` | | ||
| 83 | |||
| 84 | See `src/bin/ngit/sub_commands/init.rs` (`InitState` enum) and `tests/ngit_init.rs` for the implementation and test coverage. | ||