upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/docs/architecture/maintainer-model.md
blob: 9aebea727d5fca627fb12c9b89b0b90d6fc2af5c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# Maintainer Model

How ngit handles multi-maintainer repositories: coordinate discovery, maintainer sets, and the distinction between shared metadata and personal infrastructure.

## Coordinate Discovery

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).

ngit discovers the coordinate locally from (in priority order):

1. `nostr://` git remotes
2. `nostr.repo` git config
3. `maintainers.yaml`

No network access is required to find the coordinate. The coordinate may exist without a corresponding announcement event on relays.

## Maintainer Set

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.

Each maintainer independently decides who they list. Adding someone to your maintainers tag is an invitation to co-maintain.

## Consuming vs Publishing

The key architectural distinction is between **consuming** repository data (fetching, cloning, listing) and **publishing** it (`ngit init`).

### Consuming: Union Across Maintainers

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.

### Publishing: Personal Infrastructure, Shared Metadata

When publishing via `ngit init`, fields are sourced differently depending on their type:

#### Shared Metadata

Sourced from the **latest event** (by `created_at`) across the maintainer set:

- `name`
- `description`
- `web`
- `hashtags`

Rationale: these are shared identity. If any maintainer updates the project name, all subsequent re-announcements should pick it up.

#### Infrastructure (Personal)

Each maintainer has their own infrastructure preferences. When publishing, infrastructure comes from **my own announcement only**, not the union:

- **Grasp servers** -- where my git+nostr data is hosted. Each grasp server derives:
  - Clone URL: `https://{server}/{npub}/{identifier}.git`
  - Relay URL: `wss://{server}`
  - Blossom URL: `https://{server}`
- **Additional relays, git servers, blossoms** -- beyond what grasp servers provide

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.

#### Maintainers

Sourced from **my own announcement only**. Each maintainer independently decides who they list.

If I don't have an existing announcement (first time co-maintaining), the default is `[me, trusted_maintainer]`.

#### Earliest Unique Commit

Cascade: my own event's value, then other maintainers' values, then the local root commit. A mismatch between maintainers may indicate a fork.

#### Identifier

From the existing coordinate. Cannot change without `--force` (changing it creates a new repository).

## Init States

When `ngit init` runs, there are 5 possible states based on what exists locally and on relays:

| State | Condition | Behavior |
|-------|-----------|----------|
| **Fresh** | No coordinate found | Must provide name + server infrastructure |
| **Coordinate Only** | Coordinate exists, no announcement on relays | Requires `--force` (could be a relay/network issue) |
| **My Announcement** | Announcement exists, I'm the trusted maintainer | Re-publish/update, no force needed |
| **Co-Maintainer** | Announcement exists, I'm listed as maintainer | Publish own announcement, no force needed |
| **Not Listed** | Announcement exists, I'm not in maintainer set | Requires `--force` |

See `src/bin/ngit/sub_commands/init.rs` (`InitState` enum) and `tests/ngit_init.rs` for the implementation and test coverage.