lib/gitea/fed.sx: forges federate as peers. Each forge carries an instance id; users and repos project as AP actor documents (Person/ Group/Repository with inbox/outbox + clone endpoint); the outbox is the activity log in an AP-shaped envelope. Trust follows the events-federation pattern — a kv set of peer ids RE-CHECKED on every operation (inbox, mirror sync, delivery), so revoking a peer takes effect immediately; peer transports (dream app fns) live only in the runtime cache. Inbox (POST /api/ap/inbox, trust-gated): every accepted activity lands in a federated log with :origin provenance; open-issue/comment/open-pr MATERIALIZE — the foreign author becomes an auto-created proxy user '<name>@<peer>' and the issue/comment/PR is created locally under that identity. fed-deliver! pushes public-repo activities (cursor-based, never private) to every trusted peer's inbox. Cross-instance repo follow = mirror!/mirror-sync! over the Phase 3 wire client. fed-timeline merges local + foreign activities with provenance tags. Suite: two in-memory forges federating end to end — actor docs, trust lifecycle, materialization, proxy-user reuse, wire inbox 400/403/200, mirrors (clone/sync/trust-revocation), cursor delivery, timelines. Adds lib/gitea/README.md (composition map, architectural rules, known limits). Final scoreboard: 615/615 across repo/access/wire/issues/pr/ activity/search/fed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
62 lines
3.5 KiB
Markdown
62 lines
3.5 KiB
Markdown
# sx-gitea — a federated git forge in plain SX
|
|
|
|
A git forge built by **composing the x-on-sx subsystems**: every phase
|
|
wires one more substrate onto the forge. No third-party dependencies —
|
|
the whole stack is SX on the OCaml kernel.
|
|
|
|
Run the suite: `bash lib/gitea/conformance.sh` (per-suite scores in
|
|
`scoreboard.md`). Suites are independent `sx_server` sessions; heavyweight
|
|
substrates (Smalltalk/content, Scheme/flow, APL/feed, Haskell/search) load
|
|
only for the suites that need them.
|
|
|
|
## Composition map
|
|
|
|
| Phase | Module | Built on |
|
|
|-------|--------|----------|
|
|
| 1 repo | `repo.sx` | **sx-git** (`lib/git`, native-CID object store), persist kv |
|
|
| 2 access | `access.sx` | **acl** (datalog): repo role groups, collaborators, org teams; bearer tokens |
|
|
| 3 wire | `wire.sx` | git-style smart HTTP: pkt-line framing, upload/receive-pack, CID-verified packs; client (`clone!`/`fetch!`/`push!`) drives any dream app fn |
|
|
| 4 issues | `issues.sx` | **content** (Smalltalk): Markdown bodies as block documents; **relations** (datalog): derived issue graph |
|
|
| 5 pr | `pr.sx` | **sx-git** merge-base diffs + 3-way merge; **flow** (Scheme): durable open→approval→merge lifecycle; merge queue |
|
|
| 6 activity | `activity.sx` | **feed** (APL): timelines/dashboard; **events** (flow): durable at-least-once notifications |
|
|
| 7 search | `search.sx` | **search** (Haskell): tf-idf ranked code/issue/PR search, batched evaluations |
|
|
| 8 fed | `fed.sx` | ForgeFed: AP actors, trust-gated inbox with provenance + materialized federated issues/PRs, mirrors over the wire client, cursor-based delivery |
|
|
| web | `web.sx` | **dream**: routes, auth gating (401/403/404-hides-private), route-pack registry |
|
|
|
|
## Architectural rules of thumb
|
|
|
|
- **The kv store is the source of truth.** Owners, repo records, issues,
|
|
PRs, collaborators, teams, tokens, follows, trust, mirrors — all plain
|
|
dicts under `gitea/...` keys on one persist backend per forge.
|
|
Deleting a repo is a prefix purge (no ghost state on recreate).
|
|
- **Derived, not maintained.** The acl database and the relations graph
|
|
are *derived* from kv state and rebuilt when the derived facts change
|
|
(cached in the forge handle) — deletions can never dangle.
|
|
- **Instrument in the runtime.** Activity logging wraps the mutation
|
|
verbs by redefinition (`gitea/base-*!` + wrapper), so every caller
|
|
emits activity with zero call-site edits.
|
|
- **Everything is testable without sockets.** A forge is a value over a
|
|
`persist/mem-backend`; `gitea/app` is a pure request→response fn; the
|
|
wire client federates two in-memory forges directly.
|
|
- **Trust is re-checked, never cached.** Federation operations
|
|
(inbox, mirror sync, delivery) consult the trust set at use time.
|
|
|
|
## Per-repo git stores
|
|
|
|
Each repo's objects/refs live in their own `git/repo-named` namespace
|
|
`forge/<owner>/<name>` — identical content still shares CIDs, but repos
|
|
cannot see each other's objects. All ref moves go through `ref-cas!`;
|
|
concurrent pushes surface as `stale`/`non-fast-forward` per-ref statuses.
|
|
|
|
## Known limits (deliberate, documented)
|
|
|
|
- Wire packs carry one object per pkt line (~64KB); side-band chunking
|
|
is a future extension (`gitea/pkt-fits?` reports it). SHA-1/packfile
|
|
byte compat for stock git clients lives in `lib/git/{export,import}.sx`
|
|
and is not yet wired into the HTTP endpoints.
|
|
- Inbox activities are trust-gated but not signature-verified.
|
|
- Reopening a PR restarts its lifecycle flow (a cancelled flow cannot
|
|
resume); reviews survive.
|
|
- Issue web close/reopen does not emit activity (no actor at the core
|
|
call sites for `issue-close!`).
|