소스 검색

openspec/swarm: tighten terminology due to inconsistent use of swarm for both subnet and orchestrator. now swarm is the subnet and orchestrator is called swarm pool.

darkfi 2 주 전
부모
커밋
b0958dc157

+ 93 - 68
openspec/changes/swarm/design.md

@@ -1,4 +1,4 @@
-# Design: swarm overlay for subnet rendezvous
+# Design: swarm overlay for swarm rendezvous
 
 ## Context
 
@@ -22,13 +22,38 @@ implementation constraints are:
 - `VersionMessage.features` is retained remotely but sent locally as empty.
   Existing variable version fields can combine past `VERSION_MAX_BYTES`, so
   feature validation alone is insufficient.
-- An ad store knows only one-way `SubnetId` and addresses. It cannot perform a
-  subnet handshake and must not become an attacker-controlled dialer.
+- An ad store knows only one-way `SwarmId` and addresses. It cannot perform a
+  swarm handshake and must not become an attacker-controlled dialer.
 - Current Tor state is process-global and does not guarantee independent onion
-  identities per subnet; I2P does not provide a general inbound listener.
+  identities per swarm; I2P does not provide a general inbound listener.
 - Apps such as darkirc/fud construct substantial state from `P2pPtr` before
   protocol registration. A registration closure alone is insufficient.
 
+## Terminology
+
+One sense per word:
+
+| Term | Meaning |
+|---|---|
+| overlay | the single rendezvous `P2p` network (fixed identity `darkfi-swarm`) where ads and lookups happen |
+| swarm | one application network: one descriptor, one `SwarmId`, one independent `P2p` instance |
+| `SwarmPool` | the orchestrator owning at most one overlay plus the independent `P2p` instance of each active swarm |
+
+The naming follows BitTorrent, which uses the same shape:
+
+| BitTorrent | Swarm subsystem |
+|---|---|
+| swarm = all peers sharing one torrent | swarm = the peers of one application network |
+| tracker / DHT rendezvous | the overlay, which hands out swarm addresses |
+| client session (libtorrent `session`) | `SwarmPool` |
+
+Wire names use the per-network sense: `getaddr`/`addrs` fetch one swarm's
+addresses and `getswarm`/`swarms` enumerate public swarms. `SwarmError`,
+`ProtocolSwarm`, the `swarm` feature and `net::swarm` module, and
+`swarm-ad-store` name the overlay protocol and subsystem, never a single
+swarm.
+
+
 ## Goals / Non-Goals
 
 **Goals:**
@@ -38,13 +63,13 @@ implementation constraints are:
 - Keep all untrusted wire, persistence, queue, request, and work state bounded.
 - Make bootstrap/source attempts, join completion, serving creation, rollback,
   recreation, and teardown explicit and testable.
-- Store/relay hints passively and validate only inside a joining subnet.
+- Store/relay hints passively and validate only inside a joining swarm.
 - Add no third-party dependency.
 - State realistic protocol disclosure and persistence boundaries.
 
 **Non-Goals:**
 
-- Multiplexing subnet traffic over overlay channels.
+- Multiplexing swarm traffic over overlay channels.
 - Authenticating ad authors or proving address ownership.
 - PIR, cover traffic, Sybil resistance, or global-observer resistance.
 - Automatic independent Tor/I2P provisioning.
@@ -106,20 +131,20 @@ field/format ambiguity would split deployed IDs.
 Initial messages are:
 
 ```text
-SubnetAd {
-    subnet_id, visibility, ad_id: [u8; 32],
+SwarmAd {
+    swarm_id, visibility, ad_id: [u8; 32],
     lifetime_secs, addrs: Vec<Url> // 1..=32
 }
-GetSubnetAddrs { request_id: [u8; 16], subnet_id, cursor? }
-SubnetAddrs    { request_id: [u8; 16], subnet_id, addrs, next? }
-GetPublicSubnets { request_id: [u8; 16], cursor? }
-PublicSubnets    { request_id: [u8; 16], subnet_ids, next? }
+GetSwarmAddrs { request_id: [u8; 16], swarm_id, cursor? }
+SwarmAddrs    { request_id: [u8; 16], swarm_id, addrs, next? }
+GetPublicSwarms { request_id: [u8; 16], cursor? }
+PublicSwarms    { request_id: [u8; 16], swarm_ids, next? }
 SwarmError       { request_id: [u8; 16], bounded_code }
 ```
 
-Commands are fixed to `swarm.ad`, `swarm.geta`, `swarm.addrs`, `swarm.gets`,
-`swarm.subs`, and `swarm.err` respectively. Struct field order is exactly the
-order shown in `swarm-overlay`; existing DarkFi encoding is used. Visibility is
+Commands are fixed to `ad`, `getaddr`, `addrs`, `getswarm`, `swarms`, and
+`err` respectively. Struct field order is exactly the order shown in
+`swarm-overlay`; existing DarkFi encoding is used. Visibility is
 `u8` (`0` public, `1` non-public), lifetime is `u32`, error codes are fixed
 `u8` values 0 through 3, and cursor version is one. No new serializer is added.
 
@@ -146,7 +171,7 @@ version: u8 | last_key: [u8; 32] | terminal_key: [u8; 32]
 ```
 
 Address pages use BLAKE3 of canonical URL bytes as ordered key; public pages use
-`SubnetId`. The first page captures the greatest current live key as a terminal.
+`SwarmId`. The first page captures the greatest current live key as a terminal.
 Later pages return live keys strictly after `last_key` and no greater than that
 terminal, then advance `last_key`. Mutation may make a traversal include or omit
 records, but never invalidates a well-formed cursor, allocates a server snapshot,
@@ -168,10 +193,10 @@ visibility is unsigned, an attacker can re-advertise an observed ID as public.
 
 Persistent nodes use existing `kvdb-overlay` trees for address records, public
 index, seen IDs, and monotonic-epoch metadata. Address keys are
-`(SubnetId, hash(canonical_url))`. Atomic batches update records and indexes.
-Seen keys remain global by `ad_id`; their values bind the advertised `SubnetId`
+`(SwarmId, hash(canonical_url))`. Atomic batches update records and indexes.
+Seen keys remain global by `ad_id`; their values bind the advertised `SwarmId`
 and general/local-reserve class only for quota/replay accounting. Reusing one ad
-ID under another subnet is therefore still a duplicate. No persistence API
+ID under another swarm is therefore still a duplicate. No persistence API
 receives a source channel/address.
 
 Each key has one normalized record containing URL, current visibility, expiry,
@@ -186,11 +211,11 @@ Defaults and hard maxima are:
 
 | Local limit | Default | Maximum |
 |---|---:|---:|
-| addresses per subnet | 256 | 1,024 |
+| addresses per swarm | 256 | 1,024 |
 | total addresses | 16,384 | 65,536 |
-| general protected IDs per subnet | 256 | 1,024 |
+| general protected IDs per swarm | 256 | 1,024 |
 | protected ad IDs | 65,536 | 262,144 |
-| local-author reserve subnet partitions | 32 | 256 |
+| local-author reserve swarm partitions | 32 | 256 |
 | accepted/authored address lifetime | 7,200 s | 86,400 s |
 | replay checkpoint interval | 300 s | 600 s |
 | relay fanout | 16 | 64 |
@@ -205,25 +230,25 @@ applies its own cap.
 Address capacity evicts expired first, then earliest expiry, then lexical key.
 Seen IDs remain protected exactly through local address expiry plus 86,400
 seconds, for at most 172,800 seconds from acceptance. Expired IDs
-are removed first. Remote IDs occupy a general pool with a per-subnet quota; if
+are removed first. Remote IDs occupy a general pool with a per-swarm quota; if
 that quota or the global general pool contains only protected IDs, the fresh ad
-is rejected before address mutation or relay. This prevents one claimed subnet
-from consuming the whole general pool, but generated subnet IDs can still cause
+is rejected before address mutation or relay. This prevents one claimed swarm
+from consuming the whole general pool, but generated swarm IDs can still cause
 distributed saturation.
 
-Authoring configuration reserves a default 32, at most 256, subnet partitions of
+Authoring configuration reserves a default 32, at most 256, swarm partitions of
 256 slots each inside the global cap. Checked multiplication/subtraction derives
 reserve and nonzero general capacities. Remote ads cannot consume a partition;
-locally authored IDs use their subnet's partition until expiry. A serving
+locally authored IDs use their swarm's partition until expiry. A serving
 transition atomically allocates/reuses a partition before listener/author start;
 stopping retains it until every protected local ID expires. Sequential churn may
 therefore return a typed capacity failure rather than overwrite protection. The
-last-ID expiry releases a stopped subnet's partition atomically; resumed serving
+last-ID expiry releases a stopped swarm's partition atomically; resumed serving
 retains it.
 
-Startup assigns persisted local IDs to partitions by distinct subnet and checks
+Startup assigns persisted local IDs to partitions by distinct swarm and checks
 each partition's 256 slots separately. Persisted general IDs are checked only
-against the remaining general capacity and per-subnet quota; local IDs already
+against the remaining general capacity and per-swarm quota; local IDs already
 inside reserve are not double-counted. The 256-slot partition exceeds the
 maximum IDs produced by the fixed 20-minute minimum cadence during the
 172,800-second maximum protection window. Protected IDs are never evicted early;
@@ -270,7 +295,7 @@ reserve partitions are validated independently before conversion.
 
 Transient stores use the same validation in bounded memory only. No store
 contains an active dialer. Active refinement is rejected because it cannot
-verify subnet attribution and creates scanning amplification.
+verify swarm attribution and creates scanning amplification.
 
 ### D6. Validate features and full version size
 
@@ -281,7 +306,7 @@ versions. Version permits at most 10 external addresses and 10 features;
 node ID is capped at 64 bytes, app name at 32, URL at 1,024, feature name at 32,
 and semver prerelease/build at 32 each. Before sending, protocol encodes and
 checks complete `VersionMessage` and `VerackMessage` against their maxima.
-Each overlay/subnet `P2p` receives a fresh CSPRNG node ID scoped to that instance;
+Each overlay/swarm `P2p` receives a fresh CSPRNG node ID scoped to that instance;
 it is not persisted or reused across networks/restarts.
 
 Inbound `VersionMessage` and `VerackMessage` use manual bounded decoders that
@@ -318,16 +343,16 @@ work rates are 32 store writes and 32 relay enqueues per 10 seconds plus
 | pages per public enumeration | 4 | 16 |
 | candidate addresses per attempt | 64 | 256 |
 | previously compatible retries | 16 | 64 |
-| persisted compatible retry URLs/subnet | 64 | 256 |
-| local-author reserve subnet partitions | 32 | 256 |
-| active subnets | 32 | 256 |
+| persisted compatible retry URLs/swarm | 64 | 256 |
+| local-author reserve swarm partitions | 32 | 256 |
+| active swarms | 32 | 256 |
 | concurrent lifecycle attempts | 8 | 32 |
 | shutdown deadline | 120 s | 600 s |
 | pending request timeout | 10 s | 60 s |
 | configured ordinary peers | 8 | 256 |
 | overlay bind addresses | 1 | 16 |
-| serving bind addresses/subnet | 1 | 16 |
-| serving external addresses/subnet | 1 | 32 |
+| serving bind addresses/swarm | 1 | 16 |
+| serving external addresses/swarm | 1 | 32 |
 | overlay inbound channels | 64 | 256 |
 | overlay outbound channels | 8 | 64 |
 | overlay manual channels | 8 | 256 |
@@ -344,7 +369,7 @@ does not claim Sybil resistance.
 
 ### D8. Bootstrap by constructing one stage at a time
 
-Swarm owns at most one running overlay candidate. A cache record is a bounded
+SwarmPool owns at most one running overlay candidate. A cache record is a bounded
 pair of the original connect URL and exact resolved endpoint used by a
 successfully completed persistent-feature channel. It never comes from
 `VersionMessage.ext_send_addr`. For a transient:
@@ -360,15 +385,15 @@ successfully completed persistent-feature channel. It never comes from
 5. Resolve/validate configured ordinary peers once, construct a fresh overlay
    candidate, install those exact targets through the same pre-start API, and
    repeat one bounded stage.
-6. Publish the successful `P2pPtr` as Swarm's active overlay only after success.
+6. Publish the successful `P2pPtr` as SwarmPool's active overlay only after success.
 
 No settings reload is used. Persistent nodes construct directly from configured
 ordinary topology. After an ordinary channel exposes `swarm-ad-store`, only its
 actual connect/resolved pair may enter the cache via atomic replacement. The
 peer's advertised external addresses are ignored for caching.
 
-Swarm does not configure a standard overlay hostlist/datastore for a
-privacy-maximal transient. Separately configured subnet and transport state is
+SwarmPool does not configure a standard overlay hostlist/datastore for a
+privacy-maximal transient. Separately configured swarm and transport state is
 outside that overlay-cache guarantee and documented.
 
 Untrusted dial paths use a new narrow target model:
@@ -419,13 +444,13 @@ attempt tries one resolved destination once. This does not prove endpoint
 ownership, but prevents local-network SSRF/DNS-rebinding and bounds public
 victim reflection.
 
-### D9. Keep overlay control channels and subnet data channels separate
+### D9. Keep overlay control channels and swarm data channels separate
 
 “Ordinary overlay session” means an inbound/outbound/manual/direct non-seed
 session on the overlay `P2p`; it does not mean every client keeps it for process
 lifetime. The channel remains bound to overlay magic/app identity, channel
 store, hosts, and `ProtocolSwarm`. Streams are never handed to another `P2p`,
-re-handshaken under subnet identity, or extended with subnet-tag multiplexing.
+re-handshaken under swarm identity, or extended with swarm-tag multiplexing.
 
 Persistent nodes retain the overlay while storing/relaying. Serving nodes retain
 it while authoring ads. Transient settings expose two policies:
@@ -436,23 +461,23 @@ ImmediateAfterOperation      // explicit reduced-privacy mode
 ```
 
 The default never reacts to lookup/join completion by disconnecting; it retains
-the overlay until an explicit `stop_overlay()` or full Swarm shutdown ends the
+the overlay until an explicit `stop_overlay()` or full SwarmPool shutdown ends the
 application session. Immediate mode deterministically stops after every
 caller-visible lookup or join reaches a terminal outcome—success, empty result,
 error, timeout, or cancellation—but not after an internal lookup phase within a
 join. Its configuration warning states that the responder and a same-operator
-subnet server may correlate query, subnet connection, and teardown timing. In
-either mode the subnet handle owns an independent `P2p` and outlives overlay
+swarm server may correlate query, swarm connection, and teardown timing. In
+either mode the swarm handle owns an independent `P2p` and outlives overlay
 stop. Later discovery runs staged bootstrap again only when no active overlay
 remains.
 
-Swarm tracks overlay lifetime separately from subnet registry lifetime.
+SwarmPool tracks overlay lifetime separately from swarm registry lifetime.
 `stop_overlay()` rejects persistent/serving duties, but for an eligible
-transient it stops only overlay tasks/channels and leaves subnet entries
-untouched. Full Swarm shutdown still stops every subnet and any active overlay.
+transient it stops only overlay tasks/channels and leaves swarm entries
+untouched. Full SwarmPool shutdown still stops every swarm and any active overlay.
 Transport reuse was rejected because it requires multiplexing or handoff,
-correlates overlay queries with subnet membership, mixes host/protocol state,
-and works only when the overlay peer also serves the subnet.
+correlates overlay queries with swarm membership, mixes host/protocol state,
+and works only when the overlay peer also serves the swarm.
 
 ### D10. Registry-owned lifecycle and explicit source attempts
 
@@ -484,7 +509,7 @@ A join attempt:
    time; verified and fresh resolution each receive half that subdeadline, so
    verified DNS/transport preparation cannot consume fresh preparation time;
 7. retains selected candidates as ephemeral `ValidatedDialTarget` values and
-   installs a two-phase verified/fresh plan through the subnet's pre-start manual-
+   installs a two-phase verified/fresh plan through the swarm's pre-start manual-
    target API, not its URL-only hostlist/refinery;
 8. starts and waits for a channel whose session flag is inbound, outbound, or
    manual; at start it snapshots remaining dial time, cancels verified targets at
@@ -534,7 +559,7 @@ visibility/lifetime
 ```
 
 Create-and-serve validates persistent role and all fields, builds `P2p` with
-listeners before start, atomically allocates/reuses the subnet's local-author
+listeners before start, atomically allocates/reuses the swarm's local-author
 reserve partition, runs the initializer, and calls `P2p::start()`. Reserve
 exhaustion fails before initializer/listener/author activity. Success requires
 listener readiness, not an existing peer, enabling a first server. Peer
@@ -557,8 +582,8 @@ warning.
 One author task uses a fixed version-one 30-minute base interval with
 independent uniformly sampled ±10-minute `OsRng` jitter; it is not configurable.
 Each emission uses a fresh 32-byte `OsRng` ad ID, configured lifetime capped at
-24 hours and defaulting to two hours, and only that subnet's external addresses.
-Multi-subnet emission order is shuffled with independent jitter.
+24 hours and defaulting to two hours, and only that swarm's external addresses.
+Multi-swarm emission order is shuffled with independent jitter.
 
 Initialization, listener readiness, peer connection, recreation, new overlay
 channel, and stop only mutate local author state. They never invoke immediate
@@ -566,7 +591,7 @@ send. Stop removes future snapshots; relayed ads expire locally.
 
 ### D13. Lilith uses ordinary persistent behavior
 
-Lilith `[overlay]` maps to normal persistent Swarm settings and strict policy. It
+Lilith `[overlay]` maps to normal persistent SwarmPool settings and strict policy. It
 may be inbound-only or have ordinary outbound peers; it never places overlay
 bootstrap into `Settings.seeds`.
 
@@ -576,7 +601,7 @@ Corrupt records decode fallibly. Malformed/unverifiable seen-ID, quota/reserve,
 or epoch state fails startup; address/index state may be quarantined/rebuilt only
 when authoritative replay/accounting remains intact. Status RPC reads only
 aggregate listener/connection/capacity/address/dedup/eviction/expiry/rejection
-counters, including aggregate per-subnet-quota and epoch-checkpoint failures. It
+counters, including aggregate per-swarm-quota and epoch-checkpoint failures. It
 never reports local-author reserve occupancy/use/transition timing and never
 walks full IDs/addresses or query mappings.
 
@@ -586,16 +611,16 @@ shutdown.
 ### D14. Scoped metadata threat model
 
 Protected properties are no stable author identity, no overlay-source-peer to
-subnet/authorship persistence, and isolated subnet state.
+swarm/authorship persistence, and isolated swarm state.
 
 Disclosed properties are requested ID to responder, connection-level query
 linkage, IDs and advertised endpoints observed/mapped by gossip/store peers,
 timing/topology evidence, public catalog, endpoint reuse, local full-ID paths
-when subnet persistence is enabled, and remote peer retention. The
+when swarm persistence is enabled, and remote peer retention. The
 ID-to-endpoint mapping is intentional rendezvous output. Separate anonymity
-circuits may reduce linkage but are not provisioned or guaranteed by Swarm.
+circuits may reduce linkage but are not provisioned or guaranteed by SwarmPool.
 
-Absolute cross-subnet unlinkability is rejected because direct query and shared
+Absolute cross-swarm unlinkability is rejected because direct query and shared
 connections make it false.
 
 ## Risks / Trade-offs
@@ -607,7 +632,7 @@ connections make it false.
   traversal, CSPRNG-shuffle bounded tiers, partition verified/fresh time and
   attempts, validate compatibility, retain app authorization/static fallback,
   make no authenticity claim.
-- **[Dedup saturation]** Protected IDs can fill capacity → Per-subnet general
+- **[Dedup saturation]** Protected IDs can fill capacity → Per-swarm general
   quotas prevent one-ID monopolization, local-author partitions preserve
   allocated local cadence, and strict global bounds reject distributed-ID floods
   without early eviction; general remote-ad availability and serving transitions
@@ -619,7 +644,7 @@ connections make it false.
   addresses → Resolve once, reject local/reserved ranges, connect the exact
   validated direct socket or configured trusted proxy socket, never locally
   resolve hidden names, reject proxy DNS bypass, enforce per-destination/rate/
-  concurrency/total budgets, and retain subnet handshake checks.
+  concurrency/total budgets, and retain swarm handshake checks.
 - **[Transport abort]** Existing transport constructors/dialers may assume
   validated configuration → Reject unaudited schemes before construction and
   require fallible no-unwind handling across every attacker-selected candidate
@@ -637,11 +662,11 @@ connections make it false.
   bounded forward progress while accepting non-snapshot omissions/additions.
 - **[Local reserve metadata]** Reserve records reveal local ephemeral authorship
   to the local database → Store no peer/stable identity and exclude reserve use,
-  occupancy, subnet labels, and timing from RPC/status/telemetry.
+  occupancy, swarm labels, and timing from RPC/status/telemetry.
 - **[Role Sybil]** Attackers claim persistent feature → Treat only as hint,
   cache multiple peers, grant no privilege.
 - **[Bootstrap concentration]** Configured peers can observe/censor → Multiple
-  peers/cache, staged deadlines, static subnet fallback.
+  peers/cache, staged deadlines, static swarm fallback.
 - **[Reconstruction cost]** Failed stages rerun P2p/app initialization → Explicit
   bounded attempts and complete cleanup; no unsupported reload semantics.
 - **[Serving downtime]** Promotion requires stop/recreate → Require serving mode
@@ -654,7 +679,7 @@ connections make it false.
 These are deliberately not `swarm` completion criteria:
 
 - **Endpoint-reuse enforcement:** A later transport-identity change may reject
-  cross-subnet external-endpoint reuse by default and require an explicit
+  cross-swarm external-endpoint reuse by default and require an explicit
   reduced-privacy override. This change only detects, warns, and documents reuse
   because independent Tor/I2P identity provisioning is unresolved.
 - **Query-peer privacy budget:** A later discovery-policy change may specify
@@ -669,7 +694,7 @@ These are deliberately not `swarm` completion criteria:
    bootstrap, and lifecycle attempts/recreation using local transports.
 3. Add lilith's optional overlay section alongside unchanged legacy sections.
 4. Run local multi-node tests for replay saturation, poisoning, no probing,
-   per-subnet quota/local reserve, monotonic-epoch restart, two-hour TTL clamp,
+   per-swarm quota/local reserve, monotonic-epoch restart, two-hour TTL clamp,
    mutation-tolerant terminal cursors, CSPRNG candidate ordering, DNS rebinding/
    local-range/reflection rejection, decoder truncation/hostile lengths, direct/
    proxy exact routing, hidden-service no-local-DNS, full candidate-pipeline no-
@@ -682,4 +707,4 @@ These are deliberately not `swarm` completion criteria:
 
 Rollback disables pilot/overlay config and returns to static seeds and legacy
 lilith. State is namespaced and removable after shutdown; existing wire and
-subnet persistence formats are unchanged.
+swarm persistence formats are unchanged.

+ 25 - 25
openspec/changes/swarm/proposal.md

@@ -1,28 +1,28 @@
 ## Why
 
 DarkFi applications currently need independently configured bootstrap peers and
-seed lists for every subnet. This makes dynamic subnet discovery, first-server
+seed lists for every swarm. This makes dynamic swarm discovery, first-server
 startup, and shared operational deployment unnecessarily difficult.
 
-Introduce one bounded rendezvous overlay that discovers subnet endpoints while
-keeping each subnet in its own ordinary `P2p` instance. Static seeds remain an
+Introduce one bounded rendezvous overlay that discovers swarm endpoints while
+keeping each swarm in its own ordinary `P2p` instance. Static seeds remain an
 explicit fallback and unchanged default during the pilot.
 
 The overlay is a metadata and availability dependency, not an anonymity,
 authentication, authorization, or access-control mechanism. It does not remove
-per-subnet handshakes, application authorization, endpoint poisoning, Sybil
+per-swarm handshakes, application authorization, endpoint poisoning, Sybil
 risk, or availability failure, and it cannot hide a lookup from its responder.
 
 ## What Changes
 
 - Add an isolated higher-level `src/net/swarm/` subsystem, exposed as
-  `darkfi::net::swarm` behind a dedicated `swarm` feature. `Swarm` owns one
-  overlay `P2p` instance at a time and manages independent subnet `P2p`
+  `darkfi::net::swarm` behind a dedicated `swarm` feature. `SwarmPool` owns one
+  overlay `P2p` instance at a time and manages independent swarm `P2p`
   instances. Existing BLAKE3, `kvdb-overlay`, serialization, and networking
   facilities are activated explicitly; no third-party dependency is added
   silently.
 
-- Define a versioned, collision-resistant `SubnetId`. Its normative encoding is
+- Define a versioned, collision-resistant `SwarmId`. Its normative encoding is
   domain-separated, length-delimited, byte-exact, and covered by a golden vector.
   It binds application name, magic bytes, and the major/minor compatibility
   boundary enforced by the current handshake. Patch/prerelease/build metadata do
@@ -55,13 +55,13 @@ risk, or availability failure, and it cannot hide a lookup from its responder.
 
 - Treat advertisements as untrusted routing hints. Address/replay storage is
   TTL-, cap-, and work-bounded. Replaying a retained global ad ID neither
-  refreshes it nor relays it again, including reuse under another subnet.
+  refreshes it nor relays it again, including reuse under another swarm.
   Protected IDs are never evicted early.
 
-- Partition replay capacity into a general pool with per-subnet quotas and a
-  fixed local-author reserve. Reserve subnet partitions default to 32, max 256,
+- Partition replay capacity into a general pool with per-swarm quotas and a
+  fixed local-author reserve. Reserve swarm partitions default to 32, max 256,
   and hold exactly 256 IDs each inside the global cap. Remote ads cannot consume
-  them. Serving allocates a partition before network activity; stopped subnets
+  them. Serving allocates a partition before network activity; stopped swarms
   retain nonempty partitions until expiry. General and reserve state are checked
   separately at startup without double-counting. Reserve occupancy, use,
   failure, and timing remain absent from wire, RPC, status, metrics, and
@@ -84,9 +84,9 @@ risk, or availability failure, and it cannot hide a lookup from its responder.
   pilot metrics, and static fallback mitigate stale hints without probing.
 
 - Perform no advertisement liveness dialing from overlay stores or Lilith.
-  `SubnetId` is one-way, so a store cannot perform the subnet handshake, and a
+  `SwarmId` is one-way, so a store cannot perform the swarm handshake, and a
   transport-only probe would create attacker-controlled scanning/reflection.
-  Only a descriptor-holding joining subnet validates returned addresses through
+  Only a descriptor-holding joining swarm validates returned addresses through
   ordinary magic, application-name, and major/minor compatibility. Compatibility
   and possession of a private ID still grant no application authorization.
 
@@ -123,28 +123,28 @@ risk, or availability failure, and it cannot hide a lookup from its responder.
   except their bounded successful-endpoint cache. The self-declared feature
   grants no validation, metering, query, or storage privilege.
 
-- Keep overlay and subnet channels structurally separate. Overlay channels remain
+- Keep overlay and swarm channels structurally separate. Overlay channels remain
   owned by the fixed overlay identity and are never transferred, re-handshaken,
-  multiplexed, or reused for subnet data. Subnet `P2p` lifetime remains
+  multiplexed, or reused for swarm data. Swarm `P2p` lifetime remains
   independent.
 
 - Persistent store/gossip and serving-advertisement duties retain the overlay.
-  The transient default retains it until explicit stop or Swarm shutdown and
+  The transient default retains it until explicit stop or SwarmPool shutdown and
   never disconnects because lookup/join completed. Explicit reduced-privacy mode
   tears down after every caller-visible terminal result—but not an internal join
   lookup—and documents timing correlation. Later discovery reconstructs an
   overlay only when none remains.
 
 - Make metadata disclosure explicit. Ads contain no stable node/signing ID,
-  author, relay provenance, or intentional cross-subnet field. Nevertheless,
+  author, relay provenance, or intentional cross-swarm field. Nevertheless,
   responders see requested IDs; requests on one channel are linkable; gossip
   peers observe ID-to-endpoint mappings; timing/topology can suggest origin; and
-  endpoint reuse links subnets. The prohibited durable mapping is source peer to
-  subnet/authorship, not the rendezvous mapping itself. No PIR or global-observer
+  endpoint reuse links swarms. The prohibited durable mapping is source peer to
+  swarm/authorship, not the rendezvous mapping itself. No PIR or global-observer
   guarantee is made.
 
 - Generate `VersionMessage.node_id` independently with a CSPRNG for every
-  overlay/subnet `P2p` instance and process lifetime; do not persist or reuse it
+  overlay/swarm `P2p` instance and process lifetime; do not persist or reuse it
   across networks. Explicit endpoint reuse remains linkable and warning-only.
 
 - Plumb a validated local feature vector into `VersionMessage` without changing
@@ -160,7 +160,7 @@ risk, or availability failure, and it cannot hide a lookup from its responder.
   or reachable unimplemented branch. Unsupported/unaudited schemes fail before
   dialer construction.
 
-- Implement registry-owned subnet attempts. Initialization constructs app state,
+- Implement registry-owned swarm attempts. Initialization constructs app state,
   protocols, and a shutdown hook before network activity. Join succeeds only on
   a compatible ordinary inbound/outbound/manual channel—not seed, direct, or
   refinement—and failure/timeout/cancellation fully rolls back.
@@ -193,7 +193,7 @@ risk, or availability failure, and it cannot hide a lookup from its responder.
 
 Deferred follow-up changes, intentionally not implemented here:
 
-- fail-closed cross-subnet endpoint-reuse policy;
+- fail-closed cross-swarm endpoint-reuse policy;
 - query-peer selection/privacy budgets.
 
 Non-goals: connection multiplexing; authenticated ads; PIR, cover traffic,
@@ -209,7 +209,7 @@ compatibility, or seed-session changes.
 - `swarm-overlay`: versioned IDs; bounded correlated messages/pagination;
   passive replay storage; roles; ordinary-peer bootstrap; exact-target dialing;
   resource limits; and privacy disclosure.
-- `subnet-lifecycle`: descriptor resolution; registry-owned app state; ordinary
+- `swarm-lifecycle`: descriptor resolution; registry-owned app state; ordinary
   join completion; bounded source fallback; isolated state; serving/recreation;
   and teardown.
 - `lilith-overlay-seed`: optional persistent ordinary overlay peer with bounded
@@ -234,10 +234,10 @@ None. No existing specifications under `openspec/specs/` are modified.
   supply-chain review.
 - **Lilith:** one optional overlay section and aggregate status; legacy sections
   remain available.
-- **Pilot:** opt-in Swarm construction and descriptor pinning with static fallback
+- **Pilot:** opt-in SwarmPool construction and descriptor pinning with static fallback
   unchanged by default.
 - **Operational cost:** one additional bounded overlay connection set/store plus
-  independent per-subnet listeners.
+  independent per-swarm listeners.
 - **Security review:** this is a privacy-sensitive shared-network change.
   Transport/privacy, persistence, descriptor hashing, and candidate fairness
   require focused human review. `@anon-security-review`, CI, and final human

+ 22 - 22
openspec/changes/swarm/specs/lilith-overlay-seed/spec.md

@@ -1,8 +1,8 @@
 ## Purpose
 
-Defines lilith as a persistent ordinary overlay peer providing bounded subnet
+Defines lilith as a persistent ordinary overlay peer providing bounded swarm
 rendezvous through one listener and durable store without joining, probing, or
-serving advertised subnets.
+serving advertised swarms.
 
 ## ADDED Requirements
 
@@ -19,7 +19,7 @@ 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-subnet listener, descriptor, magic, datastore, or
+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.
 
@@ -39,13 +39,13 @@ lilith join or serve it.
 - **THEN** startup fails before overlay activity without panic or unbounded
   fallback
 
-### Requirement: Unknown subnets need no reconfiguration
+### 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 subnet listeners or app protocols.
+IDs MUST NOT create swarm listeners or app protocols.
 
-#### Scenario: Fresh subnet ad
+#### Scenario: Fresh swarm ad
 
 - **WHEN** a valid unknown-ID ad arrives
 - **THEN** it becomes available to bounded lookup without restart or operator
@@ -53,16 +53,16 @@ IDs MUST NOT create subnet listeners or app protocols.
 
 #### Scenario: Rendezvous-only learning
 
-- **WHEN** many subnet IDs are learned
-- **THEN** lilith still runs one overlay and no subnet instance
+- **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-subnet address records with their current
+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 subnet, 65,536 total addresses, 1,024
-general-pool protected IDs per subnet, and 262,144 protected IDs globally.
+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.
@@ -82,12 +82,12 @@ 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 subnet ads and SHALL
-configure zero local-author reserve partitions. If a subnet quota or its global
+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/
-subnet associations, or private secrets. Replay ID-to-advertised-subnet binding
+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.
@@ -120,11 +120,11 @@ relay.
 - **THEN** each receives its conservative checkpointed remainder on a new
   monotonic epoch rather than a fresh full horizon
 
-#### Scenario: One subnet reaches its replay quota
+#### Scenario: One swarm reaches its replay quota
 
-- **WHEN** one claimed subnet consumes all of its unexpired general-pool slots
+- **WHEN** one claimed swarm consumes all of its unexpired general-pool slots
 - **THEN** lilith rejects another fresh ad for it without consuming other
-  subnet capacity
+  swarm capacity
 
 #### Scenario: Store rollback loses accepted ID
 
@@ -144,11 +144,11 @@ relay.
 
 ### Requirement: Lilith performs no advertisement liveness dialing
 
-Lilith MUST NOT connect to an advertised subnet address due to accepting,
+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 subnet compatibility; only a descriptor-holding joining app can
-perform the subnet handshake.
+reachability as swarm compatibility; only a descriptor-holding joining app can
+perform the swarm handshake.
 
 #### Scenario: Attacker-selected address
 
@@ -206,7 +206,7 @@ policy.
 
 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 subnet IDs,
+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.
 
@@ -214,7 +214,7 @@ Public enumeration, if enabled, remains the bounded overlay protocol.
 
 - **WHEN** status is requested
 - **THEN** aggregate health/capacity/count metrics are returned without peer or
-  subnet-query identifiers
+  swarm-query identifiers
 
 #### Scenario: Querier data is absent
 

+ 0 - 394
openspec/changes/swarm/specs/subnet-lifecycle/spec.md

@@ -1,394 +0,0 @@
-## Purpose
-
-Defines bounded, failure-safe application lifecycle behavior for resolving,
-initializing, joining, creating, serving, recreating, and stopping isolated
-swarm-managed subnets.
-
-## ADDED Requirements
-
-### Requirement: Descriptor-based resolution uses untrusted candidates
-
-An application SHALL request a subnet using a valid descriptor, not a raw ID.
-The swarm SHALL derive the normative ID and request only addresses for it.
-Overlay results SHALL enter only an ephemeral typed unverified-target set for
-that subnet, preserving original URL and validated socket until compatibility.
-They MUST NOT enter URL-only persistent host/refinery state before success.
-Overlay and subnet connections MUST remain separate. Every subnet peer MUST
-independently pass magic-byte, application-name, and major/minor checks.
-An overlay channel MUST NOT be reused, transferred, re-handshaken, or
-multiplexed for subnet traffic. Default transient policy retains the overlay for
-the application session; immediate post-lookup/join stop requires explicit
-reduced-privacy policy and occurs after every caller-visible terminal outcome,
-not an internal lookup phase within join. Any overlay stop MUST NOT stop the
-independent subnet.
-
-Resolution and connection phases SHALL have finite deadlines. A complete join
-deadline MUST be configurable and no greater than 900 seconds.
-Every untrusted candidate SHALL pass the overlay's resolution-time egress
-policy before connection and consume the configured resolution/dial concurrency,
-rate, per-destination, and total-candidate budgets. Rejected destinations SHALL
-remain fallible candidate failures and MUST NOT trigger a second resolution in
-the connector. Direct candidates SHALL retain the exact validated destination
-socket. Tor/I2P candidates SHALL retain the exact trusted locally configured
-proxy socket plus canonical hidden-service destination, with no local DNS or
-advertisement-selected proxy. Every accepted transport path SHALL reject
-malformed/unsupported input without unwind before or during compatibility.
-
-Candidate ordering SHALL use two independently `OsRng`-shuffled bounded tiers:
-at most 256 URLs from the bounded previously-compatible retry index and a
-bounded reservoir sampled across the fresh terminal traversal through completion
-or its fixed 16-page cap.
-Persisted peers MUST be re-resolved and revalidated. After both tiers exist, the
-candidate-preparation subdeadline SHALL use at most half the then-remaining
-overall time and split resolution/validation time equally between tiers; unused
-persisted time MAY pass to fresh, not conversely. A two-phase pre-start target
-plan SHALL then split remaining dial time at a monotonic midpoint: persisted
-targets stop/cancel by it and consume no more than their configured limit or half
-the attempt budget; fresh targets activate for the second half. URL, wire, store,
-hash, DNS-answer, and lexical order MUST NOT choose either attempted prefix.
-Fresh candidates MUST NOT be persisted before compatibility succeeds.
-
-#### Scenario: Descriptor-only resolution
-
-- **WHEN** lookup returns a compatible reachable peer
-- **THEN** the swarm attempts it through a distinct subnet connection
-
-#### Scenario: Wrong-subnet candidate
-
-- **WHEN** a candidate fails any compatibility field
-- **THEN** it remains unverified and failure is not attributed to the relay
-
-#### Scenario: Unknown subnet
-
-- **WHEN** no selected source yields a compatible ordinary peer
-- **THEN** join fails within its deadline and leaves no attempt running
-
-#### Scenario: Candidate resolves to prohibited destination
-
-- **WHEN** an advertised clearnet candidate resolves to loopback/private/
-  reserved space outside explicit local-test mode
-- **THEN** it is rejected before dialing and join continues within its budgets
-
-#### Scenario: Hidden-service candidate enters subnet connector
-
-- **WHEN** a canonical Tor/I2P candidate is selected for connection
-- **THEN** the connector uses the trusted configured proxy socket, passes the
-  hidden name only in proxy/TLS protocol, and has no direct fallback
-
-#### Scenario: Stored ordering is adversarial
-
-- **WHEN** returned URLs or persisted peers are arranged to control lexical or
-  insertion order
-- **THEN** bounded reservoir selection/CSPRNG shuffles choose attempted prefixes
-  and the verified tier cannot consume the fresh tier's reserved time or budget
-
-#### Scenario: Default join completion retains overlay
-
-- **WHEN** a default-policy transient completes an ordinary subnet join
-- **THEN** join completion does not itself stop the overlay
-
-#### Scenario: Explicit immediate overlay stop after join
-
-- **WHEN** reduced-privacy policy observes successful, failed, timed-out, or
-  cancelled join completion
-- **THEN** subnet network/app state continue and the caller was warned about
-  timing correlation
-
-### Requirement: Fallible initialization precedes network activity
-
-Before each subnet attempt starts, the swarm SHALL invoke a fallible app
-initializer with that attempt's `P2p` handle. It SHALL construct subnet-scoped
-app state, register protocols, and return both caller-visible state and a
-bounded shutdown hook. No listener, connection, or protocol job SHALL start
-before success.
-
-The registry SHALL retain ownership of the returned app state and shutdown hook
-for the active attempt's entire lifetime; dropping the caller handle MUST NOT
-drop required state. If initialization fails or is cancelled, partial state is
-released, nothing starts, and the original error is returned.
-
-#### Scenario: App state precedes connection
-
-- **WHEN** initialization succeeds
-- **THEN** protocols and registry-owned app state exist before network start
-
-#### Scenario: Caller drops handle
-
-- **WHEN** a caller drops its returned app handle while the subnet remains
-  active
-- **THEN** registry ownership keeps required app state alive
-
-#### Scenario: Initializer fails
-
-- **WHEN** initialization returns an error
-- **THEN** no network activity starts and partial state is released
-
-### Requirement: Join completion requires an ordinary persistent channel
-
-`P2p::start()` alone SHALL NOT complete join. Join succeeds only after an
-ordinary inbound, outbound, or manual subnet channel passes the compatibility
-handshake and remains registered as an ordinary peer. Temporary direct, seed,
-and refinement channels MUST NOT complete join, even when their handshake
-succeeds.
-
-Until then the registry SHALL expose a distinct joining state. Timeout,
-cancellation, or candidate exhaustion SHALL stop tasks and connections, run
-the shutdown hook, remove the attempt, and return a typed error without
-panicking on untrusted input.
-
-#### Scenario: Seed channel does not complete join
-
-- **WHEN** seed discovery completes but no ordinary channel exists
-- **THEN** join remains pending
-
-#### Scenario: Refinement channel does not complete join
-
-- **WHEN** a refinement probe succeeds but no ordinary channel exists
-- **THEN** join remains pending
-
-#### Scenario: Ordinary channel completes join
-
-- **WHEN** a compatible ordinary channel completes before deadline
-- **THEN** state atomically becomes joined
-
-#### Scenario: Failed join rolls back
-
-- **WHEN** deadline, cancellation, or failure ends an attempt
-- **THEN** its state is removed without disturbing overlay or other subnets
-
-### Requirement: Source policies use explicit bounded attempts
-
-Applications SHALL select overlay-only, static-only, combined, or
-overlay-then-static behavior. Sources MUST NOT activate implicitly outside the
-selected policy.
-
-- Overlay-only SHALL use overlay candidates with static seeds absent.
-- Static-only SHALL use configured subnet seed discovery with overlay lookup
-  absent.
-- Combined MAY configure both sources in one attempt.
-- Overlay-then-static SHALL complete and tear down one bounded overlay-only
-  attempt before creating a fresh static-only `P2p` attempt. It SHALL rerun the
-  initializer for the fresh attempt and remain inside one overall deadline.
-
-No policy SHALL rely on runtime manual- or seed-session reload. Every source
-uses the same compatibility and ordinary-channel completion rule.
-
-#### Scenario: Overlay-first succeeds
-
-- **WHEN** an ordinary overlay-discovered peer completes in the first attempt
-- **THEN** no static attempt starts
-
-#### Scenario: Overlay-first fails and static succeeds
-
-- **WHEN** the first attempt fully rolls back under overlay-then-static
-- **THEN** a fresh static-configured attempt reruns initialization and may join
-
-#### Scenario: First-attempt state does not leak
-
-- **WHEN** overlay-then-static creates its second attempt
-- **THEN** no task, channel, app state, or registry ownership from the first
-  attempt remains
-
-#### Scenario: Every source fails
-
-- **WHEN** all policy attempts fail within the overall deadline
-- **THEN** a typed aggregate failure is returned after complete rollback
-
-### Requirement: Per-subnet state remains isolated
-
-Each subnet SHALL have independent hostlists, refinement, app state, tasks,
-datastore, and hostlist files. Persistent paths SHALL be namespaced by full ID
-under a configured root. No state, address outcome, dispatch, or shutdown signal
-may cross subnets. Deleting one subnet MUST NOT alter another.
-
-Overlay role persistence rules apply only to overlay-owned state. Applications
-MAY separately configure subnet persistence for a transient overlay participant;
-such paths reveal local subnet history and MUST be documented. Private secrets
-MUST NOT be logged.
-
-#### Scenario: Independent refinement
-
-- **WHEN** one address has different outcomes in A and B
-- **THEN** each subnet retains only its own outcome
-
-#### Scenario: Namespaced persistence
-
-- **WHEN** two subnets persist under one root
-- **THEN** their files occupy distinct full-ID paths
-
-#### Scenario: Transient persists a subnet explicitly
-
-- **WHEN** a transient overlay caller enables subnet persistence
-- **THEN** only that separately configured subnet state is written and its
-  local-history implication is documented
-
-### Requirement: Serving mode is selected before initial start
-
-A subnet SHALL be created either join-only or serving. Serving creation SHALL
-require a persistent overlay role, at least one local listener bind address,
-and at least one externally advertised endpoint assigned to that subnet. Bind
-addresses and advertised endpoints SHALL be separate fields and MUST NOT be
-assumed identical. All addresses SHALL be validated before network start.
-
-Serving initialization SHALL configure listeners before `P2p::start()`. It
-completes when app initialization succeeds and required listeners bind; it does
-not require an existing peer. This permits the first member of a new subnet to
-serve. It may attempt peer discovery afterward under an explicit source policy.
-A transient SHALL reject serving before any network activity.
-
-Before initializer, listener, or author activation, serving SHALL atomically
-allocate or reuse one 256-slot local-author reserve partition for the subnet.
-If all configured partitions are occupied by subnets with protected local IDs,
-serving SHALL fail with a typed capacity error. A newly allocated empty partition
-SHALL be released on pre-authoring initialization/bind failure. Stopping serving
-SHALL retain a nonempty partition until all protected local IDs expire, then
-release it only if the subnet has not resumed serving.
-
-#### Scenario: First serving member
-
-- **WHEN** no subnet peer exists but a persistent caller supplies valid bind
-  and advertised addresses
-- **THEN** the serving subnet succeeds after listener readiness without a peer
-  handshake
-
-#### Scenario: Bind and advertised endpoint differ
-
-- **WHEN** an externally provisioned endpoint forwards to a distinct local bind
-  address
-- **THEN** the listener binds locally while ads contain only the external
-  endpoint
-
-#### Scenario: Transient serving is rejected
-
-- **WHEN** a transient requests serving
-- **THEN** failure occurs before initializer, listener, or author task starts
-
-#### Scenario: Author reserve is exhausted
-
-- **WHEN** a persistent caller requests serving while every reserve partition is
-  retained by protected local IDs for other subnets
-- **THEN** failure occurs before initializer, listener, or author task starts
-
-### Requirement: Serving promotion uses controlled recreation
-
-An already started join-only subnet MUST NOT be promoted by mutating inbound
-settings and calling session reload. Promotion SHALL require a controlled
-stop/recreate operation: stop the joined instance completely, then create a new
-serving-configured instance and rerun initialization. The operation SHALL be
-serialized with other lifecycle actions and return a typed result if recreation
-fails. Retained state MAY be reused only from that subnet's namespace.
-
-Listener binds and externally advertised endpoints MUST remain distinct.
-Swarm SHALL NOT claim automatic Tor or I2P identity provisioning. Reusing one
-advertised endpoint across local subnets SHALL produce an explicit linkability
-warning.
-
-#### Scenario: Promotion does not use reload
-
-- **WHEN** a joined subnet is promoted to serving
-- **THEN** its old instance fully stops before a serving-configured instance
-  starts
-
-#### Scenario: Recreation fails
-
-- **WHEN** the serving listener cannot bind or initialization fails
-- **THEN** no partial serving instance or author task remains
-
-#### Scenario: Shared endpoint warning
-
-- **WHEN** one external endpoint is assigned to two served subnets
-- **THEN** an explicit local linkability warning is produced
-
-### Requirement: Advertisement authoring is serving-only and cadence-only
-
-Join-only subnets SHALL author no ads. A successfully serving subnet SHALL
-author bounded ads only on the overlay jittered cadence and only with its
-advertised endpoints. Initialization, listener readiness, peer connection,
-recreation, and new overlay channels MUST NOT emit immediately. Stopping
-serving SHALL cease future authoring without a withdrawal.
-
-#### Scenario: Join-only remains silent
-
-- **WHEN** a subnet is joined without serving mode
-- **THEN** it authors no ad
-
-#### Scenario: First server waits
-
-- **WHEN** a first serving member becomes listener-ready
-- **THEN** its first ad waits for the next cadence tick
-
-#### Scenario: Stop is silent
-
-- **WHEN** a serving subnet stops
-- **THEN** no departure message is sent and existing ads expire locally
-
-### Requirement: Leave and recreation are idempotent and failure-safe
-
-Leave SHALL disable authoring, stop network producers/channels/protocol jobs,
-run app shutdown, and remove registry ownership. It MUST emit no withdrawal.
-Repeated leave SHALL be idempotent. Same-subnet join, serve, recreate, leave,
-and delete operations SHALL have one serialized owner.
-
-The caller SHALL choose to retain or delete namespaced state after shutdown.
-Deletion MUST occur only after stop and affect only that ID.
-
-#### Scenario: Repeated leave
-
-- **WHEN** leave is called after stop
-- **THEN** it returns the already-stopped result without restarting work
-
-#### Scenario: Retained rejoin
-
-- **WHEN** state is retained
-- **THEN** a later attempt may reuse only that subnet's files
-
-#### Scenario: Delete after stop
-
-- **WHEN** deletion is selected
-- **THEN** only that namespace is deleted after shutdown
-
-### Requirement: Concurrent lifecycle remains isolated
-
-Different subnets MAY transition concurrently; one ID SHALL have one owner.
-Duplicate joins MUST NOT create duplicate instances. Swarm shutdown SHALL stop
-authoring, cancel attempts, stop every subnet despite individual failures, and
-stop the overlay last within a finite deadline.
-
-#### Scenario: Duplicate concurrent join
-
-- **WHEN** two callers request the same descriptor
-- **THEN** at most one instance is created and deterministic state is returned
-
-#### Scenario: Late subnet operation
-
-- **WHEN** a new join or serving creation starts while others run
-- **THEN** existing subnet and overlay operation is not restarted
-
-#### Scenario: One shutdown hook fails
-
-- **WHEN** one app shutdown hook returns an error
-- **THEN** every other subnet still receives a stop attempt and failures are
-  aggregated without panic
-
-### Requirement: Pinned and private descriptors share lifecycle rules
-
-Applications SHALL pin public descriptors with golden IDs and accept valid
-private descriptors. Private secrets MUST NOT be logged, advertised, sent in
-lookup, or exposed by public status; overlay messages use only the derived ID.
-Possession of the descriptor SHALL NOT bypass compatibility or app-level
-authorization.
-
-#### Scenario: Pinned interoperability
-
-- **WHEN** deployments use one pinned descriptor
-- **THEN** they derive one ID and apply the same compatibility checks
-
-#### Scenario: Private descriptor is not transmitted
-
-- **WHEN** a private subnet is resolved or created
-- **THEN** no overlay message contains its secret
-
-#### Scenario: Private ID grants no access
-
-- **WHEN** a peer knows an ID but fails application authorization
-- **THEN** swarm grants no authorization

+ 97 - 97
openspec/changes/swarm/specs/swarm-overlay/spec.md

@@ -1,18 +1,18 @@
 ## Purpose
 
-Defines a bounded rendezvous overlay that resolves known subnet descriptors to
+Defines a bounded rendezvous overlay that resolves known swarm descriptors to
 untrusted peer addresses while making bootstrap, replay, resource, persistence,
 and metadata-disclosure boundaries explicit.
 
 ## ADDED Requirements
 
-### Requirement: Versioned subnet identifier derivation
+### Requirement: Versioned swarm identifier derivation
 
-A version-1 descriptor SHALL bind the subnet application name, magic bytes,
+A version-1 descriptor SHALL bind the swarm application name, magic bytes,
 and major/minor version pair used by the existing compatibility handshake. Its
 canonical bytes SHALL be, in order:
 
-1. ASCII `darkfi-swarm-subnet-v1` followed by one zero byte;
+1. ASCII `darkfi-swarm-id-v1` followed by one zero byte;
 2. application-name UTF-8 byte length as unsigned 16-bit big-endian, followed
    by those exact bytes without Unicode normalization;
 3. the four magic bytes;
@@ -21,7 +21,7 @@ canonical bytes SHALL be, in order:
 6. for a private descriptor only, exactly 32 secret bytes.
 
 Application names MUST contain 1 through 32 UTF-8 bytes. Other private-secret
-lengths and flag values MUST be rejected. `SubnetId` SHALL be the 32-byte
+lengths and flag values MUST be rejected. `SwarmId` SHALL be the 32-byte
 BLAKE3 hash of the canonical bytes. Implementations SHALL rely on collision
 resistance rather than claim collision is impossible. Patch, prerelease, and
 build metadata SHALL NOT affect the ID.
@@ -29,11 +29,11 @@ build metadata SHALL NOT affect the ID.
 For public `darkirc`, magic `fb e5 c7 b5`, and compatibility pair `0.5`, the
 canonical bytes SHALL be:
 
-`6461726b66692d737761726d2d7375626e65742d76310000076461726b697263fbe5c7b50000000000000000000000000000000500`
+`6461726b66692d737761726d2d69642d76310000076461726b697263fbe5c7b50000000000000000000000000000000500`
 
-and `SubnetId` SHALL be:
+and `SwarmId` SHALL be:
 
-`b4c9d83b53cc7473d26bf173a9abd5e3025957141e20779960d587eec88618ed`.
+`cad1d01807849a400541725bd75af14e53d259392082c14fa96ce6313551feb4`.
 
 #### Scenario: Golden public identifier
 
@@ -103,7 +103,7 @@ MUST contain no more than 256 endpoint records, 262,144 encoded file bytes, or
 connect URL and resolved endpoint actually used by a successfully completed
 ordinary channel advertising `("swarm-ad-store", 1)`. Advertised external
 addresses MUST NOT be cached from that feature handshake. The cache MUST NOT
-contain features, subnet IDs, ads, queries, or source/query associations.
+contain features, swarm IDs, ads, queries, or source/query associations.
 
 #### Scenario: Cached stage succeeds
 
@@ -121,7 +121,7 @@ contain features, subnet IDs, ads, queries, or source/query associations.
 - **WHEN** a fresh configured-peer stage establishes an ordinary channel
 - **THEN** lookup can run on that channel without a seed session
 
-#### Scenario: Cache contains no subnet activity
+#### Scenario: Cache contains no swarm activity
 
 - **WHEN** lookup sessions persist the swarm cache
 - **THEN** it contains successful connect/resolved endpoint pairs only
@@ -134,7 +134,7 @@ contain features, subnet IDs, ads, queries, or source/query associations.
 
 ### Requirement: Untrusted dials use validated exact targets
 
-Before dialing a fresh advertised or configured untrusted target, the swarm
+Before dialing a fresh advertised or configured untrusted target, the joiner
 SHALL resolve clearnet names once and validate the selected socket address. A
 cached target SHALL instead revalidate and reuse its stored successfully
 connected socket without DNS resolution. Unless explicit local-test mode is
@@ -167,7 +167,7 @@ proxy route SHALL revalidate that its exact socket still matches current trusted
 local proxy configuration; mismatch fails without DNS or fallback.
 
 An advertised candidate SHALL remain a typed pair of original URL and validated
-resolved socket through the subnet connection attempt. Before compatibility
+resolved socket through the swarm connection attempt. Before compatibility
 succeeds it MUST NOT be downgraded into a URL-only hostlist, persisted, or sent
 through a connector/refinery path that resolves it again. Failed candidates
 SHALL be dropped. After compatibility succeeds, ordinary host persistence MAY
@@ -217,9 +217,9 @@ resolve, validate, budget, and dial one exact socket again.
   proxy matching its transport
 - **THEN** it fails before dialer construction without direct-network fallback
 
-#### Scenario: Candidate crosses into subnet connector
+#### Scenario: Candidate crosses into swarm connector
 
-- **WHEN** a validated advertised candidate is handed to the subnet connection
+- **WHEN** a validated advertised candidate is handed to the swarm connection
   attempt
 - **THEN** its original URL and exact socket remain typed until compatibility
   succeeds, with no second DNS lookup or URL-only greylist insertion
@@ -271,46 +271,46 @@ the join budget.
   transport path
 - **THEN** the candidate is rejected before transport construction or dialing
 
-### Requirement: Overlay control channels are never subnet data channels
+### Requirement: Overlay control channels are never swarm data channels
 
 An overlay channel SHALL remain owned by the overlay `P2p` identified by fixed
 swarm magic bytes, app name, version policy, host state, and protocol registry.
-It MUST NOT carry subnet application messages, be transferred to a subnet
-`P2p`, be re-handshaken in place under a subnet identity, or multiplex traffic
-tagged by subnet ID. Joining SHALL create a separate subnet channel under that
-subnet's own identity and state.
+It MUST NOT carry swarm application messages, be transferred to a swarm
+`P2p`, be re-handshaken in place under a swarm identity, or multiplex traffic
+tagged by swarm ID. Joining SHALL create a separate swarm channel under that
+swarm's own identity and state.
 
 A persistent participant SHALL keep its overlay active while it performs
-durable store or gossip duties. A participant serving any subnet SHALL keep the
+durable store or gossip duties. A participant serving any swarm SHALL keep the
 overlay active while advertisement authoring is enabled. The default transient
 policy SHALL retain the overlay for the application session and MUST NOT stop it
 as an automatic reaction to lookup or join completion. The application session
-ends only through explicit overlay stop or full Swarm shutdown. An explicit
+ends only through explicit overlay stop or full SwarmPool shutdown. An explicit
 reduced-privacy policy SHALL stop after every caller-visible lookup/join terminal
 outcome—success, empty result, error, timeout, or cancellation—but MUST NOT stop
 after an internal lookup phase within join. Configuration and documentation
-MUST warn that responder/subnet observers can correlate the query, subnet
+MUST warn that responder/swarm observers can correlate the query, swarm
 connection, and teardown timing. Stopping the overlay MUST NOT stop or transfer
-the independent subnet `P2p`; a later lookup establishes a new overlay session
+the independent swarm `P2p`; a later lookup establishes a new overlay session
 only when no active overlay remains.
 
-#### Scenario: Overlay peer also serves requested subnet
+#### Scenario: Overlay peer also serves requested swarm
 
 - **WHEN** the lookup responder also operates a serving endpoint for the
-  requested subnet
-- **THEN** the client opens a separate subnet connection rather than reusing the
+  requested swarm
+- **THEN** the client opens a separate swarm connection rather than reusing the
   overlay channel
 
 #### Scenario: Default transient join does not trigger disconnect
 
-- **WHEN** a default-policy transient completes lookup or subnet join
+- **WHEN** a default-policy transient completes lookup or swarm join
 - **THEN** join completion itself does not stop the overlay
 
 #### Scenario: Explicit reduced-privacy teardown
 
 - **WHEN** a caller selects immediate teardown and lookup or join reaches any
   terminal outcome
-- **THEN** the overlay stops without stopping the subnet and configuration/docs
+- **THEN** the overlay stops without stopping the swarm and configuration/docs
   flag the timing-correlation risk
 
 #### Scenario: Join's internal lookup completes
@@ -338,11 +338,11 @@ Protocol hard limits SHALL be:
 
 | Message | Maximum encoded bytes |
 |---|---:|
-| `SubnetAd` | 65,536 |
-| `GetSubnetAddrs` | 128 |
-| `SubnetAddrs` | 65,536 |
-| `GetPublicSubnets` | 128 |
-| `PublicSubnets` | 16,384 |
+| `SwarmAd` | 65,536 |
+| `GetSwarmAddrs` | 128 |
+| `SwarmAddrs` | 65,536 |
+| `GetPublicSwarms` | 128 |
+| `PublicSwarms` | 16,384 |
 | `SwarmError` | 128 |
 
 Every encoded URL MUST be no more than 1,024 bytes. A page cursor SHALL be a
@@ -354,12 +354,12 @@ Message command strings and canonical field order SHALL be:
 
 | Command | Fields in encoded order |
 |---|---|
-| `swarm.ad` | `SubnetId[32]`, visibility `u8`, ad ID `[32]`, lifetime `u32`, URL vector |
-| `swarm.geta` | request ID `[16]`, `SubnetId[32]`, optional cursor |
-| `swarm.addrs` | request ID `[16]`, `SubnetId[32]`, URL vector, optional cursor |
-| `swarm.gets` | request ID `[16]`, optional cursor |
-| `swarm.subs` | request ID `[16]`, `SubnetId` vector, optional cursor |
-| `swarm.err` | request ID `[16]`, error code `u8` |
+| `ad` | `SwarmId[32]`, visibility `u8`, ad ID `[32]`, lifetime `u32`, URL vector |
+| `getaddr` | request ID `[16]`, `SwarmId[32]`, optional cursor |
+| `addrs` | request ID `[16]`, `SwarmId[32]`, URL vector, optional cursor |
+| `getswarm` | request ID `[16]`, optional cursor |
+| `swarms` | request ID `[16]`, `SwarmId` vector, optional cursor |
+| `err` | request ID `[16]`, error code `u8` |
 
 Fields SHALL use existing DarkFi canonical wire encoding. Visibility values
 SHALL be `0 = public` and `1 = non-public`; other values are invalid. Error
@@ -402,18 +402,18 @@ MUST precede allocation and element decoding.
 
 ### Requirement: Advertisement format is bounded and nonempty
 
-An ad SHALL contain exactly one subnet ID, public/non-public visibility, a
+An ad SHALL contain exactly one swarm ID, public/non-public visibility, a
 fresh 32-byte per-ad ID, lifetime seconds, and 1 through 32 serving addresses.
 The ad ID MUST use a cryptographically secure random source and MUST NOT be
-reused for another emission or subnet. It is deduplication data, not identity.
+reused for another emission or swarm. It is deduplication data, not identity.
 
 Lifetime MUST be 1 through 86,400 seconds. Ads MUST NOT contain `last_seen`, a
 stable node ID, signing key, author, relay provenance, hop count, or identifier
-shared with another subnet. Every address MUST be valid, publicly shareable,
+shared with another swarm. Every address MUST be valid, publicly shareable,
 and within the URL bound. A receiver SHALL reject the entire ad if any field or
 address is invalid.
 
-#### Scenario: Valid ad is subnet-scoped
+#### Scenario: Valid ad is swarm-scoped
 
 - **WHEN** a valid ad for S is accepted
 - **THEN** it contains only S, S's addresses, and a unique ephemeral ad ID
@@ -430,15 +430,15 @@ address is invalid.
 
 ### Requirement: Expiry, replay suppression, and storage remain bounded
 
-A persistent participant SHALL use finite nonzero per-subnet, global-address,
-per-subnet protected-ID, and global protected-ID caps. Configured values MUST NOT
-exceed 1,024 addresses per subnet, 65,536 total addresses, 1,024 protected IDs
-per subnet in the general pool, or 262,144 protected IDs globally. Runtime
+A persistent participant SHALL use finite nonzero per-swarm, global-address,
+per-swarm protected-ID, and global protected-ID caps. Configured values MUST NOT
+exceed 1,024 addresses per swarm, 65,536 total addresses, 1,024 protected IDs
+per swarm in the general pool, or 262,144 protected IDs globally. Runtime
 expiry SHALL use a monotonic deadline from local receipt and sender clocks SHALL
 have no effect.
 
-Those caps SHALL default respectively to 256 addresses per subnet, 16,384 total
-addresses, 256 general protected IDs per subnet, and 65,536 protected IDs
+Those caps SHALL default respectively to 256 addresses per swarm, 16,384 total
+addresses, 256 general protected IDs per swarm, and 65,536 protected IDs
 globally.
 
 The receiver SHALL clamp each accepted address lifetime to the lesser of the
@@ -450,23 +450,23 @@ Receiving a retained ad ID MUST NOT extend expiry or repeat relay work. The
 dedup deadline SHALL be exactly 86,400 seconds after the associated locally
 clamped address expiry, making total protection no greater than 172,800 seconds
 from acceptance. A protected ID MUST NOT be evicted before that deadline. A fresh remote
-ad SHALL be rejected without address mutation or relay when its subnet's
+ad SHALL be rejected without address mutation or relay when its swarm's
 general-pool quota or the global general pool has no expired slot. Expired IDs
 may be evicted deterministically.
 
-Local-author reserve-subnet partitions SHALL default to 32 and MUST NOT exceed
+Local-author reserve-swarm partitions SHALL default to 32 and MUST NOT exceed
 256. Each partition contains exactly 256 protected-ID slots and counts within the
 global cap; checked configuration arithmetic SHALL require a nonzero remaining
 general pool. Remote ads MUST NOT consume reserve partitions. A serving
-transition SHALL atomically allocate/reuse one partition for its subnet before
+transition SHALL atomically allocate/reuse one partition for its swarm before
 listener or author activation and fail with a typed capacity error when none is
 available. Stopping service SHALL retain that partition until all its protected
-local IDs expire, then release it atomically when that subnet is not serving, so
-sequential subnet churn cannot overwrite protection.
+local IDs expire, then release it atomically when that swarm is not serving, so
+sequential swarm churn cannot overwrite protection.
 
 Startup SHALL validate the partitions independently: general protected IDs MUST
-fit the global general capacity and each general per-subnet quota; local IDs MUST
-fit 256 slots for each distinct reserved subnet and the configured partition
+fit the global general capacity and each general per-swarm quota; local IDs MUST
+fit 256 slots for each distinct reserved swarm and the configured partition
 count. Persisted local IDs already occupy their reserve and MUST NOT be counted
 again as general state. Locally authored IDs MAY NOT evict protected IDs. The
 reserve prevents remote admission from blocking allocated local cadence, but
@@ -475,9 +475,9 @@ does not provide preferential validation or remote role privilege.
 Fresh IDs may refresh addresses subject to caps. Address eviction SHALL choose
 expired entries first, then earliest expiry, then lexical key. Stores MUST NOT
 dial advertised addresses or record ad sources, queriers, query history, or
-source-peer/subnet associations. Replay IDs SHALL remain globally keyed; each
-record SHALL bind its advertised subnet only for quota/protection accounting, so
-reuse of one ad ID under another subnet is still a duplicate. A local-author
+source-peer/swarm associations. Replay IDs SHALL remain globally keyed; each
+record SHALL bind its advertised swarm only for quota/protection accounting, so
+reuse of one ad ID under another swarm is still a duplicate. A local-author
 reserve record necessarily marks an ID as generated by this process; that local
 fact and reserve occupancy/use/failure/timing MUST NOT enter wire messages,
 RPC, status, metrics, telemetry, or peer-linked state, even as aggregate counters.
@@ -489,7 +489,7 @@ enqueued. Commit failure SHALL cause no mutation or relay. Rollback of the
 database to a snapshot before that commit can remove the seen ID and permit a
 later replay; this capability makes no non-rollbackable replay guarantee.
 
-Store state SHALL be normalized per `(SubnetId, canonical address)`. Accepting
+Store state SHALL be normalized per `(SwarmId, canonical address)`. Accepting
 a fresh ad updates visibility and expiry for every address present in that ad;
 addresses absent from it retain their current record until independently
 updated, expired, or evicted. An ID is publicly enumerable iff at least one
@@ -532,10 +532,10 @@ be rebuilt only when authoritative replay/accounting state remains intact.
 - **WHEN** a retained ad ID is replayed
 - **THEN** original expiry remains and no second relay occurs
 
-#### Scenario: Ad ID is reused for another subnet
+#### Scenario: Ad ID is reused for another swarm
 
-- **WHEN** a retained global ad ID appears with a different subnet ID
-- **THEN** it remains a duplicate and does not consume that subnet's quota or
+- **WHEN** a retained global ad ID appears with a different swarm ID
+- **THEN** it remains a duplicate and does not consume that swarm's quota or
   mutate/relay addresses
 
 #### Scenario: Protected dedup set is full
@@ -543,23 +543,23 @@ be rebuilt only when authoritative replay/accounting state remains intact.
 - **WHEN** a fresh ad arrives while every dedup slot is protected
 - **THEN** the fresh ad is rejected instead of evicting a protected ID
 
-#### Scenario: One subnet fills its protected-ID quota
+#### Scenario: One swarm fills its protected-ID quota
 
-- **WHEN** fresh remote ads for one subnet consume every unexpired slot in that
-  subnet's general quota
-- **THEN** another fresh ad for that subnet is rejected without consuming slots
-  reserved for other subnets or local authoring
+- **WHEN** fresh remote ads for one swarm consume every unexpired slot in that
+  swarm's general quota
+- **THEN** another fresh ad for that swarm is rejected without consuming slots
+  reserved for other swarms or local authoring
 
 #### Scenario: Remote flood reaches the local-author reserve
 
 - **WHEN** the remote general pool is full while local authoring remains active
-- **THEN** a locally authored ad may use its subnet reserve and no remote ad may
+- **THEN** a locally authored ad may use its swarm reserve and no remote ad may
   consume that slot
 
 #### Scenario: Sequential serving exhausts reserve partitions
 
-- **WHEN** stopped subnets with protected local IDs occupy every configured
-  reserve partition and another subnet requests serving
+- **WHEN** stopped swarms with protected local IDs occupy every configured
+  reserve partition and another swarm requests serving
 - **THEN** transition fails before listener/author activation without evicting
   or shortening any occupied partition
 
@@ -615,7 +615,7 @@ Queued relay work MUST be bounded and duplicates MUST NOT be requeued. Own ads
 SHALL be authored only on a fixed 1,800-second base cadence with independently
 sampled uniform jitter from -600 through +600 seconds. This cadence is not
 configurable in version one. Authored lifetime SHALL default to 7,200 seconds
-and MUST NOT exceed 86,400 seconds. Subnet start, listener start, and new overlay
+and MUST NOT exceed 86,400 seconds. Swarm start, listener start, and new overlay
 channels MUST NOT trigger authoring.
 
 A transient MAY relay newly accepted ads from bounded memory but SHALL NOT
@@ -626,7 +626,7 @@ timing, topology, first-seen, or global observation.
 #### Scenario: Relay preserves contents
 
 - **WHEN** an accepted ad is relayed
-- **THEN** subnet ID, visibility, ad ID, lifetime, and addresses are unchanged
+- **THEN** swarm ID, visibility, ad ID, lifetime, and addresses are unchanged
 
 #### Scenario: Gossip loop
 
@@ -640,7 +640,7 @@ timing, topology, first-seen, or global observation.
 
 ### Requirement: Lookup and optional public enumeration are paginated
 
-Direct lookup SHALL name one subnet and return addresses only for it. Each page
+Direct lookup SHALL name one swarm and return addresses only for it. Each page
 SHALL echo the request ID, contain at most 64 addresses and 65,536 encoded
 bytes, and include at most one fixed cursor. On the first page, the responder
 SHALL capture the current greatest live ordered key as the terminal key. A next
@@ -722,7 +722,7 @@ existing field order and valid wire encoding byte-for-byte.
 
 A transient SHALL accept no inbound overlay connections, author no ads, and
 persist no swarm-overlay ad/query/history state. It MAY keep bounded in-memory
-ads and the bounded successful-endpoint-only cache. Subnet lifecycle persistence and
+ads and the bounded successful-endpoint-only cache. Swarm lifecycle persistence and
 transport-managed state are separate scopes and MUST be documented separately.
 Both roles apply identical decoding, validation, authorization, and work
 bounds.
@@ -760,7 +760,7 @@ bounds.
 - **WHEN** a transient disconnects
 - **THEN** swarm-overlay state retained by the module is at most its bounded
   successful connect-URL/resolved-endpoint cache, while separately configured
-  subnet/transport state follows its own documented policy
+  swarm/transport state follows its own documented policy
 
 ### Requirement: Resource accounting covers amplification paths
 
@@ -784,16 +784,16 @@ limits SHALL use these defaults and MUST NOT exceed these maxima:
 | pages consumed per public enumeration | 4 | 16 |
 | candidate addresses per join attempt | 64 | 256 |
 | previously compatible retry attempts | 16 | 64 |
-| persisted compatible retry URLs per subnet | 64 | 256 |
-| local-author reserve subnet partitions | 32 | 256 |
-| active subnets | 32 | 256 |
+| persisted compatible retry URLs per swarm | 64 | 256 |
+| local-author reserve swarm partitions | 32 | 256 |
+| active swarms | 32 | 256 |
 | concurrent lifecycle attempts | 8 | 32 |
 | shutdown deadline seconds | 120 | 600 |
 | pending-request timeout seconds | 10 | 60 |
 | configured ordinary overlay peers | 8 | 256 |
 | overlay bind/listener addresses | 1 | 16 |
-| serving bind addresses per subnet | 1 | 16 |
-| serving external addresses per subnet | 1 | 32 |
+| serving bind addresses per swarm | 1 | 16 |
+| serving external addresses per swarm | 1 | 32 |
 | overlay inbound channels | 64 | 256 |
 | overlay outbound channels | 8 | 64 |
 | overlay manual channels | 8 | 256 |
@@ -822,11 +822,11 @@ peer addresses, and SHALL be removed on disconnect.
 - **THEN** accepting, storing, relaying, expiring, or reporting it opens no
   connection to that address
 
-### Requirement: Subnet joining alone validates advertised addresses
+### Requirement: Swarm joining alone validates advertised addresses
 
 Lookup results SHALL remain ephemeral typed original-URL/resolved-socket targets
-for the requested subnet until compatibility succeeds; they MUST NOT enter a
-URL-only host/refinery set first. A joining subnet MUST apply ordinary magic,
+for the requested swarm until compatibility succeeds; they MUST NOT enter a
+URL-only host/refinery set first. A joining swarm MUST apply ordinary magic,
 application-name, and major/minor checks before treating a peer as compatible.
 Failure MUST drop the candidate, remain fallible, and MUST NOT penalize the
 overlay relay. Passing compatibility does not authenticate an operator or
@@ -835,7 +835,7 @@ MUST independently resolve and validate a new exact target.
 
 Before resolution or dialing, the joiner SHALL place previously compatibility-
 verified persisted ordinary peers in one tier and fresh overlay URLs in another.
-The Swarm retry index itself SHALL contain at most the configured persisted-
+The swarm retry index itself SHALL contain at most the configured persisted-
 compatible cap; when full, a newly compatible URL remains usable for its current
 session but MUST NOT evict an existing retry URL merely to enter that index.
 
@@ -854,7 +854,7 @@ donated to fresh preparation but not conversely. Every persisted peer MUST
 still undergo fresh resolution and egress validation.
 
 Already validated targets SHALL be installed as a two-phase pre-start manual
-plan. At subnet start, the remaining candidate-dial duration SHALL be split at a
+plan. At swarm start, the remaining candidate-dial duration SHALL be split at a
 monotonic midpoint. The verified phase MUST stop/cancel by that midpoint and
 consume no more than its configured retry limit or half the total candidate-
 attempt budget. Fresh targets activate for the second half and retain at least
@@ -862,14 +862,14 @@ half the attempt capacity; if the verified phase is empty, fresh dialing MAY
 begin immediately. Every connector uses the exact validated target. No fresh
 candidate is persisted before compatibility.
 
-#### Scenario: Wrong subnet is rejected
+#### Scenario: Wrong swarm is rejected
 
 - **WHEN** an advertised peer fails a bound compatibility field
-- **THEN** it does not enter the verified ordinary subnet peer set
+- **THEN** it does not enter the verified ordinary swarm peer set
 
 #### Scenario: Relay is not blamed
 
-- **WHEN** a relayed address fails subnet connection
+- **WHEN** a relayed address fails swarm connection
 - **THEN** the immediate overlay relay is not treated as author
 
 #### Scenario: Attacker grinds lexical address order
@@ -894,21 +894,21 @@ candidate is persisted before compatibility.
 
 ### Requirement: Metadata disclosure and identity scoping are explicit
 
-Wire messages MUST NOT intentionally bind different subnets to one stable node
+Wire messages MUST NOT intentionally bind different swarms to one stable node
 or signing identity. Durable state MUST NOT contain querier identity or query
 history. An answering peer nevertheless observes the requested ID; requests on
 one channel are linkable; timing, topology, public enumeration, and endpoint
 reuse are metadata surfaces. The capability MUST NOT claim PIR, guaranteed
 origin anonymity, absence of remote traces, or global-observer resistance.
 
-Gossip and store peers necessarily observe and MAY retain every subnet-ID to
+Gossip and store peers necessarily observe and MAY retain every swarm-ID to
 advertised-endpoint mapping they receive. The forbidden provenance association
-is a mapping from overlay source peer to subnet/ad authorship; the rendezvous
+is a mapping from overlay source peer to swarm/ad authorship; the rendezvous
 ID-to-endpoint mapping is intentional protocol output and is not confidential.
 
-Every overlay and subnet `P2p` instance SHALL use an independently CSPRNG-
+Every overlay and swarm `P2p` instance SHALL use an independently CSPRNG-
 generated `VersionMessage.node_id`; it MUST NOT be persisted or reused across
-instances, subnets, overlay/subnet roles, or process restart.
+instances, swarms, overlay/swarm roles, or process restart.
 
 Serving documentation SHALL identify endpoint reuse as directly linkable and
 SHALL NOT claim automatic independent Tor/I2P provisioning.
@@ -918,12 +918,12 @@ SHALL NOT claim automatic independent Tor/I2P provisioning.
 - **WHEN** a lookup for S is issued
 - **THEN** documentation states the answering peer observes S
 
-#### Scenario: Overlay and subnet version identities
+#### Scenario: Overlay and swarm version identities
 
-- **WHEN** one process starts an overlay and one or more subnet `P2p` instances
+- **WHEN** one process starts an overlay and one or more swarm `P2p` instances
 - **THEN** their version node IDs are independently generated and unequal
 
 #### Scenario: Shared endpoint is linkable
 
-- **WHEN** one endpoint is advertised for two subnets
+- **WHEN** one endpoint is advertised for two swarms
 - **THEN** guidance identifies the direct link and makes no contrary claim

+ 31 - 31
openspec/changes/swarm/tasks.md

@@ -1,4 +1,4 @@
-# Tasks: swarm overlay for subnet rendezvous
+# Tasks: swarm overlay for swarm rendezvous
 
 ## 1. Review checkpoint and module foundation
 
@@ -20,7 +20,7 @@
   domain, lengths, big-endian integers, 32-byte app-name bound, and secret shape; add app-name,
   flag/secret, UTF-8 byte, and excluded-version-field tests, then run
   `make test`.
-- [ ] 2.2 Implement BLAKE3 `SubnetId` and the normative darkirc byte/hash golden
+- [ ] 2.2 Implement BLAKE3 `SwarmId` and the normative darkirc byte/hash golden
   vector plus bound-field divergence and patch-equivalence tests; run
   `make test`.
 - [ ] 2.3 Add `OsRng` private-secret generation and secret-safe debug/error
@@ -44,7 +44,7 @@
   semver strings—before reservation/allocation while preserving valid wire
   bytes. Add golden compatibility, huge declared count/string, overlong
   element, roundtrip, and combined-oversize tests. Generate each P2p node ID with
-  `OsRng`; test overlay/subnet/restart independence and no persistence/reuse,
+  `OsRng`; test overlay/swarm/restart independence and no persistence/reuse,
   then run `make test`.
 - [ ] 3.3 Record focused human review that shared-net changes alter neither
   framing, magic, version compatibility, nor manual/seed/inbound reload
@@ -74,7 +74,7 @@
 
 ## 4. Bounded correlated wire protocol
 
-- [ ] 4.1 Implement nonempty `SubnetAd` with exact ID, visibility, ad ID,
+- [ ] 4.1 Implement nonempty `SwarmAd` with exact ID, visibility, ad ID,
   lifetime, 1..=32 address, 1,024-byte URL, shareable-scheme, and 65,536-byte
   message limits plus fixed command/field order and visibility values; add
   serialization/golden-command and every-boundary test, then run `make test`.
@@ -121,17 +121,17 @@
   default/max/clamp, relay-preservation, duplicate/fresh-ID, and expiry tests,
   then run `make test`.
 - [ ] 5.3 Implement protected seen-ID retention through local address expiry
-  plus 86,400 seconds, a 256-default/1,024-maximum general quota per subnet, and
-  32-default/256-maximum remote-inaccessible local-author subnet partitions of
+  plus 86,400 seconds, a 256-default/1,024-maximum general quota per swarm, and
+  32-default/256-maximum remote-inaccessible local-author swarm partitions of
   256 slots each within the global cap. Use checked capacity arithmetic and
   validate persisted general/local partitions separately without double count;
-  retain stopped-subnet partitions until their IDs expire. Reject fresh ads or
+  retain stopped-swarm partitions until their IDs expire. Reject fresh ads or
   serving transitions before mutation/network activity when the applicable pool
-  is full. Add one-subnet, distributed-subnet, global, sequential serving churn,
-  local-reserve, stopped-subnet last-ID release/resume race, cadence-window,
+  is full. Add one-swarm, distributed-swarm, global, sequential serving churn,
+  local-reserve, stopped-swarm last-ID release/resume race, cadence-window,
   local-reserve wire/RPC/status/metric/telemetry exclusion, positive restart
   fixture where reserve IDs fit only when excluded from general accounting,
-  protected-ID, cross-subnet ad-ID reuse, expired-admission, and replay tests,
+  protected-ID, cross-swarm ad-ID reuse, expired-admission, and replay tests,
   then run `make test`.
 - [ ] 5.4 Implement dual-clock ad-address expiry plus restart-safe dedup:
   restore bounded remaining address lifetime and persist seen-ID deadlines on a
@@ -175,11 +175,11 @@
   correlated pending requests; direct lookup ignores visibility and enabled
   enumeration grants no role privilege. Add continuous-mutation bounded progress,
   terminal-key, non-snapshot omission/addition, disabled-public, transient
-  requester, no-cross-subnet, and unsolicited-response tests, then run
+  requester, no-cross-swarm, and unsolicited-response tests, then run
   `make test`.
 - [ ] 6.4 Implement the exact per-channel message/work rates and
   response-byte budget plus configured queue, global semaphore, page, candidate,
-  previously-compatible retry/index, local-author partition, active-subnet,
+  previously-compatible retry/index, local-author partition, active-swarm,
   concurrent-attempt, and shutdown defaults/maxima plus request timeout,
   configured peer, bind/external address, and
   inbound/outbound/manual/total channel, dial concurrency/rate,
@@ -211,31 +211,31 @@
 - [ ] 7.5 Test that failed cached app/protocol/store startup leaves no task or
   state before the configured stage and that overall bootstrap remains bounded;
   run `make test`.
-- [ ] 7.6 Implement transient overlay stop independently from subnet registry
+- [ ] 7.6 Implement transient overlay stop independently from swarm registry
   lifetime. Default to session-bound retention with no lookup/join-triggered
   stop; allow immediate teardown only via explicit reduced-privacy policy with
   timing warning and deterministic stop after every caller-visible success,
   empty, error, timeout, or cancellation outcome but not join's internal lookup;
   reject stop while persistent store/gossip or serving-ad duties remain. Add
   default-no-trigger, all terminal outcomes, internal-phase retention,
-  same-operator correlation, later reconnect, and subnet-survival tests with
+  same-operator correlation, later reconnect, and swarm-survival tests with
   `make test`.
 
-## 8. Registry-owned subnet lifecycle and source attempts
+## 8. Registry-owned swarm lifecycle and source attempts
 
 - [ ] 8.1 Implement per-ID `Initializing`, `Joining`, `Joined`, `Serving`, and
   `Stopping` ownership with serialized same-ID transitions and concurrent
   different-ID operation; add legal/duplicate transition tests, then run
   `make test`.
 - [ ] 8.2 Implement full-ID paths and isolated settings, hosts, refinement,
-  datastores, app state, and shutdown ownership; add cross-subnet isolation and
+  datastores, app state, and shutdown ownership; add cross-swarm isolation and
   delete tests, then run `make test`.
 - [ ] 8.3 Implement the fallible initializer returning typed app state plus
   shutdown; retain a type-erased `Arc` and hook in the registry so caller-handle
   drop cannot end app state. Test pre-start ordering, drop ownership, and
   initializer failure with `make test`.
 - [ ] 8.4 Keep overlay candidates as ephemeral typed original-URL/resolved-socket
-  targets through the subnet pre-start manual connector; do not insert URL-only
+  targets through the swarm pre-start manual connector; do not insert URL-only
   host/refinery state before compatibility. Drop failures, persist only after
   compatible ordinary channel, and re-resolve/revalidate every later outbound/
   retry/refine attempt. Bound the persisted-compatible index at 64 default/256
@@ -250,8 +250,8 @@
   magic/app/version, temporary-direct, seed/refine-without-peer, unreachable,
   disconnect-race, and test-only injected-RNG tests; run `make test`.
 - [ ] 8.5 Enforce channel ownership separation: no overlay stream transfer,
-  subnet re-handshake, or subnet-tag multiplexing, even when the answering
-  overlay peer also serves the subnet; add structural and same-operator tests,
+  swarm re-handshake, or swarm-tag multiplexing, even when the answering
+  overlay peer also serves the swarm; add structural and same-operator tests,
   then run `make test`.
 - [ ] 8.6 Implement overlay-only, static-only, and combined attempts with
   explicit source activation and per-attempt/overall deadlines; add policy and
@@ -267,7 +267,7 @@
   add repeated leave, join/leave race, retained rejoin, and isolated deletion
   tests, then run `make test`.
 - [ ] 8.10 Implement swarm shutdown ordering: stop authoring, cancel attempts,
-  stop every subnet despite errors, then overlay; add finite concurrent teardown
+  stop every swarm despite errors, then overlay; add finite concurrent teardown
   and failing-hook tests, then run `make test`.
 
 ## 9. Initial serving, controlled recreation, and authoring
@@ -287,11 +287,11 @@
   success, retained-state, bind failure, and no-partial-author tests, then run
   `make test`.
 - [ ] 9.4 Validate advertised endpoints separately, emit explicit warning on
-  local cross-subnet reuse, and avoid automatic Tor/I2P provisioning claims;
+  local cross-swarm reuse, and avoid automatic Tor/I2P provisioning claims;
   add bind/external distinction and reuse tests, then run `make test`.
 - [ ] 9.5 Implement one author task with a fixed non-configurable 30-minute
   base interval, independent uniform ±10-minute
-  `OsRng` jitter, fresh `OsRng` ad IDs, shuffled subnet order, and bounded
+  `OsRng` jitter, fresh `OsRng` ad IDs, shuffled swarm order, and bounded
   two-hour-default/24-hour-maximum lifetime and addresses; add default/clamp,
   config-rejection, reserve-capacity, and injected clock/RNG tests, then run
   `make test`.
@@ -308,12 +308,12 @@
   omitted tests, then run `make test`.
 - [ ] 10.2 Start lilith with the durable passive store and no ad-refinery/dial
   path; add direct ordinary cold-start, persisted expiry, dedup saturation,
-  per-subnet quota, zero local-author partitions, checked monotonic-epoch
+  per-swarm quota, zero local-author partitions, checked monotonic-epoch
   remainder restart, equality/overflow/reduced-cap/epoch startup failure, two-
   hour stale-address clamp, rollback-before-ID-commit replay, forward-clock, and
   dial-spy tests, then run `make test`.
 - [ ] 10.3 Implement aggregate-only status RPC for listener, aggregate
-  connections, capacities, address/dedup, per-subnet-quota/checkpoint failures,
+  connections, capacities, address/dedup, per-swarm-quota/checkpoint failures,
   eviction, expiry, and rejection; prove peer/advertised addresses, IDs, sources,
   per-peer data, and query mappings are absent with `make test`.
 - [ ] 10.4 Keep overlay and legacy instances isolated in settings, policy,
@@ -323,9 +323,9 @@
 
 - [ ] 11.1 Add local cold-start tests for cached ordinary success, fresh
   configured fallback, descriptor lookup, app initialization, and ordinary
-  subnet join without per-subnet overlay configuration; run `make test`.
+  swarm join without per-swarm overlay configuration; run `make test`.
 - [ ] 11.2 Add first-server creation followed by cadence ad, client lookup, and
-  ordinary join, proving a new subnet requires no preexisting peer; run
+  ordinary join, proving a new swarm requires no preexisting peer; run
   `make test`.
 - [ ] 11.3 Add poisoned/stale tests proving stores/lilith never dial targets,
   two-hour defaults reduce stale retention, CSPRNG ordering defeats lexical
@@ -333,7 +333,7 @@
   blamed, and static fallback remains usable; run `make test`.
 - [ ] 11.4 Add concurrent abuse tests for oversized fields/URLs, query/pending
   floods, fresh-ID floods, protected-set saturation, replay loops, relay fanout,
-  one-subnet/distributed saturation, local-author reserve, terminal-cursor
+  one-swarm/distributed saturation, local-author reserve, terminal-cursor
   mutation/duplicate/empty-page churn, durable-write/checkpoint pressure,
   candidate reservoir/order/time-budget grinding, dial concurrency/rate, per-
   destination repetition, DNS resolution totals, and victim reflection; verify
@@ -343,8 +343,8 @@
   serving recreation, concurrent leave, retained rejoin, isolated delete, and
   failing shutdown hooks; run `make test`.
 - [ ] 11.6 Add artifact/privacy tests proving no protocol-added stable node/
-  signing identity across overlay/subnets (excluding disclosed endpoint reuse),
-  non-public omission, caches/stores free of queries/sources, scoped subnet and
+  signing identity across overlay/swarms (excluding disclosed endpoint reuse),
+  non-public omission, caches/stores free of queries/sources, scoped swarm and
   transport persistence, explicit ID-to-endpoint visibility but no source-peer/
   authorship mapping; run `make test`.
 
@@ -355,7 +355,7 @@
   behavior; add overlay success, fresh static fallback, repeated initializer,
   and complete failure tests, then run `make test`.
 - [ ] 12.2 Add bounded aggregate pilot metrics for lookup latency, stale/poisoned
-  failures, per-subnet/global remote dedup pressure, checkpoint failures, page
+  failures, per-swarm/global remote dedup pressure, checkpoint failures, page
   completion/mutation omissions, and bootstrap/source fallback. Expose no local-
   author reserve occupancy/use/failure timing. Prove schemas contain no peer,
   query mapping, private ID, local-author fact, or secret with `make test`.
@@ -363,7 +363,7 @@
   seeds, staged reconstruction, initializer reruns, serving recreation downtime,
   bind versus external endpoints, ID/query disclosure, unsigned poisoning,
   gossip/store visibility of ID-to-endpoint mappings, egress/DNS/proxy policy,
-  reflection budgets, per-subnet/global dedup saturation and local reserve,
+  reflection budgets, per-swarm/global dedup saturation and local reserve,
   monotonic checkpoint extension and rollback-before-commit replay limitation,
   two-hour TTL defaults, mutation-tolerant non-snapshot pagination, randomized
   reservoir/time-partitioned candidate tiers, static fallback, endpoint