Add buzz-dkg integration to the registry - #21
Conversation
ReviewI reviewed this from several angles — registry-policy compliance, technical correctness, security, the grounded-answering path, setup and in-channel UX, documentation, and repo quality. I read the pinned commit Verdict: strong submission, clear changes needed before merge. The engineering is well above the bar for a community entry, and the evidence trail (gate reports, transcripts, spend accounting) made this reviewable in a way most submissions aren't. Nothing below is a scope violation or a trust problem — the security declarations are honest and, where I could check them, conservative rather than inflated. Baseline I reproduced: registry validator passes (3 entries, 0 errors, 1 expected VM-scope warning). In the integration repo, Blocking — the entry itself1. 2. The headline evidence claim can't be verified at the commit it pins. I assume this is a genuine post-pin publish rather than an error — but it's the entry's only externally checkable claim, and it's the one a reviewer spot-checks first. 3. 4. 5. Please close PR #20 as part of merging this. Blocking — one real defect6. One
The node's What makes this a blocker rather than a nuisance is that it is self-perpetuating. The same shape, minus the OOM, means any ask that errors — a node 5xx, a malformed subject reaching the Setup is currently broken
7. The documented start sequence cannot work: nothing loads 8. There is no documented path from "I have a relay and a node" to a working bot. The fallback path is blocked too: 9. Two defaults and a version floor are wrong.
Should-fix10. The entry doesn't disclose that event authenticity is delegated to the relay. To be fair — and this is why it's not a blocker — this is a documented, deliberate delegation, not an oversight: The gap is that the registry entry — unlike DESIGN.md — never says so. 11. A crash between publish and persist strands a paid on-chain publish outside all accounting. On the adjacent double-publish worry: a second, distinct ✅ clears the dedup, the UNIQUE insert, and §6.7 ( 12.
13. Four of the nine advertised §6 invariants have no test. Mutating each guard to 14. §6.5 is a comment, not an implementation. 15. An unconfirmed publish is announced as an on-chain anchoring. 16. Every failure is silent to the room. 17. Client-controlled 18. No HTTP timeout anywhere, behind a single serial queue. 19. No in-process retry. 20. Two answering-quality issues. (a) The sufficiency gate is 21. 22. Promoter pubkeys are unvalidated. Nits (click to expand)
Verified clean — what I checked and found correct, so it isn't re-litigated (click to expand)
Nice work overall — the invariant-driven approval gate, the read-back-everywhere lifecycle, the documented trust boundaries, and the honesty of the security declarations are the strongest parts, and the gate reports are a model for how to make a submission reviewable. Items 1–5 gate merging the entry; 6 is the one real defect I'd want fixed regardless; 7–9 are what stand between an adopter and a running daemon. Happy to re-review on a re-pin. On the |
…, docs Fixes for OriginTrail/dkg-integrations#21 (Jurij89 review): - Add Apache-2.0 LICENSE + NOTICE (blocking #1) - Bound @dkg ask: cap question length + term set, resolve-on-error so a poisoned ask can't replay-brick the daemon (blocking #6) - Reserve a 'publishing' intent before vm/publish with budget + crash recovery; never announce a tentative (HTTP 502) publish as confirmed (#11,#15) - §6.5 re-reads and compares the shared source-set digest at approval (#14) - Fail closed on a non-numeric publish budget (#12); validate promoter pubkeys as hex/npub (#22); clamp future created_at cursor (#17) - Post rejection reasons back into the room (#16) - Setup: load .env, default DKG API :9200, Node >=22.9, document mainnet mode + a deploy-against-an-existing-node path (#7,#8,#9) - Nits: codepoint digest sort, bare-distill guard, untrack demo-fifa runtime state, quote-framed answers, test count 48->51 - 61 tests green; typecheck/lint/prettier clean Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…losures Re-pin to Zigoljube/buzz-dkg-integration@3cafe11 (adds LICENSE + the code and doc fixes from the review). Entry changes: - requiresDkgNodeVersion >=10.0.0 -> >=10.0.8 (graph-scoped publish landed 10.0.7) - description: drop "neuro-symbolic" for the deterministic no-model retrieval the code actually implements - promotionPath: cite the fully-evidenced UAL 2201 (tx/block + gate report) - security.notes: disclose relay-delegated event authenticity and that a bound channel's members are all readers of its Context Graph - oneLiner: point setup/env at the README deploy section Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for the exceptionally thorough review — the mutation-testing and the executed measurements made every finding actionable. Re-pinned to Blocking
The real defect
Setup
Should-fix10 / 21. Relay-trust + read-oracle disclosure — added both clauses to NitsFixed: codepoint digest ordering (was Deliberately deferred (documented, non-blocking)HTTP request timeouts (#18), in-process retry vs restart-only recovery (#19), a per-pubkey ask rate-limit (needs a schema column), and citation/receipt human-resolvability (#20b, some nits). Happy to land these too if you'd like them before merge. Suite is 61 tests (was 51), typecheck/lint/prettier clean. Thanks again — glad to iterate on a re-review. |
Re-review —
|
| # | How I verified |
|---|---|
| 1 LICENSE | Genuine 202-line Apache-2.0 + NOTICE; new commit, not just a re-pin |
| 2 Evidence | …/2201 + tx + block, all substantiated at the pin; 2209 fully retired from the entry |
| 3 Version floor | >=10.0.8, matching every gate report |
| 4 Description | "deterministic, no-model retrieval" is literally true of grounded.ts |
| 5 Duplicate | #20 closed |
| 6a Amplification | Re-ran my probe: same 9.7 KB hostile message → 1200 terms capped to 12, largest SPARQL 59 MiB → 5.6 KiB. MAX_TERMS lives inside questionTerms, the sole tokenizer, so there's no bypass; MAX_QUESTION_CHARS=512 also drops it at classify (boundary checked: 512 accepted, 513 dropped). Both caps mutation-proof |
| 6b The brick | Fixed and tested — including a real file-backed crash/restart, which now recovers and answers |
7 .env |
Verified the flag itself: loads when present, continues when absent (container-injected env still starts), and plain --env-file would have crashed. Right flag |
| 9 Defaults | :9200; >=22.9 correctly covers --experimental-strip-types (22.6) and --env-file-if-exists (22.9) |
| 11 Reserve-before-spend | Op is durably publishing at the instant publish() is invoked; a stranded op consumes the ceiling (maxPublishesPerDay=1 → second ✅ refused, 0 publishes) |
| 12 Budget fail-open | Re-ran my probe: unlimited/none now throw at startup instead of becoming NaN |
| 14 §6.5 | Real digest re-read against the SWM view, before the consume, and fail-closed — a throwing query rejects as transient, leaving no permanent row |
| 16 Silent rejections | Posts to the room, and the transient/permanent split is coherent: transient writes no terminal row and "re-add the reaction" genuinely works |
| 17 Cursor | Math.min(created_at, now + 300) |
| 22 Promoter format | npub decode and uppercase-hex normalisation — an UPPERCASE-configured promoter's ✅ is honoured end to end, and that breaks the moment .toLowerCase() is dropped |
| 8 Deploy docs | The new section answers nearly every gap — I ran the key-gen one-liner (works), and confirmed the <DKG_HOME>/auth.token story, the 0x<Curator>/<name> form, and the npub claim against ground truth |
| 10/21 Disclosures | Both clauses accurate against the code |
Also fixed unasked: loadConfig's injected env is now honoured. And the 10 new tests are real — every guard they claim to cover dies under mutation, verified with the mutation still on disk when vitest exited.
Regressions introduced by the publish-path fixes
R1 — #11's recovery path re-opens what #15 closed. completePublish's pre-check is pre.state === 'published' || 'finalized' (daemon.ts:525). But the node stamps dkg:state "published" for a tentative publish too — I read the guard myself: if (result.status === 'confirmed' || result.status === 'tentative') at packages/agent/src/dkg-agent-publish.ts:5824. So a crash during a tentative publish makes recovery announce "Published to Verifiable Memory / UAL: …" with zero publish calls — the same false anchoring claim #15 was written to prevent, arriving through a different door.
One caution on the obvious fix: gating on publishedUal does not work — it's stamped for tentative as well (assigned unconditionally as the …/t<opId> tentative form). The descriptor exposes no confirmed/tentative discriminator, so closing this properly needs an on-chain confirmation check, or persisting "we already called publish once" and refusing to announce without a fresh confirmed response.
R2 — a failed publish becomes an unattended re-publish that bypasses every §6 invariant. A non-502 failure deliberately leaves the op in publishing (:567-575); pendingOps() includes publishing; recover() → executeOp:256 → completePublish. Executed: one human ✅ → two on-chain publish attempts (publish() calls: 2 | final state: vm_receipted), and it retries on every boot.
The serious part isn't the retry, it's the entry point: executeOp:256 calls completePublish directly, so the boot path re-publishes without re-checking the promoter allowlist, the source-set digest, the channel↔CG binding, the chain gate, or the budget. Verified adversarially — a publish succeeded on recovery after the shared digest was tampered with and the channel was re-bound to a different Context Graph. The comment at :569 ("the node dedups a re-publish") holds only for an already-confirmed KA; for a failed one it genuinely publishes again. This weakens the human-attestation gate that justifies the entry's VM scope, so it's worth fixing even though mainnet is opt-in.
R3 — the CG presence probe now fails open. .catch(() => ({ exists: false })) became .catch(err => { warn; return { exists: true } }) (:99-105). Executed: with the probe erroring, the daemon starts. A definitively-absent CG still blocks, so the check isn't gone — but any 400/500/timeout now defeats the fail-early binding check. I think this came from my nit about the misleading error message, so: the ask was to distinguish "probe errored" from "probe says absent" and report the former accurately — not to proceed on error. Worth reverting to fail-closed with a clearer message.
The #15 fix advertises a recovery that the state machine forbids
The 502 branch is right in substance — no UAL, honest note. But it posts "Re-add ✅ once the node confirms." and then refuses. I drove it end to end (temporary test file, since deleted; tracked tree verified clean):
--- after 502 --- op.state: failed ual: (none)
--- after re-adding the ✅ --- rejected: "Can't publish that: KA already published or in publish flow."
failed is terminal: a 9×9 transition matrix confirms the only legal edge out of failed is failed→failed. Against the real node it fails one gate earlier — the node stamped state=published for the tentative tx, so §6.5's descriptor check rejects first — but either way the instruction can't be followed, and the message the promoter sees ("already published") is false. Either drop the instruction, or add a real re-approval route.
Untested guards (worth closing given what they protect)
Mutation results against the shipped suite, each with the mutation confirmed still on disk at exit — all SURVIVED with 61/61 green:
- the 502 branch (
if (false && status === 502)) — the original defect reappears undetected. Given R1 above, this is the most valuable missing test in the PR. - budget-includes-
publishing— the single guard keeping a stranded paid publish inside the declared ceiling. - §6.3 receipt KA+digest match, §6.4 channel↔CG remap, §6.7 not-already-published, and the descriptor-state half of §6.5. Only the new §6.5 content re-check gained coverage, so 3 of the 4 I flagged last round are unchanged. §6.7 is the guard involved in the dead-end above.
Smaller items
- A wrong
BDI_DKG_APIport doesn't produce the promised diagnostic. The README now says the symptom isECONNREFUSED; the operator actually gets{"message":"daemon failed to start","err":"TypeError: fetch failed"}.err.cause.codeisECONNREFUSED— loggingerr.causealongsideerrmakes the troubleshooting note true. - A 502 releases the budget reservation.
failedisn't counted, so N tentative publishes cost N real gas attempts while the ceiling reads 0 — the one path where a tx may already have cost money. published → vm_receiptedis never resumed. If the receipt send fails, the op stayspublished;recover()re-lists it every boot butexecuteOphas no branch, so the room never learns about a publish it paid for. Pre-existing, not from this round.- Silent drops. An over-length ask, a bare
@dkg distill, and an internally-failing ask all reply with nothing. The over-length refusal is the right safety choice (it refuses rather than silently answering a different question) — it just isn't announced, which cuts against this round's own "post the reason to the room" principle. - Approver attribution blanks to
approved-by:only on the crash-recovery path when the approval event is unreadable; the normal path passes the pubkey explicitly and is fine. package-lock.jsonstill records"node": ">=22.5"whilepackage.jsonsays>=22.9.- The Node floor is still one notch too low.
>=22.9is right for the two flags you cite, butsrc/registry/store.ts:8requiresnode:sqliteat module eval, and that was flag-gated until v22.13.0 (release notes: "unflag sqlite module", #55890). So 22.9–22.12 is still a band where the daemon cannot boot at all — reproduced asERR_UNKNOWN_BUILTIN_MODULE: No such built-in module: node:sqlite. Same class as the original>=22.5;>=22.13closes it, andREADME.md:62/:117need the bump too. - Stragglers:
deploy/docker-compose.integration.yml:29still defaults to:9420; README still says "51 tests" in two places (actual 61); the mainnet section still omits the prerequisites (publisher identity, funded wallet, on-chain-registered CG) and a dry-run path;phase0/README.md's.env.spikegenerator is still the no-op heredoc; three.env.examplevars have no inline comment despite "every var is documented inline". - The PR description was never updated — it still pins
c42efd2, still says "neuro-symbolic retrieval", and still cites…/2209. The JSON is what merges, but the body is the submission record. - Not a defect:
npm run format:checkgoes red on a Windows clone withcore.autocrlf=true, but passes with--end-of-line auto— the code is formatted and the script quoting was correctly fixed. A.gitattributesorendOfLine: "auto"would spare the next Windows contributor.
On the deferred items
Agreed on priorities, with one update: the approval path now makes 6 unbounded, untimeouted round trips, up from 4 (the #11 pre-check and the #14 digest re-read are both new), and the new digest re-read is the same SPARQL shape that already needed a bounded retry elsewhere. That makes #18 more load-bearing than when I first raised it.
Strong turnaround — the ask-path fix is complete and well-tested, completePublish is a better design than what I suggested, and the new tests target real failure modes rather than coverage. R1 and R2 are the ones I'd want resolved before mainnet is switched on anywhere; from a registry standpoint I'm happy for this to merge once you've decided how to handle them.
Re-review of 3cafe11 (OriginTrail/dkg-integrations#21) found the #11/#15 publish fixes interacted badly. Redesign around a terminal 'publish_unconfirmed' state: - R1: never decide publish success from the descriptor — the node stamps state='published' for a TENTATIVE tx too. A UAL is announced ONLY on a confirmed publish() response (one carrying a txHash). - R2: boot recovery of a 'publishing' op no longer re-publishes (that bypassed every §6 human-attestation gate) — it records the op unconfirmed instead. - 502/error/lost-response are all terminal 'publish_unconfirmed': honest note, counted toward the 24h budget (gas may have been spent), never auto-retried. - Dead-end fixed: the note no longer says "re-add ✅" (a route the state machine forbids); it says to re-capture the thread. - R3: CG presence probe fails closed again, with a message distinguishing "probe errored" from "CG absent". Also: - Resume a stranded published->vm_receipted op (re-post the receipt, read-back guards a double) - Log err.cause so a wrong BDI_DKG_API port shows ECONNREFUSED, per the docs - Node floor -> >=22.13 (node:sqlite unflagged there); README + package-lock - deploy compose default :9420 -> :9200; README test count -> 61; document the remaining .env.example vars; add .gitattributes (eol=lf) - 8 new/updated tests: 502 branch, budget counting, R1/R2/R3, §6.3/§6.4/§6.5 state, published->vm_receipted resume. 69 tests green; lint/prettier clean Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-pin to Zigoljube/buzz-dkg-integration@9195710, which fixes the three publish-path regressions from the re-review (R1 tentative-vs-confirmed, R2 unattended re-publish on boot, R3 fail-open CG probe) plus the Node floor (>=22.13). Entry text: bump the oneLiner's Node prereq to >=22.13. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for catching these — you were right that #11 and #15 fought each other. Re-pinned to RegressionsR1 — recovery re-opened the tentative dishonesty. R2 — boot re-publish bypassed §6. The recovery path no longer calls the publish routine at all. Both are folded into one honest terminal state, R3 — CG probe fail-open. Reverted to fail-closed, with the message split you asked for: a probe error now throws The dead-end messageFixed. The unconfirmed note no longer says "re-add ✅" (which New tests (69 total, up from 61)Closing the guards you mutation-flagged:
Smaller items
Still deferred (acknowledged)#18 (HTTP timeouts) I've left for a follow-up, but noted your point that it's now more load-bearing at 6 round trips — the new §6.5 digest re-read reuses the bounded-retry SPARQL shape, so the worst offender is contained, but a real per-request deadline is the right fix and I'll prioritise it. Also still deferred: #19 (in-process retry), a per-pubkey ask rate-limit, Approver attribution can still blank only on the crash-recovery receipt when the approval event is unreadable — best-effort by design; the normal path passes the pubkey explicitly. Thanks again for the depth here — the tentative/confirmed distinction in particular was exactly the kind of thing the descriptor made easy to get wrong. |
/rate-limit/#20b) Re-pin to Zigoljube/buzz-dkg-integration@0a5de8b: HTTP request timeouts (OriginTrail#18), in-process capture retry on reconnect (OriginTrail#19), a per-pubkey @dkg ask rate limit, and human-resolvable receipts + citations (#20b). No entry-field changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Landed the deferred items too — re-pinned to #18 — HTTP timeouts. Every DKG and relay #19 — in-process retry on reconnect. Per-pubkey ask rate-limit. Added #20b — resolvable citations + receipts. New
New tests (74 total): the reconnect capture retry, a check that Still open, by choice: full UAL/Nostr-source-id resolution inside That covers everything from both reviews I'm aware of. Thanks again for the exceptionally careful passes — happy to do another round or answer anything before merge. |
Final review —
|
| Item | Evidence |
|---|---|
| R1 | performPublish announces only on a txHash-bearing response; no descriptor read-back anywhere in the decision. Soundness checked from the other side too: the node emits status:'confirmed' only alongside onChainResult.txHash, so the new check can't false-negative a real confirmation |
| R2 | No path publishes without a fresh ✅ — performPublish now has exactly one call site (inside handleApproval). A publishing op on boot goes terminal instead of re-publishing; driven over a real process restart on on-disk SQLite: 0 publish calls |
| R3 | Fail-closed again, with the message split I asked for — a probe error now refuses start, distinct from the definitive "not present" |
publish_unconfirmed |
Terminal in the transition matrix, counted by countRecentPublishes, excluded from pendingOps. The budget is a genuine self-healing 24h rolling window, not a permanent burn |
published → vm_receipted |
New resume branch; the read-back genuinely prevents a duplicate receipt |
| ECONNREFUSED | Executed against a dead port: {"message":"daemon failed to start","err":"TypeError: fetch failed","cause":"ECONNREFUSED"} — the README's promise is now true |
| Node floor | >=22.13 in engines, package-lock.json, and both README prereqs, with the right rationale (node:sqlite unflagged at 22.13) |
.gitattributes |
Actually works — I cloned fresh with core.autocrlf=true, got LF files, and format:check exits 0 |
| Budget atomicity | Check-then-act is safe: events are serialised on one promise chain and recover() completes before subscribe()/connect(), so concurrent approvals can't both pass the ceiling |
| Stragglers | compose port → :9200; the three bare .env.example vars documented; entry re-pinned; oneLiner updated |
The new tests are good work — the four §6 tests and the R1/R2 tests assert observable outcomes (publishes === 0, not.toContain('UAL:')) rather than internal state, which is the right style. 9 of 13 publish-path mutants die against your own suite.
The one thing I'd still fix: "re-capture the thread" is a silent dead end
Last round's re-add ✅ pointed at a route the state machine forbade. The replacement is safer — it no longer misstates publication — but for the literal instruction it still doesn't work, and now it fails silently.
kaName is buzz-dkg-<sourceSetDigest[0:12]>. A bare re-pin doesn't change the source set (kind 40004 isn't in fetchThread's [9,40002,40003] filter, and the bot's own note is filtered by servicePubkey), so the new op targets the same kaName — which on the node is already promoted/SWM or published/VM. The first node call then hard-fails:
POST /api/knowledge-assets/buzz-dkg-…/wm/write
→ Assertion "…" is not an active Working Memory draft (KA_WM_LIFECYCLE_REQUIRED)
That's the node's own gate (assertWorkingMemoryLifecycleMutable requires state==='created' && layer===WM), and it applies whether or not the tentative tx landed. executeOp's catch is log-only, so nothing is posted to the room, and the doomed op stays in pendingOps and re-fails on every boot.
The route works only if the thread gained a non-service message tagging the root in between — which changes the digest and the KA name. That's a real escape hatch, but it's conditional and invisible to the operator, and it isn't what the message says.
Worth noting why the suite didn't catch this: MockDkg.write/finalize/share don't consult ka.state at all, so on your mock the re-capture appears to work end to end. And the one new assertion touching this message is not.toMatch(/re-add/i) — which passes for any replacement text, including one pointing at a route that does nothing.
Cheapest correct fix is any of: say the truth ("I can't retry this automatically — verify the tx on-chain, then post a message in the thread before re-capturing; a bare re-pin won't work, the content is unchanged"); or detect the collision up front — registry.opByKaName already exists and is currently unused — and explain it in-room; or, most generally, make executeOp report capture failures to the room instead of dropping them, which also closes the wider silent-failure gap.
Coverage gaps worth closing
hardening.test.ts > … > §6.5 rejects when the descriptor is not in finalized+shared SWM statedoes not test that guard. Deletingif (desc.state !== 'promoted' || desc.memoryLayer !== 'SWM')leaves the suite at 69/69 green. I verified this in a private copy with the mutation confirmed on disk at exit, a null control (comment-only → 69/69, as expected) and a positive control (disabling the promoter allowlist →rejects unauthorized reactorsfails, 68/69). The §6.5 content re-check is genuinely covered; it's the descriptor-state half that isn't, despite the test name.- R1's txHash-less-200 branch and the
pendingOpsexclusion also survive mutation — both behaviourally correct, just unasserted (MockDkg.publishalways returns atxHash). - The §6 tests assert
publishes === 0andoutcome === 'rejected'but never the reason, so rejection reasons are interchangeable without any test noticing.
Smaller items
docs/DESIGN.md— which the entry links asdesignBrief— still documents the deleted anti-pattern. Lines 49-50: "publish ambiguity: never blind-retry — descriptor read-back;published+reservedUal⇒ success recovered." That is verbatim R1, anddaemon.ts:521-525now says the opposite in a comment. The state diagram also omitspublish_unconfirmed.docs/wasn't touched this round. A committee member reading the linked brief would reasonably conclude R1 wasn't fixed — worth ~3 lines.- A pre-chain 4xx burns a budget slot and overstates the risk.
markUnconfirmedis used for any publish error, so a 409VM_PUBLISH_PRECONDITION— which definitively never reached the chain — consumes a mainnet budget slot and tells the room "a transaction may already have been submitted". Worth distinguishing definitively-pre-chain statuses from genuinely-ambiguous ones. - HTTP 207 is still consumed as full success.
res.okis true for 207, socontextGraphErroris discarded and the room gets a clean "Published to Verifiable Memory … context-graph: …" receipt. I checked reachability rather than just the handling: on v10.0.8contextGraphErroris only ever read, never assigned, so a stock node doesn't currently emit 207 — this is latent, not live. But the node's own first-party adapter states the contract explicitly ("must NOT be reported as full success"), so it's worth the ~5 lines before it becomes reachable. - README drift, third round running: still says "61 tests" in two places (actual 69), and
deploy/docker-compose.integration.yml:3still says "Node ≥22.5" against the new>=22.13floor. The README's approval section also still describes a binary success/rejection model and never mentionspublish_unconfirmed, what an operator should do about it, or that it consumes budget. - Minor robustness: a confirmed response lacking
ualwould post a literalUAL: null(the guard checkstxHash, the receipt printsual); andfindExistingReceiptmatches any service-authored thread message containing the substringtrigger: <id>, which an@dkg askanswer could in principle contain.
What I tried and couldn't break
For the record, since this is the last pass: the op-state graph has no reachable state without an exit; the mainnet budget check-then-act is atomic; the SQLite schema upgrades cleanly from the very first pin (c42efd2) to this one; the capture pipeline and full WM→SWM→receipt→✅→VM lifecycle still complete with exactly one of each external call; the §6 gates all still fire; SPARQL scoping and injection-resistance are intact; and the new in-room rejection messages leak nothing — I made the descriptor throw an error string containing a token and a path, and the room received only the fixed reason text.
That's a genuinely solid landing. The publish path is in much better shape than when I started — the txHash-only rule is the kind of invariant that stays correct under future edits, which the descriptor read-back never would have. From my side: merge whenever you're ready; I'd just fold the re-capture message, the DESIGN.md lines and the §6.5 test into your next commit rather than block on them.
Addendum — one correction, plus the new pinCorrection: I overstated the re-capture findingMy previous comment said the "re-capture the thread" instruction doesn't work for the case it names. That's too strong, and the distinction matters: The reason is the source set. So the message gives the operator a working route; it just also offers one that silently no-ops. That's a much smaller thing than I made it, and it isn't merge-relevant. Worth a word-level fix ("re-capture with The PR moved after I reviewedMy review was against Checked on
I have not put this pin through the full adversarial pass the previous three got, so treat the above as a smoke test rather than a clearance — the publish-path invariants are untouched by this diff, which is the part I'd have worried about. Still open on
|
What this PR does
Adds
buzz-dkg— an integration that joins a Block Buzz (Nostr/NIP-29) channel as a member and turns pinned decision threads into DKG Knowledge Assets across Working, Shared, and Verifiable Memory, preserving a PROV-O provenance chain to the signed source events. It also answers questions in-channel using deterministic, no-model retrieval scoped to the room's Context Graph (claims cited, unsupported questions refused).Integration links
0a5de8bc23449535e1673609a590d63271f01109manual(a standalone daemon run against an operator's own Buzz relay + DKG v10 edge node;docsUrl→ README.md)Scope & faithfulness
dkgCLI, MCP). It does not import internal DKG packages, patch node source, or write to SPARQL directly. (The integration's design principle is "no forks, no patches, on either side"; the DKG is used only through the edge node's public HTTP surface.)memoryLayerscorrectly reflects which layer(s) the integration touches (WM, SWM, VM).v10PrimitivesUsedcorrectly reflects which primitives are exercised.Security declarations (Section 8a)
security.networkEgresslists every external host (the operator-configured Buzz relay; the local DKG node on 127.0.0.1 does not count).security.writeAuthoritylists every DKG write operation; the Curator-authority op (vm/publish— PUBLISH to Verifiable Memory) is called out explicitly.security.credentialsHandledlists the one third-party credential (the Buzz service Nostr key).preinstall/install/postinstallscripts. (Install kind ismanual— no published package; the source repo has no install scripts.)manual, no published package).Contributor attestation
security.networkEgress.Notes for the committee
writeAuthoritydeclares a Verifiable-Memory publish (Curator authority), which is beyond a WM/SWM-only Round 1. The WM→SWM capture-and-receipt path is the integration's floor and runs on every pinned decision; Verifiable-Memory publish is opt-in and disabled by default (publishMode: 'disabled'), and when enabled is triple-gated: the node must report chainbase:8453, a rolling-24h publication budget applies, and each publish additionally requires a per-channel authorized promoter's approval reaction, consumed exactly once and validated against the receipt's KA + source-set digest. So the integration fully satisfies the WM/SWM scope while extending — only under explicit human attestation — to Verifiable Memory.manual. The integration is a standalone daemon (not yet an npm/docker package) that an operator runs against their own Buzz relay and DKG v10 edge node, binding a channel to a Context Graph. Setup, env, and the channel↔Context-Graph binding are documented inREADME.md+docs/DESIGN.md. Happy to publish a container image and resubmit askind: serviceif the committee prefers.wasDerivedFromchain to its signed Nostr source events (Schnorr-verifiable independently of the relay). A live example produced by this integration is published at UALdid:dkg:base:8453/0x633e5a7c5e612d9981538f60d824cc03be97e2ab/2201(tx0x6daf3e0bad8cba13f7508f69c5550750a30294e997bf5e7fdffe9a24170dbb38, block 49145748) in the FIFA World Cup 2026 Context Graph.Review history
0a5de8bafter review by @Jurij89:c42efd2(initial) →3cafe11(5 entry blockers + the ask-path defect + setup/docs) →9195710(3 publish-path regressions R1/R2/R3, Node floor>=22.13) →0a5de8b(deferred Add integration: memorygraph-dkg (verifiable persistent memory for AI agents) #18/Add DKG Contestation Protocol #19/ask-rate-limit/#20b). 74 tests; typecheck/lint/prettier clean.