@origintrail-official/dkg-node-ui 10.0.5 → 10.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db.d.ts +166 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +765 -32
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist-ui/assets/{3d-force-graph-DRDhpplH.js → 3d-force-graph-oy_vn0Oy.js} +1 -1
- package/dist-ui/assets/{AgentHub-DdUAxJoy.js → AgentHub-BdodC7bc.js} +1 -1
- package/dist-ui/assets/{AgentProfilePage-yI8fA4vv.js → AgentProfilePage-DsjmgCNC.js} +1 -1
- package/dist-ui/assets/{ApproveWalletsModal-9w2edv7e.js → ApproveWalletsModal-CoK9PC14.js} +18 -18
- package/dist-ui/assets/{ConvictionDetailView-CcQMya-y.js → ConvictionDetailView-DnpWB1CW.js} +2 -2
- package/dist-ui/assets/{Network-DpAKVzwi.js → Network-B1hFeJc_.js} +1 -1
- package/dist-ui/assets/{OnChainProvenanceCard-DMOm3jLz.js → OnChainProvenanceCard-5HtdkdH8.js} +1 -1
- package/dist-ui/assets/{Operations-DKJFp6hM.js → Operations-CD56ktgV.js} +1 -1
- package/dist-ui/assets/{PublishingConviction-1CPHxNwp.js → PublishingConviction-CsGM0rzZ.js} +2 -2
- package/dist-ui/assets/{Settings-DaYMlGHD.js → Settings-FtB9ej6P.js} +1 -1
- package/dist-ui/assets/{ccip-hCQYqfNz.js → ccip-C3eQxfL2.js} +1 -1
- package/dist-ui/assets/{index-DGpnb_b1.js → index-B0ozkBOt.js} +137 -137
- package/dist-ui/assets/{index-Bh_8bMB0.js → index-zKvsH-2o.js} +3 -3
- package/dist-ui/assets/{jsonld-32FQRO67-C3B_LjZ-.js → jsonld-32FQRO67-Ckj0IC7a.js} +2 -2
- package/dist-ui/assets/{jsonld-CoPRWQuM.js → jsonld-D76K907R.js} +1 -1
- package/dist-ui/assets/{renderer-3d-2EVDZII7-Bx6o7Gge.js → renderer-3d-2EVDZII7-C0_bWCJC.js} +2 -2
- package/dist-ui/assets/shikiHighlighter-FYa041eg.js +12 -0
- package/dist-ui/assets/{turtle-BsS91CYL.js → sparql-DVZY-KUA.js} +1 -1
- package/dist-ui/assets/{java-CylS5w8V.js → xml-B8PEiB0p.js} +1 -1
- package/dist-ui/index.html +1 -1
- package/package.json +4 -4
- package/dist-ui/assets/abap-BdImnpbu.js +0 -1
- package/dist-ui/assets/actionscript-3-CoDkCxhg.js +0 -1
- package/dist-ui/assets/ada-bCR0ucgS.js +0 -1
- package/dist-ui/assets/andromeeda-C4gqWexZ.js +0 -1
- package/dist-ui/assets/angular-html-DgL0RwuR.js +0 -1
- package/dist-ui/assets/angular-ts-DtEQuh-b.js +0 -1
- package/dist-ui/assets/apache-Pmp26Uib.js +0 -1
- package/dist-ui/assets/apex-D8_7TLub.js +0 -1
- package/dist-ui/assets/apl-CORt7UWP.js +0 -1
- package/dist-ui/assets/applescript-Co6uUVPk.js +0 -1
- package/dist-ui/assets/ara-BRHolxvo.js +0 -1
- package/dist-ui/assets/asciidoc-Ve4PFQV2.js +0 -1
- package/dist-ui/assets/asm-D_Q5rh1f.js +0 -1
- package/dist-ui/assets/astro-HNnZUWAn.js +0 -1
- package/dist-ui/assets/aurora-x-D-2ljcwZ.js +0 -1
- package/dist-ui/assets/awk-DMzUqQB5.js +0 -1
- package/dist-ui/assets/ayu-dark-DYE7WIF3.js +0 -1
- package/dist-ui/assets/ayu-light-BA47KaF1.js +0 -1
- package/dist-ui/assets/ayu-mirage-32ctXXKs.js +0 -1
- package/dist-ui/assets/ballerina-BFfxhgS-.js +0 -1
- package/dist-ui/assets/bat-BkioyH1T.js +0 -1
- package/dist-ui/assets/beancount-k_qm7-4y.js +0 -1
- package/dist-ui/assets/berry-uYugtg8r.js +0 -1
- package/dist-ui/assets/bibtex-CHM0blh-.js +0 -1
- package/dist-ui/assets/bicep-Bmn6On1c.js +0 -1
- package/dist-ui/assets/bird2-BIv1doCn.js +0 -1
- package/dist-ui/assets/blade-BjGOyj-B.js +0 -1
- package/dist-ui/assets/bsl-BO_Y6i37.js +0 -1
- package/dist-ui/assets/c-BIGW1oBm.js +0 -1
- package/dist-ui/assets/c3-eo99z4R2.js +0 -1
- package/dist-ui/assets/cadence-Bv_4Rxtq.js +0 -1
- package/dist-ui/assets/cairo-KRGpt6FW.js +0 -1
- package/dist-ui/assets/catppuccin-frappe-DFWUc33u.js +0 -1
- package/dist-ui/assets/catppuccin-latte-C9dUb6Cb.js +0 -1
- package/dist-ui/assets/catppuccin-macchiato-DQyhUUbL.js +0 -1
- package/dist-ui/assets/catppuccin-mocha-D87Tk5Gz.js +0 -1
- package/dist-ui/assets/clarity-D53aC0YG.js +0 -1
- package/dist-ui/assets/clojure-P80f7IUj.js +0 -1
- package/dist-ui/assets/cmake-D1j8_8rp.js +0 -1
- package/dist-ui/assets/cobol-nBiQ_Alo.js +0 -1
- package/dist-ui/assets/codeowners-Bp6g37R7.js +0 -1
- package/dist-ui/assets/codeql-DsOJ9woJ.js +0 -1
- package/dist-ui/assets/coffee-Ch7k5sss.js +0 -1
- package/dist-ui/assets/common-lisp-Cg-RD9OK.js +0 -1
- package/dist-ui/assets/coq-DkFqJrB1.js +0 -1
- package/dist-ui/assets/cpp-CofmeUqb.js +0 -1
- package/dist-ui/assets/crystal-DNxU26gB.js +0 -1
- package/dist-ui/assets/csharp-COcwbKMJ.js +0 -1
- package/dist-ui/assets/csv-fuZLfV_i.js +0 -1
- package/dist-ui/assets/cue-D82EKSYY.js +0 -1
- package/dist-ui/assets/cypher-COkxafJQ.js +0 -1
- package/dist-ui/assets/d-85-TOEBH.js +0 -1
- package/dist-ui/assets/dark-plus-C3mMm8J8.js +0 -1
- package/dist-ui/assets/dart-bE4Kk8sk.js +0 -1
- package/dist-ui/assets/dax-CEL-wOlO.js +0 -1
- package/dist-ui/assets/desktop-BmXAJ9_W.js +0 -1
- package/dist-ui/assets/dotenv-Da5cRb03.js +0 -1
- package/dist-ui/assets/dracula-BzJJZx-M.js +0 -1
- package/dist-ui/assets/dracula-soft-BXkSAIEj.js +0 -1
- package/dist-ui/assets/dream-maker-BtqSS_iP.js +0 -1
- package/dist-ui/assets/edge-FbVlp4U3.js +0 -1
- package/dist-ui/assets/elixir-CkH2-t6x.js +0 -1
- package/dist-ui/assets/elm-DbKCFpqz.js +0 -1
- package/dist-ui/assets/emacs-lisp-CXvaQtF9.js +0 -1
- package/dist-ui/assets/erb-BYCe7drp.js +0 -1
- package/dist-ui/assets/erlang-DsQrWhSR.js +0 -1
- package/dist-ui/assets/everforest-dark-BgDCqdQA.js +0 -1
- package/dist-ui/assets/everforest-light-C8M2exoo.js +0 -1
- package/dist-ui/assets/fennel-BYunw83y.js +0 -1
- package/dist-ui/assets/fish-BvzEVeQv.js +0 -1
- package/dist-ui/assets/fluent-C4IJs8-o.js +0 -1
- package/dist-ui/assets/fortran-fixed-form-CkoXwp7k.js +0 -1
- package/dist-ui/assets/fortran-free-form-BxgE0vQu.js +0 -1
- package/dist-ui/assets/fsharp-CXgrBDvD.js +0 -1
- package/dist-ui/assets/gdresource-BOOCDP_w.js +0 -1
- package/dist-ui/assets/gdscript-C5YyOfLZ.js +0 -1
- package/dist-ui/assets/gdshader-DkwncUOv.js +0 -1
- package/dist-ui/assets/genie-D0YGMca9.js +0 -1
- package/dist-ui/assets/gherkin-DyxjwDmM.js +0 -1
- package/dist-ui/assets/git-commit-F4YmCXRG.js +0 -1
- package/dist-ui/assets/git-rebase-r7XF79zn.js +0 -1
- package/dist-ui/assets/github-dark-DHJKELXO.js +0 -1
- package/dist-ui/assets/github-dark-default-Cuk6v7N8.js +0 -1
- package/dist-ui/assets/github-dark-dimmed-DH5Ifo-i.js +0 -1
- package/dist-ui/assets/github-dark-high-contrast-E3gJ1_iC.js +0 -1
- package/dist-ui/assets/github-light-DAi9KRSo.js +0 -1
- package/dist-ui/assets/github-light-default-D7oLnXFd.js +0 -1
- package/dist-ui/assets/github-light-high-contrast-BfjtVDDH.js +0 -1
- package/dist-ui/assets/gleam-BspZqrRM.js +0 -1
- package/dist-ui/assets/glimmer-js-ByusRIyA.js +0 -1
- package/dist-ui/assets/glimmer-ts-BfAWNZQY.js +0 -1
- package/dist-ui/assets/glsl-DplSGwfg.js +0 -1
- package/dist-ui/assets/gn-n2N0HUVH.js +0 -1
- package/dist-ui/assets/gnuplot-DdkO51Og.js +0 -1
- package/dist-ui/assets/graphql-ChdNCCLP.js +0 -1
- package/dist-ui/assets/groovy-gcz8RCvz.js +0 -1
- package/dist-ui/assets/gruvbox-dark-hard-CFHQjOhq.js +0 -1
- package/dist-ui/assets/gruvbox-dark-medium-GsRaNv29.js +0 -1
- package/dist-ui/assets/gruvbox-dark-soft-CVdnzihN.js +0 -1
- package/dist-ui/assets/gruvbox-light-hard-CH1njM8p.js +0 -1
- package/dist-ui/assets/gruvbox-light-medium-DRw_LuNl.js +0 -1
- package/dist-ui/assets/gruvbox-light-soft-hJgmCMqR.js +0 -1
- package/dist-ui/assets/hack-i7_Ulhet.js +0 -1
- package/dist-ui/assets/haml-D5jkg6IW.js +0 -1
- package/dist-ui/assets/handlebars-BpdQsYii.js +0 -1
- package/dist-ui/assets/haskell-Df6bDoY_.js +0 -1
- package/dist-ui/assets/haxe-CzTSHFRz.js +0 -1
- package/dist-ui/assets/hcl-BWvSN4gD.js +0 -1
- package/dist-ui/assets/hjson-D5-asLiD.js +0 -1
- package/dist-ui/assets/hlsl-D3lLCCz7.js +0 -1
- package/dist-ui/assets/horizon-BUw7H-hv.js +0 -1
- package/dist-ui/assets/horizon-bright-CUuTKBJd.js +0 -1
- package/dist-ui/assets/houston-DnULxvSX.js +0 -1
- package/dist-ui/assets/html-derivative-DlHx6ybY.js +0 -1
- package/dist-ui/assets/http-jrhK8wxY.js +0 -1
- package/dist-ui/assets/hurl-irOxFIW8.js +0 -1
- package/dist-ui/assets/hxml-Bvhsp5Yf.js +0 -1
- package/dist-ui/assets/hy-DFXneXwc.js +0 -1
- package/dist-ui/assets/imba-DGztddWO.js +0 -1
- package/dist-ui/assets/index-CVwCoJrM.js +0 -153
- package/dist-ui/assets/ini-BEwlwnbL.js +0 -1
- package/dist-ui/assets/jinja-f2NsQr07.js +0 -1
- package/dist-ui/assets/jison-wvAkD_A8.js +0 -1
- package/dist-ui/assets/json5-C9tS-k6U.js +0 -1
- package/dist-ui/assets/jsonc-Des-eS-w.js +0 -1
- package/dist-ui/assets/jsonl-DcaNXYhu.js +0 -1
- package/dist-ui/assets/jsonnet-DFQXde-d.js +0 -1
- package/dist-ui/assets/jssm-C2t-YnRu.js +0 -1
- package/dist-ui/assets/julia-CxzCAyBv.js +0 -1
- package/dist-ui/assets/just-VxiPbLrw.js +0 -1
- package/dist-ui/assets/kanagawa-dragon-CkXjmgJE.js +0 -1
- package/dist-ui/assets/kanagawa-lotus-CfQXZHmo.js +0 -1
- package/dist-ui/assets/kanagawa-wave-DWedfzmr.js +0 -1
- package/dist-ui/assets/kdl-DV7GczEv.js +0 -1
- package/dist-ui/assets/kotlin-BdnUsdx6.js +0 -1
- package/dist-ui/assets/kusto-wEQ09or8.js +0 -1
- package/dist-ui/assets/laserwave-DUszq2jm.js +0 -1
- package/dist-ui/assets/latex-CWtU0Tv5.js +0 -1
- package/dist-ui/assets/lean-BZvkOJ9d.js +0 -1
- package/dist-ui/assets/less-B1dDrJ26.js +0 -1
- package/dist-ui/assets/light-plus-B7mTdjB0.js +0 -1
- package/dist-ui/assets/liquid-C0sCDyMI.js +0 -1
- package/dist-ui/assets/llvm-DjAJT7YJ.js +0 -1
- package/dist-ui/assets/log-2UxHyX5q.js +0 -1
- package/dist-ui/assets/logo-BtOb2qkB.js +0 -1
- package/dist-ui/assets/lua-BaeVxFsk.js +0 -1
- package/dist-ui/assets/luau-C-HG3fhB.js +0 -1
- package/dist-ui/assets/make-CHLpvVh8.js +0 -1
- package/dist-ui/assets/marko-DjSrsDqO.js +0 -1
- package/dist-ui/assets/material-theme-D5KoaKCx.js +0 -1
- package/dist-ui/assets/material-theme-darker-BfHTSMKl.js +0 -1
- package/dist-ui/assets/material-theme-lighter-B0m2ddpp.js +0 -1
- package/dist-ui/assets/material-theme-ocean-CyktbL80.js +0 -1
- package/dist-ui/assets/material-theme-palenight-Csfq5Kiy.js +0 -1
- package/dist-ui/assets/matlab-D7o27uSR.js +0 -1
- package/dist-ui/assets/mdc-DTYItulj.js +0 -1
- package/dist-ui/assets/mdx-Cmh6b_Ma.js +0 -1
- package/dist-ui/assets/mermaid-mWjccvbQ.js +0 -1
- package/dist-ui/assets/min-dark-CafNBF8u.js +0 -1
- package/dist-ui/assets/min-light-CTRr51gU.js +0 -1
- package/dist-ui/assets/mipsasm-CKIfxQSi.js +0 -1
- package/dist-ui/assets/mojo-rZm6bMo-.js +0 -1
- package/dist-ui/assets/monokai-D4h5O-jR.js +0 -1
- package/dist-ui/assets/moonbit-_H4v1dQx.js +0 -1
- package/dist-ui/assets/move-IF9eRakj.js +0 -1
- package/dist-ui/assets/narrat-DRg8JJMk.js +0 -1
- package/dist-ui/assets/nextflow-C-mBbutL.js +0 -1
- package/dist-ui/assets/nextflow-groovy-vE_lwT2v.js +0 -1
- package/dist-ui/assets/nginx-BpAMiNFr.js +0 -1
- package/dist-ui/assets/night-owl-C39BiMTA.js +0 -1
- package/dist-ui/assets/night-owl-light-CMTm3GFP.js +0 -1
- package/dist-ui/assets/nim-BIad80T-.js +0 -1
- package/dist-ui/assets/nix-CwoSXNpI.js +0 -1
- package/dist-ui/assets/nord-Ddv68eIx.js +0 -1
- package/dist-ui/assets/nushell-Cz2AlsmD.js +0 -1
- package/dist-ui/assets/objective-c-DXmwc3jG.js +0 -1
- package/dist-ui/assets/objective-cpp-CLxacb5B.js +0 -1
- package/dist-ui/assets/ocaml-C0hk2d4L.js +0 -1
- package/dist-ui/assets/odin-BBf5iR-q.js +0 -1
- package/dist-ui/assets/one-dark-pro-DVMEJ2y_.js +0 -1
- package/dist-ui/assets/one-light-C3Wv6jpd.js +0 -1
- package/dist-ui/assets/openscad-C4EeE6gA.js +0 -1
- package/dist-ui/assets/pascal-D93ZcfNL.js +0 -1
- package/dist-ui/assets/perl-NvoQZIq0.js +0 -1
- package/dist-ui/assets/php-R6g_5hLQ.js +0 -1
- package/dist-ui/assets/pkl-u5AG7uiY.js +0 -1
- package/dist-ui/assets/plastic-3e1v2bzS.js +0 -1
- package/dist-ui/assets/plsql-ChMvpjG-.js +0 -1
- package/dist-ui/assets/po-BTJTHyun.js +0 -1
- package/dist-ui/assets/poimandres-CS3Unz2-.js +0 -1
- package/dist-ui/assets/polar-C0HS_06l.js +0 -1
- package/dist-ui/assets/postcss-CXtECtnM.js +0 -1
- package/dist-ui/assets/powerquery-CEu0bR-o.js +0 -1
- package/dist-ui/assets/powershell-Dpen1YoG.js +0 -1
- package/dist-ui/assets/prisma-Dd19v3D-.js +0 -1
- package/dist-ui/assets/prolog-CbFg5uaA.js +0 -1
- package/dist-ui/assets/proto-C7zT0LnQ.js +0 -1
- package/dist-ui/assets/pug-DKIMFp6K.js +0 -1
- package/dist-ui/assets/puppet-BMWR74SV.js +0 -1
- package/dist-ui/assets/purescript-CklMAg4u.js +0 -1
- package/dist-ui/assets/qml-3beO22l8.js +0 -1
- package/dist-ui/assets/qmldir-C8lEn-DE.js +0 -1
- package/dist-ui/assets/qss-IeuSbFQv.js +0 -1
- package/dist-ui/assets/r-Dspwwk_N.js +0 -1
- package/dist-ui/assets/racket-BqYA7rlc.js +0 -1
- package/dist-ui/assets/raku-DXvB9xmW.js +0 -1
- package/dist-ui/assets/razor-BDqjjVU7.js +0 -1
- package/dist-ui/assets/red-bN70gL4F.js +0 -1
- package/dist-ui/assets/reg-C-SQnVFl.js +0 -1
- package/dist-ui/assets/regexp-CDVJQ6XC.js +0 -1
- package/dist-ui/assets/rel-C3B-1QV4.js +0 -1
- package/dist-ui/assets/riscv-BM1_JUlF.js +0 -1
- package/dist-ui/assets/ron-D8l8udqQ.js +0 -1
- package/dist-ui/assets/rose-pine-dawn-DHQR4-dF.js +0 -1
- package/dist-ui/assets/rose-pine-moon-D4_iv3hh.js +0 -1
- package/dist-ui/assets/rose-pine-qdsjHGoJ.js +0 -1
- package/dist-ui/assets/rosmsg-BJDFO7_C.js +0 -1
- package/dist-ui/assets/rst-CRjBmOyv.js +0 -1
- package/dist-ui/assets/ruby-Wjq7vjNf.js +0 -1
- package/dist-ui/assets/sas-cz2c8ADy.js +0 -1
- package/dist-ui/assets/sass-Cj5Yp3dK.js +0 -1
- package/dist-ui/assets/scala-C151Ov-r.js +0 -1
- package/dist-ui/assets/scheme-C98Dy4si.js +0 -1
- package/dist-ui/assets/scss-D5BDwBP9.js +0 -1
- package/dist-ui/assets/sdbl-DVxCFoDh.js +0 -1
- package/dist-ui/assets/shaderlab-Dg9Lc6iA.js +0 -1
- package/dist-ui/assets/shellsession-BADoaaVG.js +0 -1
- package/dist-ui/assets/slack-dark-BthQWCQV.js +0 -1
- package/dist-ui/assets/slack-ochin-DqwNpetd.js +0 -1
- package/dist-ui/assets/smalltalk-BERRCDM3.js +0 -1
- package/dist-ui/assets/snazzy-light-Bw305WKR.js +0 -1
- package/dist-ui/assets/solarized-dark-DXbdFlpD.js +0 -1
- package/dist-ui/assets/solarized-light-L9t79GZl.js +0 -1
- package/dist-ui/assets/soy-8wufbnw4.js +0 -1
- package/dist-ui/assets/sparql-rVzFXLq3.js +0 -1
- package/dist-ui/assets/splunk-BtCnVYZw.js +0 -1
- package/dist-ui/assets/ssh-config-_ykCGR6B.js +0 -1
- package/dist-ui/assets/stata-BH5u7GGu.js +0 -1
- package/dist-ui/assets/stylus-BEDo0Tqx.js +0 -1
- package/dist-ui/assets/surrealql-Bq5Q-fJD.js +0 -1
- package/dist-ui/assets/svelte-Cy7k_4gC.js +0 -1
- package/dist-ui/assets/swift-D82vCrfD.js +0 -1
- package/dist-ui/assets/synthwave-84-CbfX1IO0.js +0 -1
- package/dist-ui/assets/system-verilog-CnnmHF94.js +0 -1
- package/dist-ui/assets/systemd-4A_iFExJ.js +0 -1
- package/dist-ui/assets/talonscript-CkByrt1z.js +0 -1
- package/dist-ui/assets/tasl-QIJgUcNo.js +0 -1
- package/dist-ui/assets/tcl-dwOrl1Do.js +0 -1
- package/dist-ui/assets/templ-DhtptRzy.js +0 -1
- package/dist-ui/assets/terraform-BETggiCN.js +0 -1
- package/dist-ui/assets/tex-idrVyKtj.js +0 -1
- package/dist-ui/assets/tokyo-night-hegEt444.js +0 -1
- package/dist-ui/assets/ts-tags-DQrlYJgV.js +0 -1
- package/dist-ui/assets/tsv-B_m7g4N7.js +0 -1
- package/dist-ui/assets/twig-xg9kU7Mw.js +0 -1
- package/dist-ui/assets/typespec-CAFt9gP4.js +0 -1
- package/dist-ui/assets/typst-DHCkPAjA.js +0 -1
- package/dist-ui/assets/v-BcVCzyr7.js +0 -1
- package/dist-ui/assets/vala-CsfeWuGM.js +0 -1
- package/dist-ui/assets/vb-D17OF-Vu.js +0 -1
- package/dist-ui/assets/verilog-BQ8w6xss.js +0 -1
- package/dist-ui/assets/vesper-DU1UobuO.js +0 -1
- package/dist-ui/assets/vhdl-CeAyd5Ju.js +0 -1
- package/dist-ui/assets/viml-CJc9bBzg.js +0 -1
- package/dist-ui/assets/vitesse-black-Bkuqu6BP.js +0 -1
- package/dist-ui/assets/vitesse-dark-D0r3Knsf.js +0 -1
- package/dist-ui/assets/vitesse-light-CVO1_9PV.js +0 -1
- package/dist-ui/assets/vue-D2xRrEX4.js +0 -1
- package/dist-ui/assets/vue-html-AaS7Mt5G.js +0 -1
- package/dist-ui/assets/vue-vine-BoDAl6tE.js +0 -1
- package/dist-ui/assets/vyper-CDx5xZoG.js +0 -1
- package/dist-ui/assets/wasm-MzD3tlZU.js +0 -1
- package/dist-ui/assets/wenyan-BV7otONQ.js +0 -1
- package/dist-ui/assets/wgsl-Dx-B1_4e.js +0 -1
- package/dist-ui/assets/wikitext-BhOHFoWU.js +0 -1
- package/dist-ui/assets/wit-5i3qLPDT.js +0 -1
- package/dist-ui/assets/wolfram-lXgVvXCa.js +0 -1
- package/dist-ui/assets/xml-sdJ4AIDG.js +0 -1
- package/dist-ui/assets/xsl-CtQFsRM5.js +0 -1
- package/dist-ui/assets/zenscript-DVFEvuxE.js +0 -1
- package/dist-ui/assets/zig-VOosw3JB.js +0 -1
- /package/dist-ui/assets/{shellscript-Yzrsuije.js → bash-Yzrsuije.js} +0 -0
- /package/dist-ui/assets/{docker-BcOcwvcX.js → dockerfile-BcOcwvcX.js} +0 -0
package/dist/db.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { RESPONSE_CACHE_BYTES, } from '@origintrail-official/dkg-core';
|
|
3
|
+
import { RESPONSE_CACHE_BYTES, parseContextGraphJoinPolicyRecord, } from '@origintrail-official/dkg-core';
|
|
4
4
|
export { SqliteChainEventCursorStore, SqliteContextGraphRegistryScanCursorStore, } from './chain-cursor-stores.js';
|
|
5
|
-
const SCHEMA_VERSION =
|
|
5
|
+
const SCHEMA_VERSION = 29;
|
|
6
6
|
// Default operator retention. Lowered from 90 → 14 days on V15 (2026-05) after
|
|
7
7
|
// a production incident in which the `logs` table + its FTS5 shadow tables
|
|
8
8
|
// grew to ~9 GB on a 12-day-old node and corrupted the SQLite page (header
|
|
9
9
|
// hash mismatch on boot). 90 days had been chosen for "metrics history",
|
|
10
10
|
// but logs were the dominant grower (~1M rows/12d) and pruning was a no-op
|
|
11
|
-
// on any DB younger than 90 days. 14 days is still long enough for
|
|
12
|
-
//
|
|
13
|
-
//
|
|
11
|
+
// on any DB younger than 90 days. 14 days is still long enough for a useful
|
|
12
|
+
// operator-driven post-mortem, but the mainnet sync storm proved that time
|
|
13
|
+
// retention alone cannot bound worst-case growth. Operators who want longer
|
|
14
14
|
// retention can override via `setRetentionDays()`; the setting is persisted
|
|
15
|
-
// in the `settings` table and re-read on next boot.
|
|
15
|
+
// in the `settings` table and re-read on next boot. Time alone is not a hard
|
|
16
|
+
// size bound during a log storm, so routine info/debug rows also have a count
|
|
17
|
+
// ceiling. Warning/error rows keep the full operator-selected time window.
|
|
16
18
|
const DEFAULT_RETENTION_DAYS = 14;
|
|
17
19
|
const LEGACY_IMPLICIT_RETENTION_DAYS = 90;
|
|
20
|
+
const DEFAULT_ROUTINE_LOG_ROW_CAP = 1_000_000;
|
|
21
|
+
const DEFAULT_LOG_VOLUME_PRUNE_BATCH_ROWS = 25_000;
|
|
18
22
|
const LOGS_VACUUM_DELETE_THRESHOLD = 10_000;
|
|
19
23
|
// SQLite reports reusable-but-not-yet-reclaimed pages via freelist_count.
|
|
20
24
|
// With the default 4 KiB page size this is roughly 4 MiB, large enough
|
|
@@ -100,10 +104,14 @@ export class DashboardDB {
|
|
|
100
104
|
dataDir;
|
|
101
105
|
retentionDays;
|
|
102
106
|
explicitRetentionDays;
|
|
107
|
+
routineLogRowCap;
|
|
108
|
+
logVolumePruneBatchRows;
|
|
103
109
|
constructor(opts) {
|
|
104
110
|
this.dataDir = opts.dataDir;
|
|
105
111
|
this.explicitRetentionDays = opts.retentionDays !== undefined;
|
|
106
112
|
this.retentionDays = opts.retentionDays ?? DEFAULT_RETENTION_DAYS;
|
|
113
|
+
this.routineLogRowCap = Math.max(0, Math.floor(opts.routineLogRowCap ?? DEFAULT_ROUTINE_LOG_ROW_CAP));
|
|
114
|
+
this.logVolumePruneBatchRows = Math.max(1, Math.floor(opts.logVolumePruneBatchRows ?? DEFAULT_LOG_VOLUME_PRUNE_BATCH_ROWS));
|
|
107
115
|
this._memoTtlMs = resolveCacheTtlMs(opts.cacheTtlMs);
|
|
108
116
|
const dbPath = join(opts.dataDir, 'node-ui.db');
|
|
109
117
|
this.db = new Database(dbPath);
|
|
@@ -130,8 +138,49 @@ export class DashboardDB {
|
|
|
130
138
|
migrate() {
|
|
131
139
|
const version = this.db.pragma('user_version', { simple: true });
|
|
132
140
|
const upgradedExistingDb = version > 0 && version < SCHEMA_VERSION;
|
|
133
|
-
|
|
141
|
+
const ensureJoinPolicyAuditCapTrigger = () => this.db.exec(`
|
|
142
|
+
CREATE TRIGGER IF NOT EXISTS cap_cg_join_policy_audit_rows
|
|
143
|
+
AFTER INSERT ON context_graph_join_policy_audit
|
|
144
|
+
BEGIN
|
|
145
|
+
DELETE FROM context_graph_join_policy_audit
|
|
146
|
+
WHERE id <= NEW.id - 100000
|
|
147
|
+
AND event_type NOT IN (
|
|
148
|
+
'join_admission_committed',
|
|
149
|
+
'join_policy_changed'
|
|
150
|
+
);
|
|
151
|
+
END;
|
|
152
|
+
`);
|
|
153
|
+
const ensureJoinApprovalRepairMarker = () => {
|
|
154
|
+
const table = this.db.prepare(`
|
|
155
|
+
SELECT 1 AS found FROM sqlite_master
|
|
156
|
+
WHERE type = 'table' AND name = 'context_graph_join_approval_ledger'
|
|
157
|
+
`).get();
|
|
158
|
+
if (!table)
|
|
159
|
+
return;
|
|
160
|
+
const columns = this.db.pragma('table_info(context_graph_join_approval_ledger)');
|
|
161
|
+
if (!columns.some((column) => column.name === 'repair_pending')) {
|
|
162
|
+
this.db.exec(`
|
|
163
|
+
ALTER TABLE context_graph_join_approval_ledger
|
|
164
|
+
ADD COLUMN repair_pending INTEGER NOT NULL DEFAULT 0
|
|
165
|
+
CHECK (repair_pending IN (0, 1));
|
|
166
|
+
`);
|
|
167
|
+
}
|
|
168
|
+
this.db.exec(`
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_cg_join_approval_ledger_repair
|
|
170
|
+
ON context_graph_join_approval_ledger(
|
|
171
|
+
context_graph_id, request_digest, repair_pending, reserved_at DESC
|
|
172
|
+
);
|
|
173
|
+
`);
|
|
174
|
+
};
|
|
175
|
+
if (version > SCHEMA_VERSION)
|
|
134
176
|
return;
|
|
177
|
+
if (version === SCHEMA_VERSION) {
|
|
178
|
+
// Repair restored/development databases that carry the current version
|
|
179
|
+
// but lost an idempotent schema adjunct.
|
|
180
|
+
ensureJoinApprovalRepairMarker();
|
|
181
|
+
ensureJoinPolicyAuditCapTrigger();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
135
184
|
if (version < 1) {
|
|
136
185
|
this.db.exec(`
|
|
137
186
|
CREATE TABLE IF NOT EXISTS metric_snapshots (
|
|
@@ -483,12 +532,11 @@ export class DashboardDB {
|
|
|
483
532
|
// (i.e. the `ReliableEnvelope` proto output, not the raw
|
|
484
533
|
// application payload), so retries replay byte-identical
|
|
485
534
|
// frames without re-encoding.
|
|
486
|
-
// - `idx_outbox_next_attempt` indexes the
|
|
487
|
-
// (`SELECT ... WHERE next_attempt_at <= ?`)
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
// queue sizes (~tens of entries per peer).
|
|
535
|
+
// - `idx_outbox_next_attempt` indexes the sole automatic retry query
|
|
536
|
+
// (`SELECT ... WHERE next_attempt_at <= ?`). The peer-scoped query is
|
|
537
|
+
// retained only for diagnostics/DHT bookkeeping and MUST NOT become a
|
|
538
|
+
// connection-triggered drain; it uses the PK prefix at expected queue
|
|
539
|
+
// sizes (~tens of entries per peer).
|
|
492
540
|
//
|
|
493
541
|
// No data migration: pure additive. The chat-specific
|
|
494
542
|
// `idx_chat_msgid` from V11 stays in place — PR-3 will drop
|
|
@@ -744,6 +792,214 @@ export class DashboardDB {
|
|
|
744
792
|
this.db.exec(`
|
|
745
793
|
CREATE INDEX IF NOT EXISTS idx_runtime_cursors_namespace_scope
|
|
746
794
|
ON runtime_cursors(namespace, scope);
|
|
795
|
+
`);
|
|
796
|
+
}
|
|
797
|
+
if (version < 23) {
|
|
798
|
+
// OT-RFC-59 SC5: durable per-(peer, contextGraph) changelog cursor. NEVER
|
|
799
|
+
// pruned (see SqliteChangelogCursorStore) — a TTL would defeat the O(delta)
|
|
800
|
+
// cross-restart catch-up this table exists for.
|
|
801
|
+
this.db.exec(`
|
|
802
|
+
CREATE TABLE IF NOT EXISTS changelog_cursors (
|
|
803
|
+
peer_id TEXT NOT NULL,
|
|
804
|
+
context_graph_id TEXT NOT NULL,
|
|
805
|
+
era TEXT NOT NULL,
|
|
806
|
+
seq INTEGER NOT NULL CHECK (seq >= 0),
|
|
807
|
+
updated_at INTEGER NOT NULL,
|
|
808
|
+
PRIMARY KEY (peer_id, context_graph_id)
|
|
809
|
+
);
|
|
810
|
+
`);
|
|
811
|
+
}
|
|
812
|
+
if (version < 24) {
|
|
813
|
+
// OT-RFC-59 §6 P0 durable era guard: the write-side ChangelogStore's
|
|
814
|
+
// restore-detection needs the (era, highSeq) high-water in a store that a
|
|
815
|
+
// `store.nq` restore does NOT roll back with the RDF store. node-ui.db
|
|
816
|
+
// survives such a restore, so a rollback under the same era rotates the era
|
|
817
|
+
// (forcing peers to full-resync instead of silently skipping). Single row.
|
|
818
|
+
this.db.exec(`
|
|
819
|
+
CREATE TABLE IF NOT EXISTS changelog_era (
|
|
820
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
821
|
+
era TEXT NOT NULL,
|
|
822
|
+
high_seq INTEGER NOT NULL CHECK (high_seq >= 0),
|
|
823
|
+
updated_at INTEGER NOT NULL
|
|
824
|
+
);
|
|
825
|
+
`);
|
|
826
|
+
}
|
|
827
|
+
if (version < 25) {
|
|
828
|
+
// Durable security audit for private-CG open enrollment. V25/V26 also
|
|
829
|
+
// used this table as admission state; V27 projects those legacy rows into
|
|
830
|
+
// the typed operational ledger below.
|
|
831
|
+
this.db.exec(`
|
|
832
|
+
CREATE TABLE IF NOT EXISTS context_graph_join_policy_audit (
|
|
833
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
834
|
+
ts INTEGER NOT NULL,
|
|
835
|
+
context_graph_id TEXT NOT NULL,
|
|
836
|
+
event_type TEXT NOT NULL,
|
|
837
|
+
actor TEXT,
|
|
838
|
+
agent_address TEXT,
|
|
839
|
+
outcome TEXT NOT NULL,
|
|
840
|
+
reason TEXT,
|
|
841
|
+
request_digest TEXT,
|
|
842
|
+
policy_version INTEGER,
|
|
843
|
+
details TEXT
|
|
844
|
+
);
|
|
845
|
+
CREATE INDEX IF NOT EXISTS idx_cg_join_policy_audit_ts
|
|
846
|
+
ON context_graph_join_policy_audit(ts);
|
|
847
|
+
CREATE INDEX IF NOT EXISTS idx_cg_join_policy_audit_cg_ts
|
|
848
|
+
ON context_graph_join_policy_audit(context_graph_id, ts);
|
|
849
|
+
CREATE INDEX IF NOT EXISTS idx_cg_join_policy_audit_event_ts
|
|
850
|
+
ON context_graph_join_policy_audit(event_type, ts);
|
|
851
|
+
`);
|
|
852
|
+
}
|
|
853
|
+
if (version < 26) {
|
|
854
|
+
// Bound flood-generated decision noise without letting that same flood
|
|
855
|
+
// erase the two records needed to reconstruct a private-CG admission:
|
|
856
|
+
// who changed the policy, and who was automatically admitted under it.
|
|
857
|
+
// V26 additionally retained live reservations because they still enforced
|
|
858
|
+
// the rolling one-hour ceilings. V27 removes that dependency below.
|
|
859
|
+
//
|
|
860
|
+
// V25 already installed this trigger, so DROP before CREATE is required:
|
|
861
|
+
// CREATE TRIGGER IF NOT EXISTS would silently preserve the old predicate
|
|
862
|
+
// on an upgraded database.
|
|
863
|
+
this.db.exec('DROP TRIGGER IF EXISTS cap_cg_join_policy_audit_rows;');
|
|
864
|
+
}
|
|
865
|
+
if (version < 27) {
|
|
866
|
+
// Operational admission state is deliberately separate from the audit
|
|
867
|
+
// stream. Audit retention and event naming must never alter rate-limit,
|
|
868
|
+
// epoch, or commit-idempotency behaviour.
|
|
869
|
+
this.db.exec(`
|
|
870
|
+
CREATE TABLE IF NOT EXISTS context_graph_join_approval_ledger (
|
|
871
|
+
context_graph_id TEXT NOT NULL,
|
|
872
|
+
request_digest TEXT NOT NULL,
|
|
873
|
+
policy_epoch INTEGER NOT NULL,
|
|
874
|
+
reserved_at INTEGER NOT NULL,
|
|
875
|
+
state TEXT NOT NULL CHECK (state IN ('reserved', 'committed')),
|
|
876
|
+
committed_at INTEGER,
|
|
877
|
+
actor TEXT NOT NULL,
|
|
878
|
+
agent_address TEXT NOT NULL,
|
|
879
|
+
policy_version INTEGER NOT NULL,
|
|
880
|
+
PRIMARY KEY (context_graph_id, request_digest, policy_epoch),
|
|
881
|
+
CHECK (length(context_graph_id) > 0),
|
|
882
|
+
CHECK (length(request_digest) > 0),
|
|
883
|
+
CHECK (typeof(policy_epoch) = 'integer'),
|
|
884
|
+
CHECK (typeof(reserved_at) = 'integer'),
|
|
885
|
+
CHECK (committed_at IS NULL OR typeof(committed_at) = 'integer'),
|
|
886
|
+
CHECK (typeof(policy_version) = 'integer'),
|
|
887
|
+
CHECK (
|
|
888
|
+
(state = 'reserved' AND committed_at IS NULL)
|
|
889
|
+
OR (state = 'committed' AND committed_at IS NOT NULL)
|
|
890
|
+
)
|
|
891
|
+
);
|
|
892
|
+
CREATE INDEX IF NOT EXISTS idx_cg_join_approval_ledger_reserved_at
|
|
893
|
+
ON context_graph_join_approval_ledger(reserved_at);
|
|
894
|
+
CREATE INDEX IF NOT EXISTS idx_cg_join_approval_ledger_cg_reserved_at
|
|
895
|
+
ON context_graph_join_approval_ledger(context_graph_id, reserved_at);
|
|
896
|
+
CREATE INDEX IF NOT EXISTS idx_cg_join_approval_ledger_digest_reserved_at
|
|
897
|
+
ON context_graph_join_approval_ledger(context_graph_id, request_digest, reserved_at DESC);
|
|
898
|
+
CREATE INDEX IF NOT EXISTS idx_cg_join_approval_ledger_committed_at
|
|
899
|
+
ON context_graph_join_approval_ledger(committed_at);
|
|
900
|
+
`);
|
|
901
|
+
// Best-effort one-time projection for V25/V26 databases. Invalid legacy
|
|
902
|
+
// JSON cannot abort startup; only rows with a typed numeric epoch can be
|
|
903
|
+
// authoritative operational state. For duplicate audit reservations,
|
|
904
|
+
// retain the newest row for each (CG, digest, epoch).
|
|
905
|
+
this.db.exec(`
|
|
906
|
+
WITH raw_reservations AS (
|
|
907
|
+
SELECT
|
|
908
|
+
id,
|
|
909
|
+
context_graph_id,
|
|
910
|
+
request_digest,
|
|
911
|
+
CASE
|
|
912
|
+
WHEN json_valid(details)
|
|
913
|
+
AND json_type(details, '$.policyEpoch') IN ('integer', 'real')
|
|
914
|
+
THEN CAST(json_extract(details, '$.policyEpoch') AS INTEGER)
|
|
915
|
+
ELSE NULL
|
|
916
|
+
END AS policy_epoch,
|
|
917
|
+
ts AS reserved_at,
|
|
918
|
+
COALESCE(actor, '') AS actor,
|
|
919
|
+
COALESCE(agent_address, '') AS agent_address,
|
|
920
|
+
COALESCE(policy_version, 1) AS policy_version
|
|
921
|
+
FROM context_graph_join_policy_audit
|
|
922
|
+
WHERE event_type = 'join_auto_reservation'
|
|
923
|
+
AND outcome = 'reserved'
|
|
924
|
+
AND request_digest IS NOT NULL
|
|
925
|
+
),
|
|
926
|
+
ranked_reservations AS (
|
|
927
|
+
SELECT *, ROW_NUMBER() OVER (
|
|
928
|
+
PARTITION BY context_graph_id, request_digest, policy_epoch
|
|
929
|
+
ORDER BY reserved_at DESC, id DESC
|
|
930
|
+
) AS rank
|
|
931
|
+
FROM raw_reservations
|
|
932
|
+
WHERE policy_epoch IS NOT NULL
|
|
933
|
+
),
|
|
934
|
+
raw_commits AS (
|
|
935
|
+
SELECT
|
|
936
|
+
context_graph_id,
|
|
937
|
+
request_digest,
|
|
938
|
+
CASE
|
|
939
|
+
WHEN json_valid(details)
|
|
940
|
+
AND json_type(details, '$.policyEpoch') IN ('integer', 'real')
|
|
941
|
+
THEN CAST(json_extract(details, '$.policyEpoch') AS INTEGER)
|
|
942
|
+
ELSE NULL
|
|
943
|
+
END AS policy_epoch,
|
|
944
|
+
MIN(ts) AS committed_at
|
|
945
|
+
FROM context_graph_join_policy_audit
|
|
946
|
+
WHERE event_type = 'join_admission_committed'
|
|
947
|
+
AND outcome = 'approved'
|
|
948
|
+
AND request_digest IS NOT NULL
|
|
949
|
+
GROUP BY context_graph_id, request_digest, policy_epoch
|
|
950
|
+
)
|
|
951
|
+
INSERT OR IGNORE INTO context_graph_join_approval_ledger (
|
|
952
|
+
context_graph_id, request_digest, policy_epoch, reserved_at,
|
|
953
|
+
state, committed_at, actor, agent_address, policy_version
|
|
954
|
+
)
|
|
955
|
+
SELECT
|
|
956
|
+
reservation.context_graph_id,
|
|
957
|
+
reservation.request_digest,
|
|
958
|
+
reservation.policy_epoch,
|
|
959
|
+
reservation.reserved_at,
|
|
960
|
+
CASE WHEN commit_row.committed_at IS NULL THEN 'reserved' ELSE 'committed' END,
|
|
961
|
+
commit_row.committed_at,
|
|
962
|
+
reservation.actor,
|
|
963
|
+
reservation.agent_address,
|
|
964
|
+
reservation.policy_version
|
|
965
|
+
FROM ranked_reservations AS reservation
|
|
966
|
+
LEFT JOIN raw_commits AS commit_row
|
|
967
|
+
ON commit_row.context_graph_id = reservation.context_graph_id
|
|
968
|
+
AND commit_row.request_digest = reservation.request_digest
|
|
969
|
+
AND commit_row.policy_epoch = reservation.policy_epoch
|
|
970
|
+
WHERE reservation.rank = 1;
|
|
971
|
+
`);
|
|
972
|
+
// V26's trigger protected live reservation audit rows because they were
|
|
973
|
+
// operational state. The ledger makes that coupling obsolete, so replace
|
|
974
|
+
// the trigger while preserving the flood-resistant proof exemptions.
|
|
975
|
+
this.db.exec('DROP TRIGGER IF EXISTS cap_cg_join_policy_audit_rows;');
|
|
976
|
+
}
|
|
977
|
+
if (version < 28) {
|
|
978
|
+
// A reservation alone does not prove that admission crossed its
|
|
979
|
+
// membership boundary. Persist that distinction so an exact retry can
|
|
980
|
+
// finish moderation/audit after delegation expiry or daemon restart.
|
|
981
|
+
ensureJoinApprovalRepairMarker();
|
|
982
|
+
}
|
|
983
|
+
// Keep this repair outside the version gate. Restored/development DBs can
|
|
984
|
+
// carry the current user_version while missing a trigger; recreating it is
|
|
985
|
+
// idempotent and keeps the audit bound fail-closed on every open.
|
|
986
|
+
ensureJoinPolicyAuditCapTrigger();
|
|
987
|
+
if (version < 29) {
|
|
988
|
+
this.db.exec(`
|
|
989
|
+
CREATE TABLE IF NOT EXISTS vm_reconcile_negative_cache (
|
|
990
|
+
cache_key TEXT PRIMARY KEY,
|
|
991
|
+
context_graph_id TEXT NOT NULL,
|
|
992
|
+
failures INTEGER NOT NULL,
|
|
993
|
+
next_retry_at INTEGER NOT NULL,
|
|
994
|
+
swm_gen TEXT NOT NULL,
|
|
995
|
+
candidate_namespaces TEXT NOT NULL,
|
|
996
|
+
peer_topology_key TEXT NOT NULL,
|
|
997
|
+
updated_at INTEGER NOT NULL
|
|
998
|
+
);
|
|
999
|
+
CREATE INDEX IF NOT EXISTS idx_vm_reconcile_negative_cg
|
|
1000
|
+
ON vm_reconcile_negative_cache(context_graph_id);
|
|
1001
|
+
CREATE INDEX IF NOT EXISTS idx_vm_reconcile_negative_retry
|
|
1002
|
+
ON vm_reconcile_negative_cache(next_retry_at);
|
|
747
1003
|
`);
|
|
748
1004
|
}
|
|
749
1005
|
this.db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
@@ -778,7 +1034,21 @@ export class DashboardDB {
|
|
|
778
1034
|
this.db.exec(`DELETE FROM chat_persistence_jobs WHERE updated_at < ${cutoff} AND status IN ('stored', 'failed')`);
|
|
779
1035
|
this.db.exec(`DELETE FROM notifications WHERE ts < ${cutoff}`);
|
|
780
1036
|
this.db.exec(`DELETE FROM replication_events WHERE ts < ${cutoff}`);
|
|
1037
|
+
this.db.exec(`DELETE FROM context_graph_join_policy_audit WHERE ts < ${cutoff}`);
|
|
1038
|
+
// The admission ledger has its own typed lifetime. It intentionally does
|
|
1039
|
+
// not depend on whether the corresponding audit rows still exist. Preserve
|
|
1040
|
+
// the historical retention envelope for durable commit idempotency while
|
|
1041
|
+
// allowing fully expired reservation/commit state to age out.
|
|
1042
|
+
this.db.prepare(`
|
|
1043
|
+
DELETE FROM context_graph_join_approval_ledger
|
|
1044
|
+
WHERE reserved_at < ?
|
|
1045
|
+
AND (committed_at IS NULL OR committed_at < ?)
|
|
1046
|
+
`).run(cutoff, cutoff);
|
|
781
1047
|
this.db.prepare(`DELETE FROM sync_checkpoints WHERE expires_at < ?`).run(Date.now());
|
|
1048
|
+
// Reconcile negatives are accelerators, never historical records. Once the
|
|
1049
|
+
// retry window elapses they must not survive indefinitely or accumulate one
|
|
1050
|
+
// row per previously-seen KA across restarts.
|
|
1051
|
+
this.db.prepare(`DELETE FROM vm_reconcile_negative_cache WHERE next_retry_at < ?`).run(Date.now());
|
|
782
1052
|
// Universal Messenger idempotency table. Shorter TTL than the
|
|
783
1053
|
// operator retention: no realistic dedup window extends beyond
|
|
784
1054
|
// a day. The protocol_outbox table is intentionally not pruned
|
|
@@ -786,20 +1056,12 @@ export class DashboardDB {
|
|
|
786
1056
|
// SqliteProtocolOutboxStore.dropExpired().
|
|
787
1057
|
const messengerCutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
788
1058
|
this.db.exec(`DELETE FROM message_idempotency WHERE ts < ${messengerCutoff}`);
|
|
789
|
-
//
|
|
790
|
-
//
|
|
791
|
-
//
|
|
792
|
-
//
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
if (logsDeleted > LOGS_VACUUM_DELETE_THRESHOLD || freePages > VACUUM_FREE_PAGE_THRESHOLD) {
|
|
796
|
-
try {
|
|
797
|
-
this.db.exec(`VACUUM`);
|
|
798
|
-
}
|
|
799
|
-
catch {
|
|
800
|
-
// VACUUM requires an exclusive lock. If another connection is
|
|
801
|
-
// holding the DB open we skip and retry on the next prune.
|
|
802
|
-
}
|
|
1059
|
+
// Avoid rebuilding an oversized DB at startup while millions of routine
|
|
1060
|
+
// rows are still live. The daemon trims that backlog in bounded batches;
|
|
1061
|
+
// the final batch performs one compacting VACUUM. Databases already under
|
|
1062
|
+
// the cap retain the established time-prune reclamation behaviour.
|
|
1063
|
+
if (!this.hasRoutineLogOverflow()) {
|
|
1064
|
+
this.reclaimFreePagesIfNeeded(logsDeleted > LOGS_VACUUM_DELETE_THRESHOLD);
|
|
803
1065
|
}
|
|
804
1066
|
// Return the WAL file itself to the OS. journal_size_limit bounds it
|
|
805
1067
|
// in steady state, but a TRUNCATE checkpoint here shrinks it promptly
|
|
@@ -807,6 +1069,96 @@ export class DashboardDB {
|
|
|
807
1069
|
// which rewrites the whole DB through the WAL and momentarily grows
|
|
808
1070
|
// it. Runs unconditionally — independent of the VACUUM gate — because
|
|
809
1071
|
// an idle node still wants its -wal reclaimed.
|
|
1072
|
+
this.truncateWal('prune');
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Remove one bounded batch of the oldest routine (non-warning/error) logs.
|
|
1076
|
+
* A count cap complements time retention: a high-rate sync storm can create
|
|
1077
|
+
* millions of rows inside a single day, long before a 14-day cutoff applies.
|
|
1078
|
+
*
|
|
1079
|
+
* Deletion is deliberately incremental so an upgrade does not block node
|
|
1080
|
+
* startup on a multi-GB transaction. Once the backlog reaches the cap, one
|
|
1081
|
+
* VACUUM returns the accumulated free pages to the OS and the file shrinks.
|
|
1082
|
+
*/
|
|
1083
|
+
pruneLogVolumeBatch() {
|
|
1084
|
+
const overflowCutoff = this.routineLogOverflowCutoff();
|
|
1085
|
+
if (overflowCutoff === null) {
|
|
1086
|
+
const reclaim = this.reclaimFreePagesIfNeeded(false);
|
|
1087
|
+
if (!reclaim.reclaimPending)
|
|
1088
|
+
this.truncateWal('log-volume prune');
|
|
1089
|
+
return {
|
|
1090
|
+
deleted: 0,
|
|
1091
|
+
status: reclaim.reclaimPending
|
|
1092
|
+
? 'reclaim-pending'
|
|
1093
|
+
: reclaim.compacted
|
|
1094
|
+
? 'done-compacted'
|
|
1095
|
+
: 'done',
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
const deleted = this.db.prepare(`
|
|
1099
|
+
DELETE FROM logs
|
|
1100
|
+
WHERE id IN (
|
|
1101
|
+
SELECT id
|
|
1102
|
+
FROM logs
|
|
1103
|
+
WHERE id <= @cutoff
|
|
1104
|
+
AND level NOT IN ('warn', 'error')
|
|
1105
|
+
ORDER BY id ASC
|
|
1106
|
+
LIMIT @batchRows
|
|
1107
|
+
)
|
|
1108
|
+
`).run({
|
|
1109
|
+
cutoff: overflowCutoff,
|
|
1110
|
+
batchRows: this.logVolumePruneBatchRows,
|
|
1111
|
+
}).changes;
|
|
1112
|
+
// If the batch filled, conservatively schedule another tick. An exact-size
|
|
1113
|
+
// final batch costs one extra cheap probe before compaction, which is safer
|
|
1114
|
+
// than running a second million-row count after every deletion.
|
|
1115
|
+
const hasMore = deleted === this.logVolumePruneBatchRows;
|
|
1116
|
+
const reclaim = hasMore
|
|
1117
|
+
? { compacted: false, reclaimPending: false }
|
|
1118
|
+
: this.reclaimFreePagesIfNeeded(deleted > LOGS_VACUUM_DELETE_THRESHOLD);
|
|
1119
|
+
if (!hasMore && !reclaim.reclaimPending)
|
|
1120
|
+
this.truncateWal('log-volume prune');
|
|
1121
|
+
return {
|
|
1122
|
+
deleted,
|
|
1123
|
+
status: hasMore
|
|
1124
|
+
? 'more'
|
|
1125
|
+
: reclaim.reclaimPending
|
|
1126
|
+
? 'reclaim-pending'
|
|
1127
|
+
: reclaim.compacted
|
|
1128
|
+
? 'done-compacted'
|
|
1129
|
+
: 'done',
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
routineLogOverflowCutoff() {
|
|
1133
|
+
const row = this.db.prepare(`
|
|
1134
|
+
SELECT id
|
|
1135
|
+
FROM logs
|
|
1136
|
+
WHERE level NOT IN ('warn', 'error')
|
|
1137
|
+
ORDER BY id DESC
|
|
1138
|
+
LIMIT 1 OFFSET ?
|
|
1139
|
+
`).get(this.routineLogRowCap);
|
|
1140
|
+
return row?.id ?? null;
|
|
1141
|
+
}
|
|
1142
|
+
hasRoutineLogOverflow() {
|
|
1143
|
+
return this.routineLogOverflowCutoff() !== null;
|
|
1144
|
+
}
|
|
1145
|
+
reclaimFreePagesIfNeeded(force) {
|
|
1146
|
+
// DELETE / DROP only marks pages reusable; VACUUM is what returns them to
|
|
1147
|
+
// the OS. A failed exclusive-lock/disk attempt remains pending for a later
|
|
1148
|
+
// background retry and never prevents the node from running.
|
|
1149
|
+
const freePages = Number(this.db.pragma('freelist_count', { simple: true }) ?? 0);
|
|
1150
|
+
if (!force && freePages <= VACUUM_FREE_PAGE_THRESHOLD) {
|
|
1151
|
+
return { compacted: false, reclaimPending: false };
|
|
1152
|
+
}
|
|
1153
|
+
try {
|
|
1154
|
+
this.db.exec(`VACUUM`);
|
|
1155
|
+
return { compacted: true, reclaimPending: false };
|
|
1156
|
+
}
|
|
1157
|
+
catch {
|
|
1158
|
+
return { compacted: false, reclaimPending: true };
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
truncateWal(reason) {
|
|
810
1162
|
try {
|
|
811
1163
|
// wal_checkpoint signals reader contention through its result row
|
|
812
1164
|
// (`busy = 1`), NOT by throwing — so a busy checkpoint leaves the
|
|
@@ -816,7 +1168,7 @@ export class DashboardDB {
|
|
|
816
1168
|
// rather than indistinguishable from a real one.
|
|
817
1169
|
const [checkpoint] = this.db.pragma('wal_checkpoint(TRUNCATE)');
|
|
818
1170
|
if (checkpoint?.busy) {
|
|
819
|
-
console.warn(`[DashboardDB] wal_checkpoint(TRUNCATE) busy — WAL not reclaimed
|
|
1171
|
+
console.warn(`[DashboardDB] wal_checkpoint(TRUNCATE) busy — WAL not reclaimed during ${reason} ` +
|
|
820
1172
|
`(log=${checkpoint.log}, checkpointed=${checkpoint.checkpointed}); retried next prune`);
|
|
821
1173
|
}
|
|
822
1174
|
}
|
|
@@ -827,7 +1179,7 @@ export class DashboardDB {
|
|
|
827
1179
|
// better-sqlite3 change turns lock or I/O errors into throws, the
|
|
828
1180
|
// stalled WAL reclaim is visible in the daemon log instead of only
|
|
829
1181
|
// showing up later as unexplained disk growth. Never block prune.
|
|
830
|
-
console.warn(`[DashboardDB] wal_checkpoint(TRUNCATE) failed — WAL not reclaimed
|
|
1182
|
+
console.warn(`[DashboardDB] wal_checkpoint(TRUNCATE) failed — WAL not reclaimed during ${reason}: ` +
|
|
831
1183
|
`${err instanceof Error ? err.message : String(err)}`);
|
|
832
1184
|
}
|
|
833
1185
|
}
|
|
@@ -950,7 +1302,302 @@ export class DashboardDB {
|
|
|
950
1302
|
return this.stmt('getContextGraphSubscription', 'SELECT * FROM context_graph_subscriptions WHERE context_graph_id = ?').get(contextGraphId);
|
|
951
1303
|
}
|
|
952
1304
|
deleteContextGraphSubscription(contextGraphId) {
|
|
953
|
-
|
|
1305
|
+
const remove = this.db.transaction(() => {
|
|
1306
|
+
this.stmt('deleteContextGraphSubscription', 'DELETE FROM context_graph_subscriptions WHERE context_graph_id = ?').run(contextGraphId);
|
|
1307
|
+
this.stmt('deleteContextGraphReadinessProvenance', 'DELETE FROM settings WHERE key = ?')
|
|
1308
|
+
.run(this.contextGraphReadinessProvenanceKey(contextGraphId));
|
|
1309
|
+
});
|
|
1310
|
+
remove();
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* CLI-owned, durable provenance for context-graph readiness flags.
|
|
1314
|
+
*
|
|
1315
|
+
* Subscription rows predate per-plane proof and can therefore contain the
|
|
1316
|
+
* v10.0.6 false-ready shape (`synced=1`, `shared_memory_synced=1`) after an
|
|
1317
|
+
* unrelated peer returned an empty response. Keep the proof out of the
|
|
1318
|
+
* agent-owned subscription upsert so older/custom agent stores remain
|
|
1319
|
+
* source-compatible and routine subscription persistence cannot overwrite a
|
|
1320
|
+
* proof established by the daemon's catch-up classifier.
|
|
1321
|
+
*/
|
|
1322
|
+
getContextGraphReadinessProvenance(contextGraphId) {
|
|
1323
|
+
const row = this.stmt('getContextGraphReadinessProvenance', 'SELECT value FROM settings WHERE key = ?').get(this.contextGraphReadinessProvenanceKey(contextGraphId));
|
|
1324
|
+
if (!row)
|
|
1325
|
+
return null;
|
|
1326
|
+
try {
|
|
1327
|
+
const parsed = JSON.parse(row.value);
|
|
1328
|
+
if (!Number.isInteger(parsed.version) || (parsed.version ?? 0) < 1)
|
|
1329
|
+
return null;
|
|
1330
|
+
return {
|
|
1331
|
+
version: parsed.version,
|
|
1332
|
+
durableVerified: parsed.durableVerified === true,
|
|
1333
|
+
sharedMemoryVerified: parsed.sharedMemoryVerified === true,
|
|
1334
|
+
updatedAt: typeof parsed.updatedAt === 'number' && Number.isFinite(parsed.updatedAt)
|
|
1335
|
+
? parsed.updatedAt
|
|
1336
|
+
: 0,
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
catch {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
setContextGraphReadinessProvenance(contextGraphId, provenance) {
|
|
1344
|
+
const record = {
|
|
1345
|
+
version: provenance.version,
|
|
1346
|
+
durableVerified: provenance.durableVerified,
|
|
1347
|
+
sharedMemoryVerified: provenance.sharedMemoryVerified,
|
|
1348
|
+
updatedAt: provenance.updatedAt ?? Date.now(),
|
|
1349
|
+
};
|
|
1350
|
+
this.stmt('setContextGraphReadinessProvenance', 'INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)').run(this.contextGraphReadinessProvenanceKey(contextGraphId), JSON.stringify(record));
|
|
1351
|
+
}
|
|
1352
|
+
contextGraphReadinessProvenanceKey(contextGraphId) {
|
|
1353
|
+
return `contextGraphReadiness:${contextGraphId}`;
|
|
1354
|
+
}
|
|
1355
|
+
// --- Private context-graph join policy + audit ---
|
|
1356
|
+
getContextGraphJoinPolicy(contextGraphId) {
|
|
1357
|
+
const row = this.stmt('getContextGraphJoinPolicy', 'SELECT value FROM settings WHERE key = ?').get(this.contextGraphJoinPolicyKey(contextGraphId));
|
|
1358
|
+
if (!row)
|
|
1359
|
+
return null;
|
|
1360
|
+
try {
|
|
1361
|
+
return parseContextGraphJoinPolicyRecord(JSON.parse(row.value), contextGraphId);
|
|
1362
|
+
}
|
|
1363
|
+
catch {
|
|
1364
|
+
// Corrupt policy state is indistinguishable from no policy. The agent's
|
|
1365
|
+
// default is manual, so this is intentionally fail-closed.
|
|
1366
|
+
return null;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
setContextGraphJoinPolicy(record) {
|
|
1370
|
+
this.stmt('setContextGraphJoinPolicy', 'INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)').run(this.contextGraphJoinPolicyKey(record.contextGraphId), JSON.stringify(record));
|
|
1371
|
+
}
|
|
1372
|
+
setContextGraphJoinPolicyWithAudit(record, event) {
|
|
1373
|
+
const transition = this.db.transaction(() => {
|
|
1374
|
+
this.setContextGraphJoinPolicy(record);
|
|
1375
|
+
this.appendContextGraphJoinPolicyAudit(event);
|
|
1376
|
+
});
|
|
1377
|
+
transition();
|
|
1378
|
+
}
|
|
1379
|
+
appendContextGraphJoinPolicyAudit(event) {
|
|
1380
|
+
this.stmt('appendContextGraphJoinPolicyAudit', `
|
|
1381
|
+
INSERT INTO context_graph_join_policy_audit (
|
|
1382
|
+
ts, context_graph_id, event_type, actor, agent_address, outcome,
|
|
1383
|
+
reason, request_digest, policy_version, details
|
|
1384
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1385
|
+
`).run(event.timestamp, event.contextGraphId, event.eventType, event.actor ?? null, event.agentAddress ?? null, event.outcome, event.reason ?? null, event.requestDigest ?? null, event.policyVersion ?? null, event.details ? JSON.stringify(event.details) : null);
|
|
1386
|
+
}
|
|
1387
|
+
reserveContextGraphAutomaticApproval(input) {
|
|
1388
|
+
const reserve = this.db.transaction(() => {
|
|
1389
|
+
const since = input.timestamp - 60 * 60 * 1000;
|
|
1390
|
+
const cgRow = this.stmt('countContextGraphAutomaticApprovalReservations', `
|
|
1391
|
+
SELECT COUNT(*) AS count
|
|
1392
|
+
FROM context_graph_join_approval_ledger
|
|
1393
|
+
WHERE context_graph_id = ?
|
|
1394
|
+
AND reserved_at >= ?
|
|
1395
|
+
`).get(input.contextGraphId, since);
|
|
1396
|
+
const nodeRow = this.stmt('countNodeAutomaticApprovalReservations', `
|
|
1397
|
+
SELECT COUNT(*) AS count
|
|
1398
|
+
FROM context_graph_join_approval_ledger
|
|
1399
|
+
WHERE reserved_at >= ?
|
|
1400
|
+
`).get(since);
|
|
1401
|
+
const contextGraphCount = Number(cgRow?.count ?? 0);
|
|
1402
|
+
const nodeCount = Number(nodeRow?.count ?? 0);
|
|
1403
|
+
const existing = this.stmt('findContextGraphAutomaticApprovalReservation', `
|
|
1404
|
+
SELECT 1 AS found
|
|
1405
|
+
FROM context_graph_join_approval_ledger
|
|
1406
|
+
WHERE context_graph_id = ?
|
|
1407
|
+
AND request_digest = ?
|
|
1408
|
+
AND policy_epoch = ?
|
|
1409
|
+
AND reserved_at >= ?
|
|
1410
|
+
LIMIT 1
|
|
1411
|
+
`).get(input.contextGraphId, input.requestDigest, input.policyEpoch, since);
|
|
1412
|
+
if (existing) {
|
|
1413
|
+
return {
|
|
1414
|
+
allowed: true,
|
|
1415
|
+
contextGraphApprovalsLastHour: contextGraphCount,
|
|
1416
|
+
nodeApprovalsLastHour: nodeCount,
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
if (contextGraphCount >= input.contextGraphLimit) {
|
|
1420
|
+
return {
|
|
1421
|
+
allowed: false,
|
|
1422
|
+
contextGraphApprovalsLastHour: contextGraphCount,
|
|
1423
|
+
nodeApprovalsLastHour: nodeCount,
|
|
1424
|
+
reason: 'context-graph-rate-limit',
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
if (nodeCount >= input.nodeLimit) {
|
|
1428
|
+
return {
|
|
1429
|
+
allowed: false,
|
|
1430
|
+
contextGraphApprovalsLastHour: contextGraphCount,
|
|
1431
|
+
nodeApprovalsLastHour: nodeCount,
|
|
1432
|
+
reason: 'node-rate-limit',
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
// The typed ledger row and its audit projection share one transaction:
|
|
1436
|
+
// failures cannot consume quota without an audit event or emit an audit
|
|
1437
|
+
// reservation without durable operational state. An expired replay of
|
|
1438
|
+
// the same digest+epoch refreshes its reservation timestamp and consumes
|
|
1439
|
+
// the rolling-window quota again, matching the former append-only model.
|
|
1440
|
+
this.stmt('upsertContextGraphAutomaticApprovalReservation', `
|
|
1441
|
+
INSERT INTO context_graph_join_approval_ledger (
|
|
1442
|
+
context_graph_id, request_digest, policy_epoch, reserved_at,
|
|
1443
|
+
state, committed_at, actor, agent_address, policy_version
|
|
1444
|
+
) VALUES (?, ?, ?, ?, 'reserved', NULL, ?, ?, ?)
|
|
1445
|
+
ON CONFLICT(context_graph_id, request_digest, policy_epoch) DO UPDATE SET
|
|
1446
|
+
reserved_at = excluded.reserved_at,
|
|
1447
|
+
actor = excluded.actor,
|
|
1448
|
+
agent_address = excluded.agent_address,
|
|
1449
|
+
policy_version = excluded.policy_version
|
|
1450
|
+
`).run(input.contextGraphId, input.requestDigest, input.policyEpoch, input.timestamp, input.actor, input.agentAddress, input.policyVersion);
|
|
1451
|
+
this.appendContextGraphJoinPolicyAudit({
|
|
1452
|
+
timestamp: input.timestamp,
|
|
1453
|
+
contextGraphId: input.contextGraphId,
|
|
1454
|
+
eventType: 'join_auto_reservation',
|
|
1455
|
+
actor: input.actor,
|
|
1456
|
+
agentAddress: input.agentAddress,
|
|
1457
|
+
outcome: 'reserved',
|
|
1458
|
+
requestDigest: input.requestDigest,
|
|
1459
|
+
policyVersion: input.policyVersion,
|
|
1460
|
+
details: {
|
|
1461
|
+
contextGraphLimit: input.contextGraphLimit,
|
|
1462
|
+
nodeLimit: input.nodeLimit,
|
|
1463
|
+
policyEpoch: input.policyEpoch,
|
|
1464
|
+
},
|
|
1465
|
+
});
|
|
1466
|
+
return {
|
|
1467
|
+
allowed: true,
|
|
1468
|
+
contextGraphApprovalsLastHour: contextGraphCount + 1,
|
|
1469
|
+
nodeApprovalsLastHour: nodeCount + 1,
|
|
1470
|
+
};
|
|
1471
|
+
});
|
|
1472
|
+
return reserve();
|
|
1473
|
+
}
|
|
1474
|
+
markContextGraphAutomaticApprovalRepairPending(input) {
|
|
1475
|
+
const marked = this.stmt('markContextGraphAutomaticApprovalRepairPending', `
|
|
1476
|
+
UPDATE context_graph_join_approval_ledger
|
|
1477
|
+
SET repair_pending = 1
|
|
1478
|
+
WHERE context_graph_id = ?
|
|
1479
|
+
AND request_digest = ?
|
|
1480
|
+
AND policy_epoch = ?
|
|
1481
|
+
AND state = 'reserved'
|
|
1482
|
+
`).run(input.contextGraphId, input.requestDigest, input.policyEpoch);
|
|
1483
|
+
return marked.changes === 1;
|
|
1484
|
+
}
|
|
1485
|
+
getContextGraphAutomaticApprovalRepair(contextGraphId, requestDigest) {
|
|
1486
|
+
const repair = this.stmt('getContextGraphAutomaticApprovalRepair', `
|
|
1487
|
+
SELECT policy_epoch, actor, agent_address
|
|
1488
|
+
FROM context_graph_join_approval_ledger
|
|
1489
|
+
WHERE context_graph_id = ?
|
|
1490
|
+
AND request_digest = ?
|
|
1491
|
+
AND state = 'reserved'
|
|
1492
|
+
AND repair_pending = 1
|
|
1493
|
+
ORDER BY reserved_at DESC
|
|
1494
|
+
LIMIT 1
|
|
1495
|
+
`).get(contextGraphId, requestDigest);
|
|
1496
|
+
return repair
|
|
1497
|
+
? {
|
|
1498
|
+
policyEpoch: repair.policy_epoch,
|
|
1499
|
+
actor: repair.actor,
|
|
1500
|
+
agentAddress: repair.agent_address,
|
|
1501
|
+
}
|
|
1502
|
+
: null;
|
|
1503
|
+
}
|
|
1504
|
+
commitContextGraphAutomaticApproval(input) {
|
|
1505
|
+
const commit = this.db.transaction(() => {
|
|
1506
|
+
const reservation = this.stmt('findExactContextGraphAutomaticApprovalReservation', `
|
|
1507
|
+
SELECT context_graph_id, request_digest, policy_epoch, reserved_at,
|
|
1508
|
+
state, committed_at, actor, agent_address, policy_version
|
|
1509
|
+
FROM context_graph_join_approval_ledger
|
|
1510
|
+
WHERE context_graph_id = ?
|
|
1511
|
+
AND request_digest = ?
|
|
1512
|
+
AND policy_epoch = ?
|
|
1513
|
+
LIMIT 1
|
|
1514
|
+
`).get(input.contextGraphId, input.requestDigest, input.policyEpoch);
|
|
1515
|
+
if (!reservation)
|
|
1516
|
+
return false;
|
|
1517
|
+
if (reservation.state === 'committed')
|
|
1518
|
+
return true;
|
|
1519
|
+
const updated = this.stmt('commitContextGraphAutomaticApprovalReservation', `
|
|
1520
|
+
UPDATE context_graph_join_approval_ledger
|
|
1521
|
+
SET state = 'committed', committed_at = ?, repair_pending = 0
|
|
1522
|
+
WHERE context_graph_id = ?
|
|
1523
|
+
AND request_digest = ?
|
|
1524
|
+
AND policy_epoch = ?
|
|
1525
|
+
AND state = 'reserved'
|
|
1526
|
+
`).run(input.timestamp, reservation.context_graph_id, reservation.request_digest, reservation.policy_epoch);
|
|
1527
|
+
if (updated.changes !== 1)
|
|
1528
|
+
return false;
|
|
1529
|
+
this.appendContextGraphJoinPolicyAudit({
|
|
1530
|
+
timestamp: input.timestamp,
|
|
1531
|
+
contextGraphId: input.contextGraphId,
|
|
1532
|
+
eventType: 'join_admission_committed',
|
|
1533
|
+
actor: reservation.actor || input.actor,
|
|
1534
|
+
agentAddress: reservation.agent_address || input.agentAddress,
|
|
1535
|
+
outcome: 'approved',
|
|
1536
|
+
requestDigest: input.requestDigest,
|
|
1537
|
+
policyVersion: reservation.policy_version,
|
|
1538
|
+
details: {
|
|
1539
|
+
...input.details,
|
|
1540
|
+
policyEpoch: reservation.policy_epoch,
|
|
1541
|
+
},
|
|
1542
|
+
});
|
|
1543
|
+
return true;
|
|
1544
|
+
});
|
|
1545
|
+
return commit();
|
|
1546
|
+
}
|
|
1547
|
+
getContextGraphAutomaticApprovalUsage(contextGraphId, timestamp) {
|
|
1548
|
+
const since = timestamp - 60 * 60 * 1000;
|
|
1549
|
+
const contextGraphApprovalsLastHour = Number(this.stmt('getContextGraphAutomaticApprovalUsage', `
|
|
1550
|
+
SELECT COUNT(*) AS count
|
|
1551
|
+
FROM context_graph_join_approval_ledger
|
|
1552
|
+
WHERE context_graph_id = ?
|
|
1553
|
+
AND reserved_at >= ?
|
|
1554
|
+
`).get(contextGraphId, since)?.count ?? 0);
|
|
1555
|
+
const nodeApprovalsLastHour = Number(this.stmt('getNodeAutomaticApprovalUsage', `
|
|
1556
|
+
SELECT COUNT(*) AS count
|
|
1557
|
+
FROM context_graph_join_approval_ledger
|
|
1558
|
+
WHERE reserved_at >= ?
|
|
1559
|
+
`).get(since)?.count ?? 0);
|
|
1560
|
+
return { contextGraphApprovalsLastHour, nodeApprovalsLastHour };
|
|
1561
|
+
}
|
|
1562
|
+
listContextGraphJoinPolicyAudit(contextGraphId) {
|
|
1563
|
+
return this.stmt('listContextGraphJoinPolicyAudit', `
|
|
1564
|
+
SELECT id, ts, context_graph_id, event_type, actor, agent_address,
|
|
1565
|
+
outcome, reason, request_digest, policy_version, details
|
|
1566
|
+
FROM context_graph_join_policy_audit
|
|
1567
|
+
WHERE context_graph_id = ?
|
|
1568
|
+
ORDER BY id ASC
|
|
1569
|
+
`).all(contextGraphId);
|
|
1570
|
+
}
|
|
1571
|
+
contextGraphJoinPolicyKey(contextGraphId) {
|
|
1572
|
+
return `contextGraphJoinPolicy:${contextGraphId}`;
|
|
1573
|
+
}
|
|
1574
|
+
upsertVmReconcileNegative(record) {
|
|
1575
|
+
this.stmt('upsertVmReconcileNegative', `
|
|
1576
|
+
INSERT INTO vm_reconcile_negative_cache (
|
|
1577
|
+
cache_key, context_graph_id, failures, next_retry_at, swm_gen,
|
|
1578
|
+
candidate_namespaces, peer_topology_key, updated_at
|
|
1579
|
+
) VALUES (
|
|
1580
|
+
@cache_key, @context_graph_id, @failures, @next_retry_at, @swm_gen,
|
|
1581
|
+
@candidate_namespaces, @peer_topology_key, @updated_at
|
|
1582
|
+
)
|
|
1583
|
+
ON CONFLICT(cache_key) DO UPDATE SET
|
|
1584
|
+
context_graph_id = excluded.context_graph_id,
|
|
1585
|
+
failures = excluded.failures,
|
|
1586
|
+
next_retry_at = excluded.next_retry_at,
|
|
1587
|
+
swm_gen = excluded.swm_gen,
|
|
1588
|
+
candidate_namespaces = excluded.candidate_namespaces,
|
|
1589
|
+
peer_topology_key = excluded.peer_topology_key,
|
|
1590
|
+
updated_at = excluded.updated_at
|
|
1591
|
+
`).run(record);
|
|
1592
|
+
}
|
|
1593
|
+
getVmReconcileNegative(cacheKey) {
|
|
1594
|
+
return this.stmt('getVmReconcileNegative', 'SELECT * FROM vm_reconcile_negative_cache WHERE cache_key = ?').get(cacheKey);
|
|
1595
|
+
}
|
|
1596
|
+
deleteVmReconcileNegative(cacheKey) {
|
|
1597
|
+
this.stmt('deleteVmReconcileNegative', 'DELETE FROM vm_reconcile_negative_cache WHERE cache_key = ?').run(cacheKey);
|
|
1598
|
+
}
|
|
1599
|
+
deleteVmReconcileNegativesForContextGraph(contextGraphId) {
|
|
1600
|
+
this.stmt('deleteVmReconcileNegativesForContextGraph', 'DELETE FROM vm_reconcile_negative_cache WHERE context_graph_id = ?').run(contextGraphId);
|
|
954
1601
|
}
|
|
955
1602
|
// --- Phase F: chain-driven VM reconciliation telemetry ---
|
|
956
1603
|
/**
|
|
@@ -2031,6 +2678,74 @@ export class SqliteSyncCheckpointStore {
|
|
|
2031
2678
|
return this.db.prepare(`DELETE FROM sync_checkpoints WHERE expires_at < ?`).run(nowMs).changes;
|
|
2032
2679
|
}
|
|
2033
2680
|
}
|
|
2681
|
+
/**
|
|
2682
|
+
* OT-RFC-59 SC5 durable changelog cursor store (duck-compatible with the agent's
|
|
2683
|
+
* ChangelogCursorStore). Keyed by (peer_id, context_graph_id); stores the last
|
|
2684
|
+
* APPLIED (era, seq) from that responder. Like `ka_numbers` / `protocol_outbox`
|
|
2685
|
+
* this state is durable: it is NEVER added to `prune()` — a TTL would defeat the
|
|
2686
|
+
* O(delta) cross-restart catch-up the changelog lane exists for.
|
|
2687
|
+
*/
|
|
2688
|
+
export class SqliteChangelogCursorStore {
|
|
2689
|
+
db;
|
|
2690
|
+
clock;
|
|
2691
|
+
constructor(dashboard, options = {}) {
|
|
2692
|
+
this.db = dashboard.db;
|
|
2693
|
+
this.clock = options.clock ?? (() => Date.now());
|
|
2694
|
+
}
|
|
2695
|
+
get(peerId, contextGraphId) {
|
|
2696
|
+
const row = this.db.prepare(`SELECT era, seq, updated_at FROM changelog_cursors WHERE peer_id = ? AND context_graph_id = ?`).get(peerId, contextGraphId);
|
|
2697
|
+
if (!row)
|
|
2698
|
+
return undefined;
|
|
2699
|
+
return { era: row.era, seq: row.seq, updatedAtMs: row.updated_at };
|
|
2700
|
+
}
|
|
2701
|
+
set(peerId, contextGraphId, era, seq, nowMs = this.clock()) {
|
|
2702
|
+
if (!Number.isSafeInteger(seq) || seq < 0) {
|
|
2703
|
+
throw new Error(`Invalid changelog cursor seq for ${peerId}/${contextGraphId}: ${seq}`);
|
|
2704
|
+
}
|
|
2705
|
+
this.db.prepare(`
|
|
2706
|
+
INSERT INTO changelog_cursors (peer_id, context_graph_id, era, seq, updated_at)
|
|
2707
|
+
VALUES (?, ?, ?, ?, ?)
|
|
2708
|
+
ON CONFLICT(peer_id, context_graph_id) DO UPDATE SET
|
|
2709
|
+
era = excluded.era,
|
|
2710
|
+
seq = excluded.seq,
|
|
2711
|
+
updated_at = excluded.updated_at
|
|
2712
|
+
`).run(peerId, contextGraphId, era, seq, nowMs);
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
/**
|
|
2716
|
+
* OT-RFC-59 §6 P0 durable era guard (duck-compatible with storage's
|
|
2717
|
+
* ChangelogEraGuard). Persists the single (era, high_seq) high-water in
|
|
2718
|
+
* node-ui.db — which a `store.nq` RDF-store restore does NOT roll back — so the
|
|
2719
|
+
* write-side ChangelogStore can detect a restore/rollback (seq regressed under
|
|
2720
|
+
* the same era) and rotate the era, forcing peers to full-resync instead of
|
|
2721
|
+
* silently skipping. `save` is called after every committed seq, so it must be a
|
|
2722
|
+
* single fast upsert.
|
|
2723
|
+
*/
|
|
2724
|
+
export class SqliteChangelogEraGuard {
|
|
2725
|
+
db;
|
|
2726
|
+
clock;
|
|
2727
|
+
constructor(dashboard, options = {}) {
|
|
2728
|
+
this.db = dashboard.db;
|
|
2729
|
+
this.clock = options.clock ?? (() => Date.now());
|
|
2730
|
+
}
|
|
2731
|
+
async load() {
|
|
2732
|
+
const row = this.db.prepare(`SELECT era, high_seq FROM changelog_era WHERE id = 1`).get();
|
|
2733
|
+
return row ? { era: row.era, highSeq: row.high_seq } : null;
|
|
2734
|
+
}
|
|
2735
|
+
async save(era, highSeq) {
|
|
2736
|
+
if (!Number.isSafeInteger(highSeq) || highSeq < 0) {
|
|
2737
|
+
throw new Error(`Invalid changelog era high_seq: ${highSeq}`);
|
|
2738
|
+
}
|
|
2739
|
+
this.db.prepare(`
|
|
2740
|
+
INSERT INTO changelog_era (id, era, high_seq, updated_at)
|
|
2741
|
+
VALUES (1, ?, ?, ?)
|
|
2742
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
2743
|
+
era = excluded.era,
|
|
2744
|
+
high_seq = excluded.high_seq,
|
|
2745
|
+
updated_at = excluded.updated_at
|
|
2746
|
+
`).run(era, highSeq, this.clock());
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2034
2749
|
// --- Universal Messenger substrate stores (rc.9 plan PR-1) ---
|
|
2035
2750
|
/**
|
|
2036
2751
|
* SQLite-backed `MessageIdempotencyStore` against the V12
|
|
@@ -2176,18 +2891,36 @@ export class SqliteProtocolOutboxStore {
|
|
|
2176
2891
|
.get(peer, protocol, messageId);
|
|
2177
2892
|
return row !== undefined;
|
|
2178
2893
|
}
|
|
2894
|
+
hasPendingFor(peer) {
|
|
2895
|
+
return this.db.prepare('SELECT 1 FROM protocol_outbox WHERE peer_id = ? LIMIT 1').get(peer) !== undefined;
|
|
2896
|
+
}
|
|
2179
2897
|
pendingFor(peer) {
|
|
2180
2898
|
const rows = this.db
|
|
2181
|
-
.prepare(`SELECT * FROM protocol_outbox
|
|
2899
|
+
.prepare(`SELECT * FROM protocol_outbox
|
|
2900
|
+
WHERE peer_id = ?
|
|
2901
|
+
ORDER BY first_failure_at ASC, protocol ASC, message_id ASC`)
|
|
2182
2902
|
.all(peer);
|
|
2183
2903
|
return rows.map(SqliteProtocolOutboxStore.rowToEntry);
|
|
2184
2904
|
}
|
|
2185
2905
|
due(now) {
|
|
2186
2906
|
const rows = this.db
|
|
2187
|
-
.prepare(`SELECT * FROM protocol_outbox
|
|
2907
|
+
.prepare(`SELECT * FROM protocol_outbox
|
|
2908
|
+
WHERE next_attempt_at <= ?
|
|
2909
|
+
ORDER BY next_attempt_at ASC, first_failure_at ASC,
|
|
2910
|
+
peer_id ASC, protocol ASC, message_id ASC`)
|
|
2188
2911
|
.all(now);
|
|
2189
2912
|
return rows.map(SqliteProtocolOutboxStore.rowToEntry);
|
|
2190
2913
|
}
|
|
2914
|
+
duePage(now, limit) {
|
|
2915
|
+
const rows = this.db
|
|
2916
|
+
.prepare(`SELECT * FROM protocol_outbox
|
|
2917
|
+
WHERE next_attempt_at <= ?
|
|
2918
|
+
ORDER BY next_attempt_at ASC, first_failure_at ASC,
|
|
2919
|
+
peer_id ASC, protocol ASC, message_id ASC
|
|
2920
|
+
LIMIT ?`)
|
|
2921
|
+
.all(now, limit);
|
|
2922
|
+
return rows.map(SqliteProtocolOutboxStore.rowToEntry);
|
|
2923
|
+
}
|
|
2191
2924
|
dropExpired(now) {
|
|
2192
2925
|
const cutoff = now - this.maxAgeMs;
|
|
2193
2926
|
const rows = this.db
|