Skip to content

server: federation's receiving half — the egress pull worker, re-validation, weighted per-peer budgets, and the operator peer commands #476

Description

@justin13888

Deferred from #406 (PR #472), which landed federation's serving half: a peer holding a capability this server minted pulls a shared album through GET /v1/sync?album_id= and GET /v1/blob/{hash}, the grant's lifecycle (mint, refresh, revoke) is on the wire, per-peer events-per-hour rides CounterStore, signed federated report intake and the server-level blocklist are in, and both stores have in-memory and Postgres adapters (migration ordinal 6).

What that PR did not build, and why each was left rather than stubbed:

1. The egress pull worker (S-E2 remainder)

Nothing on this server fetches from another. design/federation.md describes a peer pulling "on Capsule's schedule, through Capsule's validation pipeline", and the ownership, schedule and failure policy of that worker are not settled anywhere — it is not a route, so it does not fall out of the API surface. With it come:

  • invariant 20 re-validation of everything pulled (all of invariants 1–18 + 25 re-applied to a peer's manifests);
  • the per-(receiving_user, source_peer) storage quota design/quota.md names for cached pulls;
  • the breadcrumb index and the soft-fail rejected-hash table (federation.md), including inbound stale-revival;
  • accepting hints on the low-trust channel, which is what would make the pull event-driven rather than polled.

capsule-sdk::federation::FederationPull is the client-side orchestration of exactly this pull and is the shape a server-side worker would drive; it is not a server component.

2. Weighted per-peer budgets, the error budget, the breaker and probation

federation.md names events/hour, bytes/hour and CPU/hour, an error budget with a circuit breaker, and a probation tier for new peers. Only events/hour shipped. The other two need a weighted counter — charge N units in one atomic call — and CounterStore::hit charges one; expressing bytes as N calls is the read-then-write loop that port exists to forbid. Nothing measures per-request CPU at all. error.federation.circuit_open is in the catalog and deliberately unused until this lands.

3. Peer keys are operator-pinned, and no operator command pins them

PeerStore::pin exists and POST /v1/federation/reports verifies against what it holds, but there is no way for an operator to put a key there. capsule-server peer pin|block|unblock was scoped and then dropped, because it cannot work: boot::assemble and boot::assemble_maintenance both refuse the Durable backend outright until #403's adapters land, so such a command would only run under serve --memory — which forgets what it pinned the moment it exits. It should land with or after the durable boot arm.

TOFU-fetching a peer's server-info at intake stays rejected on its own merits: the server has no outbound HTTP client, and making the first report from an unknown server the thing that decides whether to trust it is the wrong moment to decide.

4. Moderation's admin surface (S-C49 remainder)

ModerationStore::pending_reports is the queue and nothing reads it over HTTP. design/moderation.md names an admin throughout and specifies no way for one to authenticate; SLICES.md S-C49 says that question has to be answered first. Blocklist exchange between peers is v2 by the contract and is not this issue.

Where the seams are

  • capsule-server/src/federation/mod.rs — admit is where a receiving-side budget would be charged; Presentation is where a new presentation kind would go.
  • capsule-server/src/counter/mod.rs — CounterStore::hit is the port a weighted charge would extend.
  • capsule-server/src/federation/peers.rs — PeerRecord::signing_key is Option precisely so an unpinned peer is a real state.
  • capsule-server/src/moderation/mod.rs — pending_reports is the read an admin surface would expose.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions