|
| 1 | +# NIP-BA assurance and compatibility note |
| 2 | + |
| 3 | +Status: draft, awaiting independent redteam. Normative text is |
| 4 | +[`../../nips/NIP-BA.md`](../../nips/NIP-BA.md). This note is evidence, not extra |
| 5 | +normative protocol hidden to improve the word count. |
| 6 | + |
| 7 | +## Sources and scope |
| 8 | + |
| 9 | +This is a separate proposal on main, not a change to runtime behavior or a |
| 10 | +claim that the existing SDK/host conforms: |
| 11 | + |
| 12 | +- Original nine-operation prose: block/buzz#6790, |
| 13 | + `804ce76c167d0f435cbff65e7e3f4aad720915f6`, `docs/agent-broker.md`. |
| 14 | +- Fifteen-operation Rust contract: block/buzz#6922, |
| 15 | + `115e7975a11d7d4d95043cb847abd13c1a32f370`, |
| 16 | + `crates/buzz-sdk/src/broker/{actions,mod.rs,wire.rs,correlate.rs}`. |
| 17 | +- Client integration: block/buzz#6967, |
| 18 | + `3d2ea5b89adccf980879b9841e9a6b517b588b57`. |
| 19 | +- Buzz-local NIPs from this branch's main base |
| 20 | + `7a9a5233d9d755e715be0c585cf7850e935d28cf`. |
| 21 | + |
| 22 | +The remote-agent vision presently hands a signing key to the substrate. This |
| 23 | +proposes optional different custody, not a replacement deployment management |
| 24 | +plane. All durable user-visible state remains relay-scoped; the host's retry |
| 25 | +journal is execution safety state, not an alternative source of channel truth. |
| 26 | + |
| 27 | +## Deliberate clarifications beyond editorial compression |
| 28 | + |
| 29 | +The wire names, argument/outcome members, 15 action versions, and numerical |
| 30 | +limits follow #6922. The following need host/client agreement before adoption; |
| 31 | +they are **not assertions of already-implemented behavior**: |
| 32 | + |
| 33 | +1. Retry key includes community as well as principal. Atomic durable admission, |
| 34 | + in-flight joining, crash fencing, evidence-only reconciliation, and permanent |
| 35 | + retry tombstones close unspecified safety gaps. Availability/storage cost is |
| 36 | + intentional: finite retention without a new wire epoch cannot safely permit |
| 37 | + an old ID to execute again. |
| 38 | +2. Authenticate/authorize release on every retry, including revoked sessions. |
| 39 | + A retry refusal is attempt-local and must not clear earlier uncertainty. |
| 40 | + This corrects the original blanket “failed means never happened” wording. |
| 41 | +3. Specify memory core mapping to `profile`, absence/tombstones, exact compact |
| 42 | + size encoding, and observer partial-acceptance non-identifiability. No prefix |
| 43 | + guarantee was present in #6922; none is invented here. |
| 44 | +4. Cursor filter binding/invalid-cursor refusal, thread ancestry validation, |
| 45 | + exact unsigned integer syntax, and current membership are explicit host |
| 46 | + obligations. They are not all enforced by the baseline SDK deserializer. |
| 47 | +5. Clarify publication acknowledgement, partial lifecycle/watchdog effects, |
| 48 | + sorted changed-field subset, ambiguous-name refusal, and creation not implying |
| 49 | + a booted runtime. |
| 50 | +6. Remove false claims that NIP-46 cannot inspect intent or that closed string |
| 51 | + schemas prevent all secret transmission. Storage reads intentionally return |
| 52 | + decrypted application content. |
| 53 | + |
| 54 | +Remaining flexibility is explicit: deployment resource limits, initial read |
| 55 | +window/order/cursor lifetime, runtime/provider/model defaults, provisioning and |
| 56 | +deletion machinery, runtime telemetry body, and ownership-depth policy. This |
| 57 | +spec does not standardize runtime-to-owner telemetry interpretation. A claim of |
| 58 | +full cross-runtime interoperability would need that companion profile. |
| 59 | + |
| 60 | +## Finite transition system |
| 61 | + |
| 62 | +Run with Python 3.10+ (standard library only): |
| 63 | + |
| 64 | +```sh |
| 65 | +python3 docs/formal/nip-broker/model.py |
| 66 | +``` |
| 67 | + |
| 68 | +`model.py` defines an explicit transition relation and breadth-first explores |
| 69 | +all reachable states until a fixed point, not random traces or a depth cutoff. |
| 70 | +A state is `(records, allowed, uncertain)`. Each record is |
| 71 | +`(digest, phase, dispatches, effects, final)`, with final as a ghost observation |
| 72 | +that survives result erasure. One shared request ID, two unequal body digests, |
| 73 | +three contexts `(community,principal) = (0,0),(0,1),(1,0)` exercise collisions. |
| 74 | +One ID is a symmetry abstraction, not proof for arbitrarily many IDs. Work on |
| 75 | +different IDs can interact through real operation state, which is outside this |
| 76 | +model. `effects` is a Boolean abstraction of “any effect took hold,” not the |
| 77 | +number of relay events. |
| 78 | + |
| 79 | +Transitions: admission, digest conflict, wait timeout, dispatch, effect, |
| 80 | +completion, crash, reconciliation, result erasure, retry, and revocation. |
| 81 | +Dispatch is atomically admitted once. Revocation prevents future dispatch and |
| 82 | +stored-result release; it cannot recall already dispatched work. Reconciliation |
| 83 | +may reveal real effects but never runs the operation again. Crashed work can |
| 84 | +remain unknown forever. Lost responses are modeled by allowing completion to |
| 85 | +occur without client observation and later retry; HTTP packet order is not |
| 86 | +modeled. Reconciliation evidence is assumed truthful. |
| 87 | + |
| 88 | +### Checked invariants and mutation witnesses |
| 89 | + |
| 90 | +| Property | Normative seam | Deliberately broken transition | |
| 91 | +|---|---|---| |
| 92 | +| At most one dispatch per context/ID | Execution 2–5, retention | Concurrent redispatch, restart executor, evict protection | |
| 93 | +| Known failure has no effects | Result and Execution 6 | Crash misreported as failure | |
| 94 | +| Success has effect evidence | Published / completion | Baseline assertion (no separate mutant yet) | |
| 95 | +| No cross-context stored-result release | Session / K definition | Drop community or principal | |
| 96 | +| No release after revocation | Session / retry authorization | Replay bypasses revocation | |
| 97 | +| Different bytes do not replay | Execution 1/3 | Ignore digest | |
| 98 | +| Refusal retains previous uncertainty | Results, bold retry caveat | Clear uncertainty on refusal | |
| 99 | + |
| 100 | +Nine deliberate mutations must each produce a counterexample for their expected |
| 101 | +property; the process exits nonzero if the baseline violates an invariant or a |
| 102 | +mutation survives. This tests model guards, **not production guards**. Claims |
| 103 | +about production regressions require binding the actual host implementation, |
| 104 | +which this PR does not supply. |
| 105 | + |
| 106 | +### What this does not prove |
| 107 | + |
| 108 | +Not a proof of JSON-parser correctness, cryptography, credential entropy, TLS, |
| 109 | +Nostr event construction, thread ancestry resolution, pagination completeness, |
| 110 | +NIP-AE convergence, lifecycle atomicity, or actual host behavior. Authorization |
| 111 | +is abstracted to a Boolean; no policy engine is modeled. No fairness or liveness |
| 112 | +claim: permanent partitions and unavailable reconciliation can remain unknown. |
| 113 | +The at-most-once dispatch invariant does not establish exactly-once remote |
| 114 | +effects. Multi-step operation adapters must separately establish their effect |
| 115 | +and failure semantics. No malicious-host security theorem is possible when the |
| 116 | +host holds the identity key. |
| 117 | + |
| 118 | +## Upstream comparison protocol |
| 119 | + |
| 120 | +Fixed before drafting: upstream NIPs 01, 05, 07, 09, 10, 29, 42, 44, 46, 98 at |
| 121 | +`488b787848fcf1c6c3498c253264b8121b1a9692`. This is a purposive dependency/API/ |
| 122 | +security sample, not a random sample supporting a 90th-percentile assertion. |
| 123 | + |
| 124 | +Dimensions: normative precision; independent implementability; failure/retry |
| 125 | +clarity; security boundaries; reproducible verification; economy of expression. |
| 126 | +Each gets 0 absent, 1 major gaps, 2 usable with questions, 3 explicit adequate, |
| 127 | +4 unusually strong. N/A must not be scored zero. Independent review must cite |
| 128 | +evidence and may reject the rubric. No averages may hide a weaker dimension. |
| 129 | +Strictly better on every dimension requires every score to exceed the |
| 130 | +comparator's; Pareto superiority (no weaker, at least one stronger) is a |
| 131 | +separate, weaker criterion. Adoption, deployed interoperability, and ecosystem |
| 132 | +maturity are additional dimensions that an unimplemented draft cannot beat by |
| 133 | +editing prose. No “superior to 9/10” conclusion is asserted here. |
| 134 | + |
| 135 | + |
| 136 | +## Executable wire examples |
| 137 | + |
| 138 | +```sh |
| 139 | +python3 -m unittest discover -s docs/formal/nip-broker -v |
| 140 | +``` |
| 141 | + |
| 142 | +`vectors.json` contains a request and corresponding result for each of the 15 |
| 143 | +actions. `wire.py` is a dependency-free, independently written **partial wire |
| 144 | +oracle**, not a production implementation or a full conformance certificate. |
| 145 | +It checks closed shapes, scalar normalization, integer widths, UTF-8, identity |
| 146 | +syntax, size limits, status/code pairs and selected response correlations. |
| 147 | +`test_wire.py` systematically inserts unknown/null/duplicate members into the |
| 148 | +example objects and exercises boundaries and byte-distinct equivalent JSON. |
| 149 | +Example event IDs and d-tags are illustrative, not cryptographic vectors; the |
| 150 | +checker does not verify signatures, |
| 151 | +read-filter membership, ancestry, address derivation, or operation execution. |
| 152 | + |
| 153 | +A deliberately bypassed closed-object guard admits a forbidden `scope` member; |
| 154 | +the normal guard rejects it. An encoding regression was reproduced before the |
| 155 | +fix: Python's JSON decoder accepted UTF-16 input. The oracle now decodes bytes |
| 156 | +as UTF-8 explicitly. Neither experiment establishes a production SDK regression. |
| 157 | + |
| 158 | +## Comparison findings (author assessment; independent review pending) |
| 159 | + |
| 160 | +The fixed sample is not exchangeable: a browser capability, an event deletion |
| 161 | +request and a distributed action service solve different problems. Absence of |
| 162 | +an execution journal in a signature or serialization standard is **not a defect**. |
| 163 | +Consequently, numerical totals would reward our chosen problem and hide tradeoffs. |
| 164 | +The six dimensions above are inspection questions, not measured universal ranks. |
| 165 | +These are concrete strengths to preserve or learn from, and limits to our claim: |
| 166 | + |
| 167 | +| NIP at the pinned revision | Evidence in that document | NIP-BA comparison boundary | |
| 168 | +|---|---|---| |
| 169 | +| [01][n01] | Events/signatures specifies serialization; relay flow defines OK, CLOSED, EOSE and tie ordering | We add operation uncertainty, but depend on its event machinery; not strictly more precise in every dimension | |
| 170 | +| [05][n05] | Security Constraints forbids redirects; Notes distinguishes identification from verification and preserves pubkey identity across remapping | Comparable explicit trust boundary; much narrower and economical protocol | |
| 171 | +| [07][n07] | Two required browser methods, optional encryption methods, extension timing and implementation link | Our failure contract is fuller; its tiny API is easier to implement and explain for its task | |
| 172 | +| [09][n09] | Client Usage requires author matching; warns deletion cannot be guaranteed; deleting a deletion has no effect | Both explicitly bound promises; no reason to demand an action journal from a deletion-request event | |
| 173 | +| [10][n10] | Marked e tags distinguish parent/root and document legacy ambiguity | We reuse this idea rather than surpass it; its kind-1 scope is not our kind-9 profile | |
| 174 | +| [29][n29] | Relay-scoped group identity, forks/migrations, independent subgroup membership, reconstruction events | Our retry namespace is explicit, but UUID-only actions and out-of-band provisioning are less general | |
| 175 | +| [42][n42] | Connection-scoped challenges, request retry examples, auth-required/restricted distinction | We add revoked-result rules; our bearer provisioning is less specified than its challenge exchange | |
| 176 | +| [44][n44] | Limitations, exact algorithm/pseudocode, external audit, published positive/negative vectors | A finite retry model is not a cryptographic audit; no verification superiority claimed | |
| 177 | +| [46][n46] | Two connection flows, permission requests, secret validation, logout limitations, auth challenge examples | We add reads/execution safety; it specifies discovery and connection establishment that we leave out of band | |
| 178 | +| [98][n98] | URL/method/time checks, optional body binding, wire example and reference implementation | We require retry-byte identity but solve a different authentication problem and are less compact | |
| 179 | + |
| 180 | +NIP-BA's current strengths are explicit attempt-local failure semantics, durable |
| 181 | +retry protection, a small executable safety model with negative witnesses, and |
| 182 | +all-action wire examples. Its weaknesses remain companion runtime telemetry |
| 183 | +semantics, out-of-band provisioning/policy, unbounded lifetime journal growth |
| 184 | +controlled only by admission quotas, no production host, and no independent |
| 185 | +client/host interoperability run. Economy must be judged relative to scope, |
| 186 | +not by rewarding the document with the fewest absolute words. |
| 187 | + |
| 188 | +**The requested “superior in all dimensions to at least 9/10” target is not |
| 189 | +established.** No editing-only stopping rule can supply deployment maturity or |
| 190 | +an independent implementation. This proposal should be judged on an auditable |
| 191 | +contract and resolved review findings, not a fabricated league table. |
| 192 | + |
| 193 | +[n01]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/01.md |
| 194 | + |
| 195 | +[n05]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/05.md |
| 196 | + |
| 197 | +[n07]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/07.md |
| 198 | + |
| 199 | +[n09]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/09.md |
| 200 | + |
| 201 | +[n10]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/10.md |
| 202 | + |
| 203 | +[n29]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/29.md |
| 204 | + |
| 205 | +[n42]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/42.md |
| 206 | + |
| 207 | +[n44]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/44.md |
| 208 | + |
| 209 | +[n46]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/46.md |
| 210 | + |
| 211 | +[n98]: https://github.com/nostr-protocol/nips/blob/488b787848fcf1c6c3498c253264b8121b1a9692/98.md |
| 212 | + |
| 213 | + |
| 214 | +## Reproduction result before independent review (2026-09-03) |
| 215 | + |
| 216 | +Python 3.14, three-context baseline: **1,481,544 reachable states and 16,102,044 |
| 217 | +transitions**, exhausted with no invariant violation. All nine mutations yielded |
| 218 | +a counterexample for their designated property. Wire suite: **9 test groups |
| 219 | +passed**, including positive request/result vectors for every action. |
| 220 | +Model source SHA-256: `0f87f39917e044c4767be3d9a75fe1aed65325be9316dbebdab7c6847c7447a5`. |
| 221 | +Rerun the commands above; model stdout contains the complete mutation traces. |
| 222 | + |
| 223 | +`just ci` was attempted on base `7a9a5233d9d755e715be0c585cf7850e935d28cf` |
| 224 | +with only these documentation/model additions. The initial attempt timed out; |
| 225 | +a subsequent invocation selected an old PATH compiler and failed the MSRV check. |
| 226 | +With the installed Rust 1.95.0 toolchain selected explicitly, repository checks |
| 227 | +and the full 463-test CLI package passed, but `test-unit` stopped in buzz-acp at |
| 228 | +`acp::tests::keepalive_resets_idle_past_deadline` (82 passed, one failed before |
| 229 | +fail-fast). Later CI stages did not run. **Full repository CI is not green**; |
| 230 | +this note does not diagnose that failure as a flake or as caused by this change. |
| 231 | + |
| 232 | +Independent redteam requested from Eva; findings and disposition remain pending. |
| 233 | +This is a reviewable draft, not approval of a production broker. |
0 commit comments