spec.md 10.0 KB

Purpose

Defines lilith as a persistent ordinary overlay peer providing bounded swarm rendezvous through one listener and durable store without joining, probing, or serving advertised swarms.

ADDED Requirements

Requirement: Single ordinary overlay configuration

Lilith SHALL support one overlay section containing accept and external addresses, ordinary bootstrap peers, connection policy, datastore/hostlist/ad store paths, public-enumeration policy, and finite resource limits within swarm-overlay maxima. It SHALL start one ordinary overlay P2p advertising ("swarm-ad-store", 1).

Bootstrap addresses MUST be ordinary peers, not seed sessions. Lilith MAY run inbound-only with zero ordinary outbound slots as an operator topology choice. Production lilith overlay configuration MUST disable local-test egress mode; every configured outbound target SHALL use the exact resolved-target validation and dial budgets from swarm-overlay. It SHALL NOT require a per-swarm listener, descriptor, magic, datastore, or network instance to store ads and answer lookup. Learning an ID MUST NOT make lilith join or serve it.

Scenario: Overlay-only deployment

  • WHEN a valid overlay section exists without legacy sections
  • THEN lilith starts one persistent ordinary overlay peer

Scenario: Inbound-only topology

  • WHEN an overlay listener has zero outbound slots
  • THEN inbound ordinary peers can maintain sessions, submit ads, and query

Scenario: Invalid configuration

  • WHEN any path, address, privacy policy, or resource limit is invalid
  • THEN startup fails before overlay activity without panic or unbounded fallback

Requirement: Unknown swarms need no reconfiguration

Lilith SHALL validate, store, relay, and answer valid ads for previously unknown IDs within all message/store/work bounds and without descriptors. Learning new IDs MUST NOT create swarm listeners or app protocols.

Scenario: Fresh swarm ad

  • WHEN a valid unknown-ID ad arrives
  • THEN it becomes available to bounded lookup without restart or operator action

Scenario: Rendezvous-only learning

  • WHEN many swarm IDs are learned
  • THEN lilith still runs one overlay and no swarm instance

Requirement: Durable state preserves bounded replay and expiry

Lilith SHALL persist normalized per-swarm address records with their current visibility/expiry, local expiry metadata, stateless ordered indexes, protected replay IDs, and monotonic-epoch checkpoint metadata. It SHALL enforce configured caps no greater than 1,024 addresses per swarm, 65,536 total addresses, 1,024 general-pool protected IDs per swarm, and 262,144 protected IDs globally. Accepted address lifetime SHALL be clamped to the configured local receive cap, defaulting to 7,200 seconds and never exceeding 86,400 seconds. Restart MUST restore only remaining ad-address lifetime and MUST NOT revive expired entries.

At least every 600 seconds and on clean shutdown, lilith SHALL atomically persist the current monotonic epoch checkpoint, defaulting to 300 seconds. Restart SHALL compare unsigned 64-bit deadline/checkpoint ticks before checked subtraction: expired/equal records are removed; valid deltas over 173,400 seconds, arithmetic failure, or failed checked Instant addition are typed startup errors; remaining duration is capped at 172,800 seconds. Surviving records and the new epoch SHALL be replaced atomically. It MUST NOT reset every record present in the loaded database to a fresh horizon or shorten it. Crash/downtime MAY extend that remainder. Rollback before an ID's commit can remove replay state and permit replay; lilith makes no non-rollbackable guarantee.

Checkpoint failure reaching the 600-second maximum SHALL make lilith reject fresh ads until checkpoint recovery or controlled overlay shutdown; existing bounded lookup MAY continue.

Protected IDs MUST NOT be evicted early. Lilith authors no swarm ads and SHALL configure zero local-author reserve partitions. If a swarm quota or its global general pool has no expired slot, the applicable fresh remote ad SHALL be rejected rather than weakening replay protection. Persistence MUST NOT contain ad sources, queriers, query history, source-peer/ swarm associations, or private secrets. Replay ID-to-advertised-swarm binding solely for quota accounting is allowed and MUST NOT contain a peer/source. Decoding malformed/truncated records SHALL be fallible and bounded. Malformed or unverifiable seen-ID, quota/reserve, or epoch state SHALL fail overlay startup. Address records MAY be quarantined and the public index rebuilt only when replay and accounting state remains intact. Capacity failure SHALL not evict/reset protected state.

Seen-ID/quota state, address/index mutation, and acceptance SHALL commit atomically before relay enqueue. Commit failure performs neither mutation nor relay.

Scenario: Restart preserves remaining lifetime

  • WHEN lilith restarts before expiry without wall-clock rollback
  • THEN only remaining lifetime is restored

Scenario: Restart does not revive expiry

  • WHEN restart occurs after expiry
  • THEN the ad is not returned

Scenario: Protected set is full

  • WHEN all dedup slots are protected and a fresh ad arrives
  • THEN lilith rejects it without evicting protected replay state

Scenario: Restart restores seen-ID remainder

  • WHEN lilith loads valid persisted seen IDs after any clock movement
  • THEN each receives its conservative checkpointed remainder on a new monotonic epoch rather than a fresh full horizon

Scenario: One swarm reaches its replay quota

  • WHEN one claimed swarm consumes all of its unexpired general-pool slots
  • THEN lilith rejects another fresh ad for it without consuming other swarm capacity

Scenario: Store rollback loses accepted ID

  • WHEN lilith loads a database snapshot from before an ad's atomic commit
  • THEN replay may be accepted again and no rollback-resistant claim is made

Scenario: Reduced dedup cap blocks startup

  • WHEN configured capacity cannot hold valid persisted seen IDs
  • THEN overlay startup fails without evicting them

Scenario: Corrupt record

  • WHEN durable bytes are malformed or truncated
  • THEN authoritative replay/accounting corruption fails startup, while only non-authoritative address/index state may be quarantined/rebuilt fallibly

Requirement: Lilith performs no advertisement liveness dialing

Lilith MUST NOT connect to an advertised swarm address due to accepting, storing, relaying, expiring, or reporting an ad. It SHALL expire through local TTL, replay admission, and capacity policy only. It MUST NOT describe transport reachability as swarm compatibility; only a descriptor-holding joining app can perform the swarm handshake.

Scenario: Attacker-selected address

  • WHEN an accepted ad contains an attacker-selected shareable address
  • THEN no lilith ad-store task connects to it

Scenario: Passive expiry

  • WHEN an ad expires
  • THEN it stops being returned without a probe

Requirement: Cold-start lookup uses an ordinary correlated session

A client SHALL be able to configure lilith as an ordinary peer, establish a long-lived session, and issue request-ID-correlated bounded lookups. Lilith MAY return locally unexpired addresses whose servers are offline; responses are untrusted hints, not reachability proof.

Scenario: Fresh client queries directly

  • WHEN a no-cache client connects to lilith as an ordinary peer
  • THEN it can query without a seed-session exchange

Scenario: Stored address is stale

  • WHEN a returned unexpired address is offline
  • THEN the joining app handles failure within its deadline and lilith makes no availability guarantee

Requirement: Strict bounded resource policy

Lilith's overlay SHALL enforce all protocol message, URL, page, pending request, store, dedup, work, relay-fanout, and configured-safe maxima with strict ban policy. Small requests MUST NOT induce unbounded response, cursor, write, relay, allocation, or connection work. Legacy policy MUST NOT weaken overlay policy.

Scenario: Query flood

  • WHEN one channel exceeds message or work budgets
  • THEN strict penalties apply while state remains bounded

Scenario: Ad flood

  • WHEN fresh ads reach address or protected-ID caps
  • THEN deterministic rejection/eviction rules preserve every bound and replay guarantee

Scenario: Legacy relaxed policy

  • WHEN a legacy instance is relaxed
  • THEN overlay strict policy remains independent

Requirement: Aggregate-only overlay status

Status RPC SHALL expose listener state, aggregate connection counts, configured capacities, current address/dedup counts, evictions, rejections, and expiries. It MUST NOT expose peer or advertised addresses, queried/private swarm IDs, ad sources, per-peer counters, query history, or source/query associations. Public enumeration, if enabled, remains the bounded overlay protocol.

Scenario: Operator reads health

  • WHEN status is requested
  • THEN aggregate health/capacity/count metrics are returned without peer or swarm-query identifiers

Scenario: Querier data is absent

  • WHEN peers query different IDs
  • THEN status and durable metrics cannot identify which peer queried which ID

Requirement: Legacy sections remain isolated during migration

Lilith SHALL continue accepting valid legacy sections as independent P2p instances. Overlay and legacy settings, listeners, paths, protocol registries, policies, failures, and shutdown handles MUST remain isolated. Legacy refusal requires a later release-boundary plan.

Scenario: Mixed configuration

  • WHEN overlay and legacy sections coexist
  • THEN each runs with independent state and policy

Scenario: Overlay failure

  • WHEN overlay startup or runtime fails
  • THEN failure is reported without silently changing legacy configuration

Scenario: Legacy-only deployment

  • WHEN currently valid legacy sections exist without overlay
  • THEN they remain accepted during migration