Skip to content

feat: SignedDirectory service — relay-hosted openly-writable signed record registry #33

Description

@bigdestiny2

Context

Companion to #21 (forward-relay opt-in transport) — extends the "relays-as-availability-infrastructure" story with an always-on signed record registry that fills the gap between transient topic-swarm announces and persistent custody intents.

First consumer: anonGPT marketplace (seller publishes signed Offer, buyer lists + verifies). Generic enough that future apps (job boards, file-share directories, signed gossip feeds) can reuse without modification.

Design contract

Trust model: identical to topic-swarm — relay can omit/reorder records but cannot forge them. Records are signed by their author. Verification happens on the buyer side (verifyDirectory()-style adapter); relay's job is transport only.

Trustless properties:

  • Signature binds (authorPubkey, payload, timestamp) tuple
  • Relay never inspects payload contents beyond size + signature + timestamp envelope
  • Buyer's verifyDirectory() adapter is the authoritative validator

Service shape

Mirrors ForwardRelay exactly — opt-in, default-off, fleet-deploy-on-config-flip.

Wire protocol (hiverelay-signed-directory channel)

  • publish({ authorPubkey, payload, timestamp, signature }) — open to anonymous peers; relay validates signature + size + timestamp; stores in local Hyperbee keyed by authorPubkey; newest-timestamp-wins overwrite
  • list({ since? }) — open; returns all non-expired records, optionally filtered by timestamp > since
  • subscribe() — streaming pushes of new records as they land (replication primitive for cross-relay)

Storage policy (concrete defaults)

knob default rationale
maxEntryBytes 8192 per-record cap
ttlSeconds 86_400 (24h) balances always-on vs eviction churn
maxEntriesPerAuthor 1 single live record per authorPubkey; updates overwrite
publishRatePerMinute 5 (per-peer) mirrors circuit-relay.maxReservesPerMin
maxTotalEntries 10_000 bounds the bee for storage-constrained operators
evictionPolicy TTL-oldest-first, then LRU predictable, simple
clockSkewToleranceSeconds 60 prevents backdated "always-newest" key squatting

Critical: clockSkewToleranceSeconds enforces |now - offerTs| < tolerance on BOTH sides. Without it, a malicious author with offerTs = 2099-01-01 permanently outranks legitimate updates.

Config namespace

{
  "signedDirectory": {
    "enabled": true,
    "maxEntryBytes": 8192,
    "ttlSeconds": 86400,
    "maxEntriesPerAuthor": 1,
    "publishRatePerMinute": 5,
    "maxTotalEntries": 10000,
    "clockSkewToleranceSeconds": 60
  }
}

Default-off. Operators opt in deliberately, same posture as forwardRelay.

Replication

Cross-relay replication mechanism — prefer option 2 below:

  1. Federation HTTP poll extension — pull-based, lag scales with poll interval. Don't.
  2. New Protomux push channel (hiverelay-signed-directory-push) — symmetric with the existing custody-push design. Source of truth: any relay that received a publish propagates to peer relays. Receivers dedupe by (authorPubkey, timestamp).
  3. Seller multi-publishes (no inter-relay replication) — doesn't fit the "always-on" framing if a buyer hits an empty relay.

Out of scope (intentionally)

  • Relay does NOT validate payload semantics (no "is this a real offer?" check)
  • Relay does NOT charge for publish (zero-cost open-write; abuse-resistance is in the limits, not in payment)
  • Relay does NOT enforce uniqueness beyond (authorPubkey, timestamp) — multiple authors can publish identical payloads
  • Relay does NOT broadcast to non-subscribing clients (no push to random peers)

PR shape

Mirrors the ForwardRelay PR shape:

  • packages/core/core/services/signed-directory.js — service implementation
  • test/unit/signed-directory.test.js — brittle: rejects unsigned/oversized/backdated entries, TTL eviction, newest-timestamp-wins, per-peer rate limit, total-entries cap eviction under pressure
  • Optional but ideal: test/integration/signed-directory-replication.test.js — two-relay setup proving cross-relay propagation
  • config.signedDirectory plumbing in RelayNode
  • PRODUCTION.md "## SignedDirectory (opt-in registry)" operator enable section
  • CHANGELOG entry for v0.10.2

Acceptance

  • Brittle suite green
  • Manual: a buyer connecting to a signedDirectory.enabled=true relay can list() records the seller published; relay restart doesn't lose records within TTL; expired records eviction-pass cleans up
  • Replication: a record published to relay A appears on relay B within 30s when both are signedDirectory-enabled

Deploy plan (maintainer)

  • Land PR → tag v0.10.2 → roll fleet → flip signedDirectory.enabled=true in each box's /root/.hiverelay/config.json (same atomic-merge pattern used for forwardRelay)
  • Three already-fronted endpoints become signed-directory-capable: wss://relay-us.p2phiverelay.xyz, wss://relay-sg.p2phiverelay.xyz, and wss://relay-eu.p2phiverelay.xyz (once DNS lands)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions