Purpose: Define a complete, copy-ready architecture for Orbit-2’s endorsement (star) flow, bridging the Registry (trust ledger) and Feed (discovery) layers. Integrates webhook delivery, backfill sync, and Cloudflare materialization.
TBD
- Registry (Band 8) — canonical ledger of signed facts.
- Workers (Band 3) — read-side materialization via KV/Queue.
- Feed (Band 3) — read-only discovery API; joins star data from KV.
CREATE TABLE subjects (
id BIGSERIAL PRIMARY KEY,
origin TEXT NOT NULL, -- e.g., "feed.ff" | "feed.objkt" | "onchain.eth"
type TEXT NOT NULL, -- e.g., "playlist" | "asset" | "device" | "user"
ref TEXT NOT NULL, -- stable external ID, e.g., playlist ID or on‑chain token id
digest_b64 TEXT, -- optional: base64 of hash binding to exact version
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE (origin, type, ref, coalesce(digest_b64,''))
);
CREATE TYPE fact_status AS ENUM ('active','revoked','expired','quarantined');
CREATE TABLE facts (
id BIGSERIAL PRIMARY KEY,
kind TEXT NOT NULL, -- e.g., 'endorsement.star', 'rights.grant', 'identity.link'
subject_id BIGINT NOT NULL REFERENCES subjects(id),
issuer_did TEXT NOT NULL, -- did:key:... | did:ff:... | did:pkh:...
body_json JSONB NOT NULL, -- canonical body for human/machine semantics
body_hash_b64 TEXT NOT NULL, -- base64(sha256(RFC8785(body_json)))
sig_b64 TEXT NOT NULL, -- base64(ed25519(...) or EIP-191, etc.)
alg TEXT NOT NULL, -- 'ed25519' | 'eip191' | 'bbs+'
status fact_status NOT NULL DEFAULT 'active',
issued_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE (kind, subject_id, issuer_did, body_hash_b64)
);
CREATE INDEX idx_facts_subject_kind ON facts (subject_id, kind) WHERE status='active';
CREATE INDEX idx_facts_issuer ON facts (issuer_did) WHERE status='active';
CREATE TABLE fact_revocations (
id BIGSERIAL PRIMARY KEY,
fact_id BIGINT NOT NULL REFERENCES facts(id),
issuer_did TEXT NOT NULL,
reason TEXT,
sig_b64 TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE signers (
did TEXT PRIMARY KEY,
roles TEXT[] NOT NULL, -- e.g., {'curator','institution'}
added_by TEXT NOT NULL,
added_at TIMESTAMPTZ DEFAULT now(),
revoked_at TIMESTAMPTZ
);endorsement.star= binary flag per curator per subject.- Active fact: curator starred the playlist.
- Revoked fact: curator un-starred.
- Only one active per
(issuer_did, subject_id, kind).
POST /facts→ create new fact; validates signature and role (curator).POST /facts/{id}/revoke→ mark revoked (un-star).GET /facts?kind=endorsement.star&origin=feed.ff&type=playlist&ref=pl_123→ list active facts with pagination.GET /facts?since_id=...→ delta feed for backfill.GET /signers?role=curator→ listPOST /signers(admin) → add/rolesDELETE /signers/{did}(admin) → revoke- Webhook on every insert/update: POST to Cloudflare
registry-hookwith HMAC header.
{
"id":123,
"kind":"endorsement.star",
"subject":{"origin":"feed.ff","type":"playlist","ref":"pl_123"},
"issuer_did":"did:key:z6...",
"status":"active",
"issued_at":"2025-10-13T03:00:00Z"
}Header: X-Signature: sha256=<base64(HMAC_SHA256(body, secret))>
- Validates HMAC signature and
Dateheader. - Enqueues body to Queue
facts-ingest. - 204 response if accepted (idempotent via
X-Idempotency-Key).
Consumes from facts-ingest and writes to KV.
KV.put(`star:${playlist_id}`, `${playlist_id}`); // Query playlist has been starred.
KV.put(`star:created:asc:{playlist_created}:${playlist_id}`, `${playlist_id}`); // query starred playlist sorted by timestamp in ascending
KV.put(`star:created:desc:{playlist_created_desc}:${playlist_id}`, `${playlist_id}`); // query starred playlist sorted by timestamp in descendingKV.delete(`star:${playlist_id}`);
KV.delete(`star:created:asc:{playlist_created}:${playlist_id}`);
KV.delete(`star:created:desc:{playlist_created_desc}:${playlist_id}`);- Runs every 1 min.
- Fetches
/facts?since_id=<last>from Registry. - Applies same materialization rules.
- Updates high-water mark
KV.put('facts_highwater', id).
GET /playlists?star=true→ Lookupstar:created:asc:{playlist_created}to list starred playlists by playlist created timeGET /playlists/:id→ Lookupstar:${playlist_id}to check if the playlist has been starred.
Example response:
{
"dpVersion": "1.0.0",
"id": "503e271c-7d96-4d80-ae10-ae2ba658d535",
"slug": "sean-s-feral-file-classics-9962",
"title": "Sean’s Feral File Classics",
"star" : true,
"created": "2025-08-12T09:56:41.713Z",
"items": []
}Feed never writes stars; it reflects current KV state.
- Curator taps Star in app. (Or send from CLI)
- App signs fact (Ed25519) and POSTs to Registry.
- Registry verifies and inserts fact → webhook fired.
- Worker queues + updates KV.
- Feed reads reflect immediately.
- Curator taps again → revoke fact → KV flag removed.
factstable = source of truth; Workers are derived view.- Backfill job replays facts to heal KV divergence. (Optional, could be defered)
- Registry ensures at most one active fact per keypair, preventing duplicates.
| Path | Auth | Notes |
|---|---|---|
| App → Registry | JWT + signature (Ed25519) | Each star request signed. |
| Registry → Worker | HMAC secret | Stored as Cloudflare Secret. |
| Worker → Registry (backfill) | HMAC or mTLS | Internal only. |
Replay protection: X-Idempotency-Key + evt: KV markers.
| Component | Platform | Storage | Observability |
|---|---|---|---|
| Registry | AWS (EKS/ECS) | Postgres | Prometheus + Grafana |
| Workers | Cloudflare | KV + Queues | Cloudflare Dashboards |
| Feed | Cloudflare | KV (read-only join) | Metrics + Logs |
Backups: nightly Postgres pg_dump; KV optional snapshot weekly.
-
Authorized role: curator (must exist in
signer_whitelist). -
Body schema:
{"issued_at": {"type": "string", "format": "date-time"}, "reason": {"type": "string", "maxLength": 512}} -
Materialization: binary toggle per curator; maintain short
stars_recentlist.
Future-ready (Orbit-3): new kinds like rights.grant or identity.link reuse same schema; only differ in verifier and KV writer.
| Layer | Test Type | Tools |
|---|---|---|
| Registry | Unit + API contract tests | Go test + Postman collections |
| Webhook | HMAC verification tests | Jest / Wrangler test |
| Workers | KV state transitions | cf-workers-test lib |
| End-to-end | Simulated curator flow | k6 load tests |
Target SLOs: POST /facts < 250 ms, webhook dispatch < 50 ms, Feed join < 10 ms.
- Stars are binary flags per curator/playlist.
- Registry holds the signed source of truth.
- Feed Workers mirror via webhook/backfill, storing KV presence only.
- Feed joins this state in read APIs.
- Fully Orbit-3 compatible; extensible to rights/licensing kinds.