docs: capture remaining architecture vision; draft route-federation change
Gap analysis of docs/architecture.md against openspec/ and docs/ideas/ found the envisioned-but-uncaptured remainder. This commit captures it: New OpenSpec change (validated): - route-federation — the collaboration half of the federation vision: routes as dereferenceable trails:Route objects, Create/Update fan-out, Invite/Accept collaboration mirroring (arch decision #2), cross-instance Planner edits via HTTP-Signature-requested scoped tokens (decisions #3/#12), mirror sync healing (#16). Depends on social-federation §6 + route-sharing; carries the 2026-06-07 soak lessons as design constraints. New docs/ideas/ explorations: - instance-administration — registration toggle, suspend/ban, federation blocklists, reports (moderation now gates the federated comments idea) - social-interactions — local likes + comments (don't exist even locally; the foundation federated kudos/comments attach to) - activity-participants — group tagging with confirm/decline + federated mentions (the participants jsonb column is an untyped stub) - multi-day-collections — architecture open question #1, directions evaluated architecture.md cleanup: - Mastodon-compat section annotated with shipped/captured state + the live-soak interop lessons (attachment arrays, tombstones, 10s timeout, no backfill) - api.trails.cool removed (contradicted resolved decision #18) - Phase 1 ticked (shipped); Phase 2/3 items annotated with where each is tracked; specs/ → openspec/, activity.* → journal.*, cx21 → cx23 - brouter-web open question marked resolved-in-practice; new section pointing to where the unshipped vision is tracked Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a26d59c804
commit
bf9787e56a
11 changed files with 580 additions and 50 deletions
|
|
@ -0,0 +1,57 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Public routes are dereferenceable ActivityPub objects
|
||||
The Journal SHALL serve a public route at its canonical URL as a
|
||||
`trails:Route` object (JSON-LD metadata envelope + GPX attachment) when
|
||||
requested with an ActivityPub Accept header, and SHALL announce new
|
||||
public routes and new versions to followers and collaborators as
|
||||
`Create`/`Update` activities. Non-public routes SHALL NOT be
|
||||
dereferenceable or announced.
|
||||
|
||||
#### Scenario: Route object resolves
|
||||
- **WHEN** a trails instance GETs a public route's canonical URL with `Accept: application/activity+json`
|
||||
- **THEN** the response is a `trails:Route` object containing the metadata envelope (distance, elevation, day breaks, routing profile, contributors) and a GPX `Document` attachment
|
||||
|
||||
#### Scenario: New version fans out
|
||||
- **WHEN** a new version of a shared public route is created
|
||||
- **THEN** an `Update` activity carrying the new version is delivered to every collaborator instance's inbox
|
||||
|
||||
### Requirement: Cross-instance sharing via Invite/Accept mirroring
|
||||
Sharing a route with a user on another trails instance SHALL send an
|
||||
ActivityPub `Invite`; the remote user's `Accept` SHALL register them as
|
||||
collaborator on the owner's instance and establish a mirror (metadata +
|
||||
latest GPX, no version history) on theirs, kept current by `Update`
|
||||
fan-out. The owner's instance SHALL remain the canonical source.
|
||||
|
||||
#### Scenario: Invite accepted creates a mirror
|
||||
- **WHEN** Alice (instance A) shares a route with `@bob@b.example` and Bob accepts
|
||||
- **THEN** A records Bob as collaborator, sends the current route, and B stores a mirror visible in Bob's collection attributed to A
|
||||
|
||||
#### Scenario: Invite to a non-trails instance is refused
|
||||
- **WHEN** a share targets a handle whose instance does not pass the trails-instance check
|
||||
- **THEN** the share is refused at the API layer with a clear "route federation is trails-to-trails only" error
|
||||
|
||||
### Requirement: Cross-instance edits store to the owner via scoped tokens
|
||||
A collaborator's instance SHALL obtain a scoped, single-use edit token
|
||||
from the owner's instance via an HTTP-Signature-authenticated request,
|
||||
SHALL be refused when the requester is not an accepted collaborator,
|
||||
and the resulting Planner session SHALL save new versions directly to
|
||||
the owner's instance with the collaborator credited as contributor by
|
||||
actor IRI.
|
||||
|
||||
#### Scenario: Collaborator edits across instances
|
||||
- **WHEN** Bob starts an edit session on his mirror of Alice's route
|
||||
- **THEN** B obtains a token from A, the Planner opens with A's callback, and Bob's save creates the next sequential version on A crediting Bob's actor IRI
|
||||
|
||||
#### Scenario: Non-collaborator cannot obtain a token
|
||||
- **WHEN** an instance requests an edit token for an actor who is not an accepted collaborator
|
||||
- **THEN** the owner's instance refuses with 403 and records the attempt
|
||||
|
||||
### Requirement: Mirror sync healing
|
||||
Each instance holding mirrors SHALL periodically verify them against
|
||||
the canonical object and re-fetch when stale, and SHALL visibly mark a
|
||||
mirror whose origin has been unreachable past the verification window.
|
||||
|
||||
#### Scenario: Missed update is healed
|
||||
- **WHEN** instance B was offline long enough to miss an `Update` and its retry window
|
||||
- **THEN** B's next sync check detects the version gap and re-fetches the canonical route from A
|
||||
Loading…
Add table
Add a link
Reference in a new issue