mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 23:18:24 +00:00
docs(explanation): document the GRASP-08 outbound policy matrix
The GRASP-08 design doc listed outbound authentication as follow-up scope; the preceding commits implemented it, and architecture docs are living documents that must describe what was built. Replaces the follow-up bullet with an "Outbound authentication and sync policy" section presenting the decided matrix: NIP-42 answered everywhere with the relay owner key (skipped and immediately parked without one), restricted refusals terminal via the policy-refusal machinery, GRASP-08-advertising peers parked pre-dial on public instances versus fully credentialed (NIP-42 plus repository-root NIP-98 on Git fetches) on private ones, and missing NIP-11 treated as an ordinary relay. Records the rationale that NIP-42 is identification rather than confidentiality, and the derived-membership tightening to GRASP-08-advertising relays. The architecture doc's GRASP-08 section gains the matching outbound paragraph and membership caveat. Doc-only change; multi-service fleet and encrypted kind-10318 discovery deliberately remain follow-up scope. Validated by reading the rendered markdown against the implemented behavior in src/sync/mod.rs, src/sync/relay_connection.rs, and src/purgatory/sync/context.rs.
This commit is contained in:
@@ -716,7 +716,9 @@ single `PrivateAccess` set is shared by the HTTP and WebSocket services and
|
||||
the announcement admission policy. Push authorization remains the GRASP-01
|
||||
policy; a private credential proves service membership but never grants push
|
||||
rights. The effective set combines operator-configured members with NIP-11
|
||||
owner pubkeys learned for relays referenced by accepted announcements.
|
||||
owner pubkeys learned for relays referenced by accepted announcements,
|
||||
provided those relays' NIP-11 also advertises GRASP-08 (a public relay's
|
||||
owner gains nothing legitimate from private membership).
|
||||
Purgatory-only announcements are excluded. Reconciliation reuses the accepted
|
||||
repository index and the NIP-11 fetch already performed once per connection
|
||||
session, so private mode adds neither outbound connections nor subscriptions.
|
||||
@@ -754,6 +756,18 @@ identity publication: the kind 0/10002 events are seeded and served locally
|
||||
but never sent to the configured user-index relays, so a private relay does
|
||||
not advertise its existence.
|
||||
|
||||
Outbound, every sync connection (public or private instance) answers NIP-42
|
||||
challenges with the relay owner key when available; a `restricted:` refusal
|
||||
after authentication parks the subscription via the existing policy-refusal
|
||||
machinery. Peers advertising GRASP-08 in NIP-11 split by our own mode: a
|
||||
public instance detects them with a pre-dial NIP-11 fetch and parks them
|
||||
without ever opening the WebSocket, while a private instance treats them as
|
||||
peers — NIP-42 on the WebSocket plus the GRASP-08 repository-root NIP-98
|
||||
credential on purgatory Git fetches from that peer's host, both signed with
|
||||
the relay owner key. Relays without a readable `supported_grasps` are
|
||||
ordinary sync targets. See
|
||||
[GRASP-08 design](grasp-08-private-service.md#outbound-authentication-and-sync-policy).
|
||||
|
||||
One process currently represents one private collaborator service. Operators
|
||||
can run several independently configured instances for different groups. Fleet
|
||||
provisioning and lifecycle automation are deliberately left to a later change;
|
||||
|
||||
@@ -100,10 +100,11 @@ hints** (who to connect to), while the service-wide member set is the **ACL**
|
||||
### Why accepted-relay owners are admitted dynamically
|
||||
|
||||
Two private services mirroring the same repository must be able to read from
|
||||
each other. When an accepted announcement references another relay, that
|
||||
relay's NIP-11 `pubkey` is added to the member set, so a peer service (or the
|
||||
operator of an ordinary relay the team uses) can authenticate without manual
|
||||
whitelisting on both sides. The consequence — accepting one announcement
|
||||
each other. When an accepted announcement references another relay whose
|
||||
NIP-11 also advertises `GRASP-08`, that relay's NIP-11 `pubkey` is added to
|
||||
the member set, so a peer service can authenticate without manual
|
||||
whitelisting on both sides. Owners of referenced *public* relays are not
|
||||
admitted (see "Outbound authentication and sync policy" below). The consequence — accepting one announcement
|
||||
grants its referenced relay operators read access to the whole service —
|
||||
follows directly from the one-trust-domain model and is the operator's
|
||||
opt-in via announcement admission.
|
||||
@@ -160,7 +161,8 @@ lock a user out.
|
||||
- **Accepted-relay owners** are derived members, trusted transitively via
|
||||
announcement admission plus the referenced relay's NIP-11 self-assertion
|
||||
(the same HTTPS-from-domain trust anchor a `_@domain` NIP-05 lookup would
|
||||
provide, without an extra fetch or format).
|
||||
provide, without an extra fetch or format). Only relays whose NIP-11 also
|
||||
advertises `GRASP-08` qualify.
|
||||
- **Membership grants read access only.** Push authorization remains
|
||||
GRASP-01's maintainer model; repository admission remains announcement
|
||||
policy, which in private mode additionally requires the announcement
|
||||
@@ -169,13 +171,39 @@ lock a user out.
|
||||
invalidates future credentials, but repositories admitted while they were a
|
||||
member remain hosted until the operator curates them.
|
||||
|
||||
## Outbound authentication and sync policy
|
||||
|
||||
The outbound half of private-service support decides how this instance, as a
|
||||
*client*, treats the relays it syncs from:
|
||||
|
||||
| Situation | Behavior |
|
||||
| --- | --- |
|
||||
| Any relay issues a NIP-42 challenge | Answer with the relay owner key (public and private instances alike). The SDK retries the refused subscription once after authenticating. Without an owner key, authentication is skipped and auth-demanding subscriptions park immediately. |
|
||||
| A relay answers `restricted:` after valid authentication | Terminal: the subscription parks through the policy-refusal machinery (24-hour probe), no retry storm. |
|
||||
| Peer NIP-11 advertises `GRASP-08`, this instance is **public** | Not a sync target at all: detected by a pre-dial NIP-11 fetch and parked without ever opening the WebSocket, so no AUTH exchange happens and no credential could leak. |
|
||||
| Peer NIP-11 advertises `GRASP-08`, this instance is **private** | A peer: NIP-42 on the WebSocket *plus* the GRASP-08 repository-root NIP-98 credential attached to purgatory Git fetches from that peer's host, both signed with the relay owner key. |
|
||||
| NIP-11 missing, unreadable, or without `supported_grasps` | An ordinary relay. |
|
||||
|
||||
The pre-dial NIP-11 fetch re-runs the outbound target policy for
|
||||
event-directed URLs first, so the SSRF gate covers it like the dial itself.
|
||||
|
||||
**Why NIP-42 everywhere?** NIP-42 is identification, not confidentiality. A
|
||||
gated relay admitting our pubkey grants a *known* service read access — our
|
||||
pubkey is already published via NIP-11 and the NIP-05 root identity. A
|
||||
private instance authenticating outbound discloses its identity to the relays
|
||||
it syncs from, which is consistent with GRASP-08's public-discovery stance:
|
||||
private mode hides repository content, not the service.
|
||||
|
||||
**Why derived membership requires GRASP-08 (see above)?** For the same
|
||||
asymmetry: a public relay's owner gains nothing legitimate from private
|
||||
membership, because their relay enforces no confidentiality for the
|
||||
repositories it mirrors. Only relays advertising `GRASP-08` in their NIP-11
|
||||
`supported_grasps` mint derived members; configured `NGIT_PRIVATE_MEMBERS`
|
||||
are unaffected.
|
||||
|
||||
## Follow-up scope
|
||||
|
||||
Deliberately excluded from the initial single-service implementation:
|
||||
|
||||
- **Outbound authentication**: presenting NIP-42 and GRASP-08 NIP-98
|
||||
credentials when syncing *from* other private services, using a service
|
||||
identity key. This is the missing half of zero-configuration private
|
||||
mirroring.
|
||||
- **Multi-service fleet orchestration** and **encrypted kind-10318 client
|
||||
discovery**, which belong to future GRASP proposals.
|
||||
|
||||
Reference in New Issue
Block a user