diff --git a/CONTEXT.md b/CONTEXT.md index 6698a590..8f3a134d 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -629,7 +629,7 @@ _Avoid_: a second hand-written `hidden(..)` check at the route (the exclusion li **Consent grounding**: the target node answers only if its own rules admit the asker for `MSG` to that endpoint — enforcement stays owner-side, at the owner's seam. Presence already leaks to exactly that population one message at a time (SENT vs QUEUED), so the probe adds no new information class, only a cheaper way to ask. An unadmitted asker gets no answer at all, so it cannot even learn the endpoint exists. -**Cost**: probes fan out simultaneously and the verb **blocks** on them under a hard **10-second per-probe ceiling passed as a parameter** — one wedged node costs one ceiling, never the batch. The shared subnet-liveness `PROBE_TIMEOUT` (2500ms) is untouched: turning that dial up for this family would slow every other caller down. +**Cost**: probes fan out simultaneously and the verb **blocks** on them under a hard **2.5-second per-probe ceiling passed as a parameter** — one wedged node costs one ceiling, never the batch; an unanswered probe remains UNKNOWN, never OFFLINE. In releases#299, perri's 10105/10120 ms waits were one unreachable peer's dial reaching the former 10-second ceiling. The shared subnet-liveness `PROBE_TIMEOUT` (2500ms) stays separate and untouched; the matching values do not couple the controls. _Avoid_: reading a perch record (`is_online`, the ready file, the liveness stamp) to answer — that is the derived-view class, where a record reads ONLINE with every recorded pid dead; saying **offline** for a silence; saying **busy** anywhere but from broker authority. diff --git a/crates/spt/src/cli.rs b/crates/spt/src/cli.rs index 3f69de4a..01043d63 100644 --- a/crates/spt/src/cli.rs +++ b/crates/spt/src/cli.rs @@ -6958,14 +6958,14 @@ struct UnlistedRow { presence: Option, } -/// The HARD per-probe ceiling, passed as a PARAMETER rather than folded into -/// the shared [`PROBE_TIMEOUT`] const — that one is the subnet-liveness dial -/// (2500ms) and turning it up for this family's benefit would slow every other -/// caller down. 10s is a WAN round trip's worth of patience, and it is per -/// probe: one wedged node costs one ceiling, never the batch (`probe_all_with` -/// bounds each thread separately). +/// The HARD per-presence-probe ceiling stays a separate PARAMETER from the +/// shared [`PROBE_TIMEOUT`], even though both now carry 2500ms. Measurement +/// (releases#299): perri's 10105/10120ms waits were one unreachable peer's +/// dial reaching the former 10-second ceiling. A probe past 2.5 seconds reads +/// UNKNOWN, never OFFLINE — exactly the same silence rule as past 10 seconds. +/// One wedged node still costs one ceiling, never the batch (`probe_all_with`). // [impl->REQ-UNLISTED-PRESENCE-PROBE] -const PRESENCE_PROBE_CEILING: std::time::Duration = std::time::Duration::from_secs(10); +const PRESENCE_PROBE_CEILING: std::time::Duration = std::time::Duration::from_millis(2500); /// Ask every gathered UNLISTED row's node whether that endpoint is there right /// now, all at once, and pair each answer with its row. @@ -28163,9 +28163,9 @@ mod tests { #[test] fn a_wedged_probe_costs_one_ceiling_and_reads_unknown_not_offline() { use spt_net::net::presencemsg::Presence; - use std::time::{Duration, Instant}; + use std::time::Instant; - let ceiling = Duration::from_millis(150); + let ceiling = PRESENCE_PROBE_CEILING; let items: Vec<(String, String)> = vec![ ("fast0".into(), "aa11".into()), ("wedged".into(), "bb22".into()), @@ -28174,7 +28174,7 @@ mod tests { let start = Instant::now(); let got = probe_all_with(&items, ceiling, 16, Presence::Unknown, move |id, _| { if id == "wedged" { - std::thread::sleep(Duration::from_millis(1500)); + std::thread::sleep(10 * ceiling); } Presence::Listening }); @@ -28186,7 +28186,7 @@ mod tests { ); assert!( elapsed < 4 * ceiling, - "the wedged probe costs ONE ceiling, not its full 1500ms sleep; got {elapsed:?}" + "the wedged probe costs ONE ceiling, not its full sleep; got {elapsed:?}" ); } diff --git a/traceable-reqs.toml b/traceable-reqs.toml index 9d01b5d1..c044f748 100644 --- a/traceable-reqs.toml +++ b/traceable-reqs.toml @@ -4105,8 +4105,8 @@ required_stages = ["doc", "impl", "unit", "int"] # ACTIVATED NAMEPLATE W1 (todl [[requirements]] id = "REQ-UNLISTED-PRESENCE-PROBE" -title = "`endpoint list` answers 'is this peer there right now' for UNLISTED rows with a presence probe, and the probe is NOT A MESSAGE: it is its own kind-tagged wire family, in the shape KNOCK_KIND already established, so an N-1 daemon classifies it Unknown and DROPS IT CLEAN. Probes fan out simultaneously and the verb BLOCKS on them under a HARD 10-SECOND PER-PROBE CEILING passed as a PARAMETER -- the shared PROBE_TIMEOUT const stays 2500ms and is untouched -- over the `probe_all` + `run_bounded` fan-out shape, so the wall-time is one ceiling and one wedged dial never drags the batch. TWO SILENCES: a probe that goes unanswered renders UNKNOWN AND NEVER OFFLINE, because an N-1 daemon's clean drop and a dead node produce the same silence. The answer distinguishes LISTENING / BUSY / OFFLINE, and each word has exactly one authority: they are answered from the serving daemon's HONEST SESSION AUTHORITY (the broker's hosted-session map and its activity window), NEVER from perch records -- the derived-view class where `is_online` reads a ready-file and a liveness stamp. BUSY IS A BROKER-AUTHORITY WORD ONLY. A relay-hosted peer holds no broker session, so it answers from a real connect attempt: accepted is LISTENING, and anything else is UNKNOWN -- busy and offline are indistinguishable there, and rendering offline would be a coin flip presented as a fact; the renderer may say why. CONSENT GROUNDING: the target node answers only if its own rules admit the asker for MSG to that endpoint -- enforcement stays owner-side at the owner's seam -- and presence already leaks to exactly this population one message at a time (SENT vs QUEUED), so the probe adds no new information class. Gate: doc -- the networking entry naming the family, the ceiling and the three-plus-unknown vocabulary; impl -- the record, the dispatcher arm, the serve side and the bounded fan-out; unit -- an unanswered probe reads unknown and not offline, a relay-hosted peer never reads busy, an unadmitted asker is refused at the owner's seam, and the ceiling is honoured per probe rather than per batch." -required_stages = ["doc", "impl", "unit"] # ACTIVATED DOORBELL W3 leg (d-probe) (todlando build 2026-08-01). doc = CONTEXT.md 'The presence probe' entry under Networking (the family and its tag, the vocabulary with one authority per word, the two silences, consent grounding, and the ceiling; it also separates this from REQ-PRES-1 presence resolution, which answers 'where is the user' from recorded stamps). impl = spt-net presencemsg (PRESENCE_KIND, PresenceRecord/Reply, the Presence vocabulary whose from_token reads an unknown word as Unknown never Offline) + StreamFamily::Presence and its classify arm + serve_presence_feed/answer_presence_probe/presence_from/observe_relay in the daemon + request_presence (EOF = Unknown) + wansend::probe_presence + probe_all_with/PRESENCE_PROBE_CEILING(10s, a PARAMETER; PROBE_TIMEOUT untouched) + probe_unlisted and the presence column on both listing legs. unit = the vocabulary table (busy unreachable from any relay observation, offline minted only by no-session-and-no-channel, a session outranking a stale relay row both ways), the relay observation keyed on a REAL connect (same registry row reads Accepted then NotAccepted once the listener is gone, and that reads Unknown never Offline), the unadmitted asker refused at the owner's seam beside an admitted one answered, only-rows-with-a-node asked with answers landing on their own rows, and a wedged probe costing ONE ceiling while settling Unknown. +title = "`endpoint list` answers 'is this peer there right now' for UNLISTED rows with a presence probe, and the probe is NOT A MESSAGE: it is its own kind-tagged wire family, in the shape KNOCK_KIND already established, so an N-1 daemon classifies it Unknown and DROPS IT CLEAN. Probes fan out simultaneously and the verb BLOCKS on them under a HARD 2.5-SECOND PER-PROBE CEILING passed as a PARAMETER (releases#299: perri's 10105/10120 ms waits were one unreachable peer's dial reaching the former 10-second ceiling) -- the shared PROBE_TIMEOUT const stays 2500ms and is untouched -- over the `probe_all` + `run_bounded` fan-out shape, so the wall-time is one ceiling and one wedged dial never drags the batch. TWO SILENCES: a probe that goes unanswered renders UNKNOWN AND NEVER OFFLINE, because an N-1 daemon's clean drop and a dead node produce the same silence. The answer distinguishes LISTENING / BUSY / OFFLINE, and each word has exactly one authority: they are answered from the serving daemon's HONEST SESSION AUTHORITY (the broker's hosted-session map and its activity window), NEVER from perch records -- the derived-view class where `is_online` reads a ready-file and a liveness stamp. BUSY IS A BROKER-AUTHORITY WORD ONLY. A relay-hosted peer holds no broker session, so it answers from a real connect attempt: accepted is LISTENING, and anything else is UNKNOWN -- busy and offline are indistinguishable there, and rendering offline would be a coin flip presented as a fact; the renderer may say why. CONSENT GROUNDING: the target node answers only if its own rules admit the asker for MSG to that endpoint -- enforcement stays owner-side at the owner's seam -- and presence already leaks to exactly this population one message at a time (SENT vs QUEUED), so the probe adds no new information class. Gate: doc -- the networking entry naming the family, the ceiling and the three-plus-unknown vocabulary; impl -- the record, the dispatcher arm, the serve side and the bounded fan-out; unit -- an unanswered probe reads unknown and not offline, a relay-hosted peer never reads busy, an unadmitted asker is refused at the owner's seam, and the ceiling is honoured per probe rather than per batch." +required_stages = ["doc", "impl", "unit"] # ACTIVATED DOORBELL W3 leg (d-probe) (todlando build 2026-08-01). doc = CONTEXT.md 'The presence probe' entry under Networking (the family and its tag, the vocabulary with one authority per word, the two silences, consent grounding, and the ceiling; it also separates this from REQ-PRES-1 presence resolution, which answers 'where is the user' from recorded stamps). impl = spt-net presencemsg (PRESENCE_KIND, PresenceRecord/Reply, the Presence vocabulary whose from_token reads an unknown word as Unknown never Offline) + StreamFamily::Presence and its classify arm + serve_presence_feed/answer_presence_probe/presence_from/observe_relay in the daemon + request_presence (EOF = Unknown) + wansend::probe_presence + probe_all_with/PRESENCE_PROBE_CEILING(2.5s, a PARAMETER; PROBE_TIMEOUT untouched) + probe_unlisted and the presence column on both listing legs. unit = the vocabulary table (busy unreachable from any relay observation, offline minted only by no-session-and-no-channel, a session outranking a stale relay row both ways), the relay observation keyed on a REAL connect (same registry row reads Accepted then NotAccepted once the listener is gone, and that reads Unknown never Offline), the unadmitted asker refused at the owner's seam beside an admitted one answered, only-rows-with-a-node asked with answers landing on their own rows, and a wedged probe costing ONE ceiling while settling Unknown. [[requirements]] id = "REQ-HAZARD-PROBE-CONTEXT-ISOLATION"