@cello-protocol/daemon 0.0.227 → 0.0.229
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/agent-admin-handlers.d.ts +56 -0
- package/dist/agent-admin-handlers.d.ts.map +1 -0
- package/dist/agent-admin-handlers.js +73 -0
- package/dist/agent-admin-handlers.js.map +1 -0
- package/dist/agent-handlers.d.ts +72 -0
- package/dist/agent-handlers.d.ts.map +1 -0
- package/dist/agent-handlers.js +604 -0
- package/dist/agent-handlers.js.map +1 -0
- package/dist/agent-id-migration.d.ts +70 -0
- package/dist/agent-id-migration.d.ts.map +1 -0
- package/dist/agent-id-migration.js +468 -0
- package/dist/agent-id-migration.js.map +1 -0
- package/dist/agent-loader.d.ts +37 -0
- package/dist/agent-loader.d.ts.map +1 -0
- package/dist/agent-loader.js +37 -0
- package/dist/agent-loader.js.map +1 -0
- package/dist/agent-selection-root.d.ts +29 -0
- package/dist/agent-selection-root.d.ts.map +1 -0
- package/dist/agent-selection-root.js +126 -0
- package/dist/agent-selection-root.js.map +1 -0
- package/dist/agent-selection.d.ts +73 -0
- package/dist/agent-selection.d.ts.map +1 -0
- package/dist/agent-selection.js +80 -0
- package/dist/agent-selection.js.map +1 -0
- package/dist/agent-settings-keys.d.ts +52 -0
- package/dist/agent-settings-keys.d.ts.map +1 -0
- package/dist/agent-settings-keys.js +114 -0
- package/dist/agent-settings-keys.js.map +1 -0
- package/dist/agent-state.d.ts +67 -0
- package/dist/agent-state.d.ts.map +1 -0
- package/dist/agent-state.js +66 -0
- package/dist/agent-state.js.map +1 -0
- package/dist/assignment-verify.d.ts +90 -0
- package/dist/assignment-verify.d.ts.map +1 -0
- package/dist/assignment-verify.js +301 -0
- package/dist/assignment-verify.js.map +1 -0
- package/dist/attendance-wiring.d.ts +23 -0
- package/dist/attendance-wiring.d.ts.map +1 -0
- package/dist/attendance-wiring.js +248 -0
- package/dist/attendance-wiring.js.map +1 -0
- package/dist/attestation-body.d.ts +47 -0
- package/dist/attestation-body.d.ts.map +1 -0
- package/dist/attestation-body.js +71 -0
- package/dist/attestation-body.js.map +1 -0
- package/dist/authorship-verification.d.ts +91 -0
- package/dist/authorship-verification.d.ts.map +1 -0
- package/dist/authorship-verification.js +532 -0
- package/dist/authorship-verification.js.map +1 -0
- package/dist/away-detection.d.ts +139 -0
- package/dist/away-detection.d.ts.map +1 -0
- package/dist/away-detection.js +186 -0
- package/dist/away-detection.js.map +1 -0
- package/dist/away-inbox-oneshot.d.ts +40 -0
- package/dist/away-inbox-oneshot.d.ts.map +1 -0
- package/dist/away-inbox-oneshot.js +311 -0
- package/dist/away-inbox-oneshot.js.map +1 -0
- package/dist/backup-restore-handlers.d.ts +10 -0
- package/dist/backup-restore-handlers.d.ts.map +1 -0
- package/dist/backup-restore-handlers.js +82 -0
- package/dist/backup-restore-handlers.js.map +1 -0
- package/dist/backup-restore.d.ts +102 -0
- package/dist/backup-restore.d.ts.map +1 -0
- package/dist/backup-restore.js +341 -0
- package/dist/backup-restore.js.map +1 -0
- package/dist/bin/cello-daemon.d.ts +13 -0
- package/dist/bin/cello-daemon.d.ts.map +1 -0
- package/dist/bin/cello-daemon.js.map +1 -0
- package/dist/boot-agents.d.ts +64 -0
- package/dist/boot-agents.d.ts.map +1 -0
- package/dist/boot-agents.js +241 -0
- package/dist/boot-agents.js.map +1 -0
- package/dist/boot-connection-state.d.ts +38 -0
- package/dist/boot-connection-state.d.ts.map +1 -0
- package/dist/boot-connection-state.js +95 -0
- package/dist/boot-connection-state.js.map +1 -0
- package/dist/boot-core.d.ts +36 -0
- package/dist/boot-core.d.ts.map +1 -0
- package/dist/boot-core.js +293 -0
- package/dist/boot-core.js.map +1 -0
- package/dist/boot-parked-content.d.ts +34 -0
- package/dist/boot-parked-content.d.ts.map +1 -0
- package/dist/boot-parked-content.js +528 -0
- package/dist/boot-parked-content.js.map +1 -0
- package/dist/boot-sweeps.d.ts +22 -0
- package/dist/boot-sweeps.d.ts.map +1 -0
- package/dist/boot-sweeps.js +51 -0
- package/dist/boot-sweeps.js.map +1 -0
- package/dist/bundled-consortium-manifest.d.ts +47 -0
- package/dist/bundled-consortium-manifest.d.ts.map +1 -0
- package/dist/bundled-consortium-manifest.js +116 -0
- package/dist/bundled-consortium-manifest.js.map +1 -0
- package/dist/cbor-blob-migration.d.ts +8 -0
- package/dist/cbor-blob-migration.d.ts.map +1 -0
- package/dist/cbor-blob-migration.js +116 -0
- package/dist/cbor-blob-migration.js.map +1 -0
- package/dist/challenge-verifier.d.ts +12 -0
- package/dist/challenge-verifier.d.ts.map +1 -0
- package/dist/challenge-verifier.js +11 -0
- package/dist/challenge-verifier.js.map +1 -0
- package/dist/channel-log-store.d.ts +80 -0
- package/dist/channel-log-store.d.ts.map +1 -0
- package/dist/channel-log-store.js +290 -0
- package/dist/channel-log-store.js.map +1 -0
- package/dist/close-commitment.d.ts +67 -0
- package/dist/close-commitment.d.ts.map +1 -0
- package/dist/close-commitment.js +78 -0
- package/dist/close-commitment.js.map +1 -0
- package/dist/close-session-handler.d.ts +94 -0
- package/dist/close-session-handler.d.ts.map +1 -0
- package/dist/close-session-handler.js +1418 -0
- package/dist/close-session-handler.js.map +1 -0
- package/dist/co-attendance.d.ts +29 -0
- package/dist/co-attendance.d.ts.map +1 -0
- package/dist/co-attendance.js +32 -0
- package/dist/co-attendance.js.map +1 -0
- package/dist/column-birth.d.ts +40 -0
- package/dist/column-birth.d.ts.map +1 -0
- package/dist/column-birth.js +61 -0
- package/dist/column-birth.js.map +1 -0
- package/dist/connect-or-start.d.ts +25 -0
- package/dist/connect-or-start.d.ts.map +1 -0
- package/dist/connect-or-start.js +175 -0
- package/dist/connect-or-start.js.map +1 -0
- package/dist/connection-agents.d.ts +16 -0
- package/dist/connection-agents.d.ts.map +1 -0
- package/dist/connection-agents.js +68 -0
- package/dist/connection-agents.js.map +1 -0
- package/dist/consent-migration.d.ts +49 -0
- package/dist/consent-migration.d.ts.map +1 -0
- package/dist/consent-migration.js +128 -0
- package/dist/consent-migration.js.map +1 -0
- package/dist/consortium-bootstrap.d.ts +138 -0
- package/dist/consortium-bootstrap.d.ts.map +1 -0
- package/dist/consortium-bootstrap.js +339 -0
- package/dist/consortium-bootstrap.js.map +1 -0
- package/dist/consortium-fingerprint.d.ts +115 -0
- package/dist/consortium-fingerprint.d.ts.map +1 -0
- package/dist/consortium-fingerprint.js +175 -0
- package/dist/consortium-fingerprint.js.map +1 -0
- package/dist/contact-handlers.d.ts +59 -0
- package/dist/contact-handlers.d.ts.map +1 -0
- package/dist/contact-handlers.js +343 -0
- package/dist/contact-handlers.js.map +1 -0
- package/dist/contact-pubkey-case.d.ts +65 -0
- package/dist/contact-pubkey-case.d.ts.map +1 -0
- package/dist/contact-pubkey-case.js +136 -0
- package/dist/contact-pubkey-case.js.map +1 -0
- package/dist/contacts-tier-migration.d.ts +90 -0
- package/dist/contacts-tier-migration.d.ts.map +1 -0
- package/dist/contacts-tier-migration.js +150 -0
- package/dist/contacts-tier-migration.js.map +1 -0
- package/dist/content-encryption-status.d.ts +111 -0
- package/dist/content-encryption-status.d.ts.map +1 -0
- package/dist/content-encryption-status.js +158 -0
- package/dist/content-encryption-status.js.map +1 -0
- package/dist/content-park-client.d.ts +90 -0
- package/dist/content-park-client.d.ts.map +1 -0
- package/dist/content-park-client.js +362 -0
- package/dist/content-park-client.js.map +1 -0
- package/dist/content-park.d.ts +52 -0
- package/dist/content-park.d.ts.map +1 -0
- package/dist/content-park.js +1309 -0
- package/dist/content-park.js.map +1 -0
- package/dist/cross-node-negotiation.d.ts +44 -0
- package/dist/cross-node-negotiation.d.ts.map +1 -0
- package/dist/cross-node-negotiation.js +34 -0
- package/dist/cross-node-negotiation.js.map +1 -0
- package/dist/daemon-handle.d.ts +57 -0
- package/dist/daemon-handle.d.ts.map +1 -0
- package/dist/daemon-handle.js +2 -0
- package/dist/daemon-handle.js.map +1 -0
- package/dist/daemon-status-report.d.ts +50 -0
- package/dist/daemon-status-report.d.ts.map +1 -0
- package/dist/daemon-status-report.js +81 -0
- package/dist/daemon-status-report.js.map +1 -0
- package/dist/daemon.d.ts +45 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +1177 -0
- package/dist/daemon.js.map +1 -0
- package/dist/db-identity-store.d.ts +159 -0
- package/dist/db-identity-store.d.ts.map +1 -0
- package/dist/db-identity-store.js +502 -0
- package/dist/db-identity-store.js.map +1 -0
- package/dist/delivery-open-registry.d.ts +92 -0
- package/dist/delivery-open-registry.d.ts.map +1 -0
- package/dist/delivery-open-registry.js +121 -0
- package/dist/delivery-open-registry.js.map +1 -0
- package/dist/delivery-session-suspects.d.ts +56 -0
- package/dist/delivery-session-suspects.d.ts.map +1 -0
- package/dist/delivery-session-suspects.js +94 -0
- package/dist/delivery-session-suspects.js.map +1 -0
- package/dist/directory-auth-posture.d.ts +87 -0
- package/dist/directory-auth-posture.d.ts.map +1 -0
- package/dist/directory-auth-posture.js +134 -0
- package/dist/directory-auth-posture.js.map +1 -0
- package/dist/directory-bootstrap.d.ts +310 -0
- package/dist/directory-bootstrap.d.ts.map +1 -0
- package/dist/directory-bootstrap.js +557 -0
- package/dist/directory-bootstrap.js.map +1 -0
- package/dist/directory-connect.d.ts +27 -0
- package/dist/directory-connect.d.ts.map +1 -0
- package/dist/directory-connect.js +106 -0
- package/dist/directory-connect.js.map +1 -0
- package/dist/disconnect-cleanup.d.ts +51 -0
- package/dist/disconnect-cleanup.d.ts.map +1 -0
- package/dist/disconnect-cleanup.js +72 -0
- package/dist/disconnect-cleanup.js.map +1 -0
- package/dist/document-amendment-store.d.ts +120 -0
- package/dist/document-amendment-store.d.ts.map +1 -0
- package/dist/document-amendment-store.js +266 -0
- package/dist/document-amendment-store.js.map +1 -0
- package/dist/document-delivery-transport.d.ts +168 -0
- package/dist/document-delivery-transport.d.ts.map +1 -0
- package/dist/document-delivery-transport.js +206 -0
- package/dist/document-delivery-transport.js.map +1 -0
- package/dist/document-engine.d.ts +134 -0
- package/dist/document-engine.d.ts.map +1 -0
- package/dist/document-engine.js +282 -0
- package/dist/document-engine.js.map +1 -0
- package/dist/document-flag.d.ts +58 -0
- package/dist/document-flag.d.ts.map +1 -0
- package/dist/document-flag.js +70 -0
- package/dist/document-flag.js.map +1 -0
- package/dist/document-frame-router.d.ts +245 -0
- package/dist/document-frame-router.d.ts.map +1 -0
- package/dist/document-frame-router.js +397 -0
- package/dist/document-frame-router.js.map +1 -0
- package/dist/document-gate-wiring.d.ts +61 -0
- package/dist/document-gate-wiring.d.ts.map +1 -0
- package/dist/document-gate-wiring.js +125 -0
- package/dist/document-gate-wiring.js.map +1 -0
- package/dist/document-gate.d.ts +149 -0
- package/dist/document-gate.d.ts.map +1 -0
- package/dist/document-gate.js +509 -0
- package/dist/document-gate.js.map +1 -0
- package/dist/document-handlers.d.ts +47 -0
- package/dist/document-handlers.d.ts.map +1 -0
- package/dist/document-handlers.js +2203 -0
- package/dist/document-handlers.js.map +1 -0
- package/dist/document-handshake.d.ts +176 -0
- package/dist/document-handshake.d.ts.map +1 -0
- package/dist/document-handshake.js +452 -0
- package/dist/document-handshake.js.map +1 -0
- package/dist/document-inbound.d.ts +162 -0
- package/dist/document-inbound.d.ts.map +1 -0
- package/dist/document-inbound.js +530 -0
- package/dist/document-inbound.js.map +1 -0
- package/dist/document-json.d.ts +120 -0
- package/dist/document-json.d.ts.map +1 -0
- package/dist/document-json.js +191 -0
- package/dist/document-json.js.map +1 -0
- package/dist/document-layer.d.ts +215 -0
- package/dist/document-layer.d.ts.map +1 -0
- package/dist/document-layer.js +1025 -0
- package/dist/document-layer.js.map +1 -0
- package/dist/document-lifecycle.d.ts +52 -0
- package/dist/document-lifecycle.d.ts.map +1 -0
- package/dist/document-lifecycle.js +134 -0
- package/dist/document-lifecycle.js.map +1 -0
- package/dist/document-live-docs.d.ts +58 -0
- package/dist/document-live-docs.d.ts.map +1 -0
- package/dist/document-live-docs.js +126 -0
- package/dist/document-live-docs.js.map +1 -0
- package/dist/document-notify.d.ts +228 -0
- package/dist/document-notify.d.ts.map +1 -0
- package/dist/document-notify.js +580 -0
- package/dist/document-notify.js.map +1 -0
- package/dist/document-profile.d.ts +61 -0
- package/dist/document-profile.d.ts.map +1 -0
- package/dist/document-profile.js +112 -0
- package/dist/document-profile.js.map +1 -0
- package/dist/document-publish.d.ts +87 -0
- package/dist/document-publish.d.ts.map +1 -0
- package/dist/document-publish.js +173 -0
- package/dist/document-publish.js.map +1 -0
- package/dist/document-reachability.d.ts +42 -0
- package/dist/document-reachability.d.ts.map +1 -0
- package/dist/document-reachability.js +80 -0
- package/dist/document-reachability.js.map +1 -0
- package/dist/document-reconcile-engine.d.ts +66 -0
- package/dist/document-reconcile-engine.d.ts.map +1 -0
- package/dist/document-reconcile-engine.js +225 -0
- package/dist/document-reconcile-engine.js.map +1 -0
- package/dist/document-reconcile-scheduler.d.ts +163 -0
- package/dist/document-reconcile-scheduler.d.ts.map +1 -0
- package/dist/document-reconcile-scheduler.js +303 -0
- package/dist/document-reconcile-scheduler.js.map +1 -0
- package/dist/document-rejection.d.ts +251 -0
- package/dist/document-rejection.d.ts.map +1 -0
- package/dist/document-rejection.js +435 -0
- package/dist/document-rejection.js.map +1 -0
- package/dist/document-screen.d.ts +114 -0
- package/dist/document-screen.d.ts.map +1 -0
- package/dist/document-screen.js +223 -0
- package/dist/document-screen.js.map +1 -0
- package/dist/document-store.d.ts +372 -0
- package/dist/document-store.d.ts.map +1 -0
- package/dist/document-store.js +931 -0
- package/dist/document-store.js.map +1 -0
- package/dist/document-surface.d.ts +32 -0
- package/dist/document-surface.d.ts.map +1 -0
- package/dist/document-surface.js +151 -0
- package/dist/document-surface.js.map +1 -0
- package/dist/document-types.d.ts +94 -0
- package/dist/document-types.d.ts.map +1 -0
- package/dist/document-types.js +90 -0
- package/dist/document-types.js.map +1 -0
- package/dist/document-watch.d.ts +69 -0
- package/dist/document-watch.d.ts.map +1 -0
- package/dist/document-watch.js +108 -0
- package/dist/document-watch.js.map +1 -0
- package/dist/document-wiring.d.ts +53 -0
- package/dist/document-wiring.d.ts.map +1 -0
- package/dist/document-wiring.js +323 -0
- package/dist/document-wiring.js.map +1 -0
- package/dist/document-write-guard.d.ts +66 -0
- package/dist/document-write-guard.d.ts.map +1 -0
- package/dist/document-write-guard.js +98 -0
- package/dist/document-write-guard.js.map +1 -0
- package/dist/document-write-path.d.ts +94 -0
- package/dist/document-write-path.d.ts.map +1 -0
- package/dist/document-write-path.js +530 -0
- package/dist/document-write-path.js.map +1 -0
- package/dist/error-message.d.ts +7 -0
- package/dist/error-message.d.ts.map +1 -0
- package/dist/error-message.js +19 -0
- package/dist/error-message.js.map +1 -0
- package/dist/file-manifest-provider.d.ts +37 -0
- package/dist/file-manifest-provider.d.ts.map +1 -0
- package/dist/file-manifest-provider.js +105 -0
- package/dist/file-manifest-provider.js.map +1 -0
- package/dist/frame-values.d.ts +3 -0
- package/dist/frame-values.d.ts.map +1 -0
- package/dist/frame-values.js +84 -0
- package/dist/frame-values.js.map +1 -0
- package/dist/frontier-mismatch.d.ts +73 -0
- package/dist/frontier-mismatch.d.ts.map +1 -0
- package/dist/frontier-mismatch.js +89 -0
- package/dist/frontier-mismatch.js.map +1 -0
- package/dist/gateway-config-handlers.d.ts +26 -0
- package/dist/gateway-config-handlers.d.ts.map +1 -0
- package/dist/gateway-config-handlers.js +454 -0
- package/dist/gateway-config-handlers.js.map +1 -0
- package/dist/held-content.d.ts +145 -0
- package/dist/held-content.d.ts.map +1 -0
- package/dist/held-content.js +389 -0
- package/dist/held-content.js.map +1 -0
- package/dist/http-manifest-poll.d.ts +67 -0
- package/dist/http-manifest-poll.d.ts.map +1 -0
- package/dist/http-manifest-poll.js +150 -0
- package/dist/http-manifest-poll.js.map +1 -0
- package/dist/identity-migration.d.ts +40 -0
- package/dist/identity-migration.d.ts.map +1 -0
- package/dist/identity-migration.js +461 -0
- package/dist/identity-migration.js.map +1 -0
- package/dist/inbound-refusals.d.ts +283 -0
- package/dist/inbound-refusals.d.ts.map +1 -0
- package/dist/inbound-refusals.js +919 -0
- package/dist/inbound-refusals.js.map +1 -0
- package/dist/inbound-seal-request.d.ts +32 -0
- package/dist/inbound-seal-request.d.ts.map +1 -0
- package/dist/inbound-seal-request.js +228 -0
- package/dist/inbound-seal-request.js.map +1 -0
- package/dist/inbound-sessions.d.ts +291 -0
- package/dist/inbound-sessions.d.ts.map +1 -0
- package/dist/inbound-sessions.js +1609 -0
- package/dist/inbound-sessions.js.map +1 -0
- package/dist/inclusion-proof-handlers.d.ts +43 -0
- package/dist/inclusion-proof-handlers.d.ts.map +1 -0
- package/dist/inclusion-proof-handlers.js +565 -0
- package/dist/inclusion-proof-handlers.js.map +1 -0
- package/dist/inclusion-proof.d.ts +151 -0
- package/dist/inclusion-proof.d.ts.map +1 -0
- package/dist/inclusion-proof.js +228 -0
- package/dist/inclusion-proof.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/initiate-session-handler.d.ts +55 -0
- package/dist/initiate-session-handler.d.ts.map +1 -0
- package/dist/initiate-session-handler.js +310 -0
- package/dist/initiate-session-handler.js.map +1 -0
- package/dist/ipc-client.d.ts +31 -0
- package/dist/ipc-client.d.ts.map +1 -0
- package/dist/ipc-client.js +113 -0
- package/dist/ipc-client.js.map +1 -0
- package/dist/ipc-server.d.ts +63 -0
- package/dist/ipc-server.d.ts.map +1 -0
- package/dist/ipc-server.js +429 -0
- package/dist/ipc-server.js.map +1 -0
- package/dist/ipc-surface.d.ts +44 -0
- package/dist/ipc-surface.d.ts.map +1 -0
- package/dist/ipc-surface.js +107 -0
- package/dist/ipc-surface.js.map +1 -0
- package/dist/line-lcs.d.ts +51 -0
- package/dist/line-lcs.d.ts.map +1 -0
- package/dist/line-lcs.js +71 -0
- package/dist/line-lcs.js.map +1 -0
- package/dist/lock-file.d.ts +39 -0
- package/dist/lock-file.d.ts.map +1 -0
- package/dist/lock-file.js +120 -0
- package/dist/lock-file.js.map +1 -0
- package/dist/log-collapse.d.ts +66 -0
- package/dist/log-collapse.d.ts.map +1 -0
- package/dist/log-collapse.js +244 -0
- package/dist/log-collapse.js.map +1 -0
- package/dist/log-rotate.d.ts +67 -0
- package/dist/log-rotate.d.ts.map +1 -0
- package/dist/log-rotate.js +134 -0
- package/dist/log-rotate.js.map +1 -0
- package/dist/manifest-deps.d.ts +25 -0
- package/dist/manifest-deps.d.ts.map +1 -0
- package/dist/manifest-deps.js +151 -0
- package/dist/manifest-deps.js.map +1 -0
- package/dist/manifest-poll-scheduler.d.ts +31 -0
- package/dist/manifest-poll-scheduler.d.ts.map +1 -0
- package/dist/manifest-poll-scheduler.js +59 -0
- package/dist/manifest-poll-scheduler.js.map +1 -0
- package/dist/manifest-validity.d.ts +153 -0
- package/dist/manifest-validity.d.ts.map +1 -0
- package/dist/manifest-validity.js +268 -0
- package/dist/manifest-validity.js.map +1 -0
- package/dist/manifest-version-store-db.d.ts +24 -0
- package/dist/manifest-version-store-db.d.ts.map +1 -0
- package/dist/manifest-version-store-db.js +58 -0
- package/dist/manifest-version-store-db.js.map +1 -0
- package/dist/manifest-version-store.d.ts +16 -0
- package/dist/manifest-version-store.d.ts.map +1 -0
- package/dist/manifest-version-store.js +15 -0
- package/dist/manifest-version-store.js.map +1 -0
- package/dist/network-directory-node.d.ts +136 -0
- package/dist/network-directory-node.d.ts.map +1 -0
- package/dist/network-directory-node.js +810 -0
- package/dist/network-directory-node.js.map +1 -0
- package/dist/nonce-dedup.d.ts +68 -0
- package/dist/nonce-dedup.d.ts.map +1 -0
- package/dist/nonce-dedup.js +205 -0
- package/dist/nonce-dedup.js.map +1 -0
- package/dist/notification-dispatcher.d.ts +92 -0
- package/dist/notification-dispatcher.d.ts.map +1 -0
- package/dist/notification-dispatcher.js +210 -0
- package/dist/notification-dispatcher.js.map +1 -0
- package/dist/notification-handlers.d.ts +53 -0
- package/dist/notification-handlers.d.ts.map +1 -0
- package/dist/notification-handlers.js +461 -0
- package/dist/notification-handlers.js.map +1 -0
- package/dist/onboarding-guidance.d.ts +79 -0
- package/dist/onboarding-guidance.d.ts.map +1 -0
- package/dist/onboarding-guidance.js +95 -0
- package/dist/onboarding-guidance.js.map +1 -0
- package/dist/operator-guidance.d.ts +25 -0
- package/dist/operator-guidance.d.ts.map +1 -0
- package/dist/operator-guidance.js +50 -0
- package/dist/operator-guidance.js.map +1 -0
- package/dist/orphan-triage.d.ts +130 -0
- package/dist/orphan-triage.d.ts.map +1 -0
- package/dist/orphan-triage.js +207 -0
- package/dist/orphan-triage.js.map +1 -0
- package/dist/outbound-sessions.d.ts +126 -0
- package/dist/outbound-sessions.d.ts.map +1 -0
- package/dist/outbound-sessions.js +1052 -0
- package/dist/outbound-sessions.js.map +1 -0
- package/dist/park-envelope.d.ts +329 -0
- package/dist/park-envelope.d.ts.map +1 -0
- package/dist/park-envelope.js +509 -0
- package/dist/park-envelope.js.map +1 -0
- package/dist/park-recovery.d.ts +273 -0
- package/dist/park-recovery.d.ts.map +1 -0
- package/dist/park-recovery.js +717 -0
- package/dist/park-recovery.js.map +1 -0
- package/dist/park-refusals.d.ts +147 -0
- package/dist/park-refusals.d.ts.map +1 -0
- package/dist/park-refusals.js +331 -0
- package/dist/park-refusals.js.map +1 -0
- package/dist/quarantine-framing.d.ts +92 -0
- package/dist/quarantine-framing.d.ts.map +1 -0
- package/dist/quarantine-framing.js +111 -0
- package/dist/quarantine-framing.js.map +1 -0
- package/dist/reconnect-drain.d.ts +22 -0
- package/dist/reconnect-drain.d.ts.map +1 -0
- package/dist/reconnect-drain.js +65 -0
- package/dist/reconnect-drain.js.map +1 -0
- package/dist/recovered-position.d.ts +14 -0
- package/dist/recovered-position.d.ts.map +1 -0
- package/dist/recovered-position.js +59 -0
- package/dist/recovered-position.js.map +1 -0
- package/dist/refusal-notices.d.ts +196 -0
- package/dist/refusal-notices.d.ts.map +1 -0
- package/dist/refusal-notices.js +516 -0
- package/dist/refusal-notices.js.map +1 -0
- package/dist/refusal-reasons.d.ts +245 -0
- package/dist/refusal-reasons.d.ts.map +1 -0
- package/dist/refusal-reasons.js +363 -0
- package/dist/refusal-reasons.js.map +1 -0
- package/dist/register-handler.d.ts +36 -0
- package/dist/register-handler.d.ts.map +1 -0
- package/dist/register-handler.js +285 -0
- package/dist/register-handler.js.map +1 -0
- package/dist/registration-context.d.ts +72 -0
- package/dist/registration-context.d.ts.map +1 -0
- package/dist/registration-context.js +126 -0
- package/dist/registration-context.js.map +1 -0
- package/dist/registration-manager.d.ts +94 -0
- package/dist/registration-manager.d.ts.map +1 -0
- package/dist/registration-manager.js +585 -0
- package/dist/registration-manager.js.map +1 -0
- package/dist/registration-persistence.d.ts +183 -0
- package/dist/registration-persistence.d.ts.map +1 -0
- package/dist/registration-persistence.js +263 -0
- package/dist/registration-persistence.js.map +1 -0
- package/dist/registry-poll.d.ts +52 -0
- package/dist/registry-poll.d.ts.map +1 -0
- package/dist/registry-poll.js +140 -0
- package/dist/registry-poll.js.map +1 -0
- package/dist/registry-version-store-db.d.ts +22 -0
- package/dist/registry-version-store-db.d.ts.map +1 -0
- package/dist/registry-version-store-db.js +50 -0
- package/dist/registry-version-store-db.js.map +1 -0
- package/dist/relay-endpoints.d.ts +18 -0
- package/dist/relay-endpoints.d.ts.map +1 -0
- package/dist/relay-endpoints.js +9 -0
- package/dist/relay-endpoints.js.map +1 -0
- package/dist/relay-only.d.ts +140 -0
- package/dist/relay-only.d.ts.map +1 -0
- package/dist/relay-only.js +193 -0
- package/dist/relay-only.js.map +1 -0
- package/dist/relay-receipt-store.d.ts +155 -0
- package/dist/relay-receipt-store.d.ts.map +1 -0
- package/dist/relay-receipt-store.js +284 -0
- package/dist/relay-receipt-store.js.map +1 -0
- package/dist/relay-reconnect.d.ts +32 -0
- package/dist/relay-reconnect.d.ts.map +1 -0
- package/dist/relay-reconnect.js +29 -0
- package/dist/relay-reconnect.js.map +1 -0
- package/dist/reply-lag.d.ts +11 -0
- package/dist/reply-lag.d.ts.map +1 -0
- package/dist/reply-lag.js +46 -0
- package/dist/reply-lag.js.map +1 -0
- package/dist/resolve-named-agent.d.ts +49 -0
- package/dist/resolve-named-agent.d.ts.map +1 -0
- package/dist/resolve-named-agent.js +77 -0
- package/dist/resolve-named-agent.js.map +1 -0
- package/dist/restart-seal-resolver.d.ts +110 -0
- package/dist/restart-seal-resolver.d.ts.map +1 -0
- package/dist/restart-seal-resolver.js +353 -0
- package/dist/restart-seal-resolver.js.map +1 -0
- package/dist/resume-last-seen.d.ts +15 -0
- package/dist/resume-last-seen.d.ts.map +1 -0
- package/dist/resume-last-seen.js +44 -0
- package/dist/resume-last-seen.js.map +1 -0
- package/dist/retry-queue.d.ts +203 -0
- package/dist/retry-queue.d.ts.map +1 -0
- package/dist/retry-queue.js +701 -0
- package/dist/retry-queue.js.map +1 -0
- package/dist/roster-freshness.d.ts +160 -0
- package/dist/roster-freshness.d.ts.map +1 -0
- package/dist/roster-freshness.js +250 -0
- package/dist/roster-freshness.js.map +1 -0
- package/dist/screening-status.d.ts +15 -0
- package/dist/screening-status.d.ts.map +1 -0
- package/dist/screening-status.js +65 -0
- package/dist/screening-status.js.map +1 -0
- package/dist/seal-carried-close.d.ts +37 -0
- package/dist/seal-carried-close.d.ts.map +1 -0
- package/dist/seal-carried-close.js +179 -0
- package/dist/seal-carried-close.js.map +1 -0
- package/dist/seal-certificate-pull.d.ts +79 -0
- package/dist/seal-certificate-pull.d.ts.map +1 -0
- package/dist/seal-certificate-pull.js +186 -0
- package/dist/seal-certificate-pull.js.map +1 -0
- package/dist/seal-certified-root-check.d.ts +37 -0
- package/dist/seal-certified-root-check.d.ts.map +1 -0
- package/dist/seal-certified-root-check.js +127 -0
- package/dist/seal-certified-root-check.js.map +1 -0
- package/dist/seal-coordinator.d.ts +118 -0
- package/dist/seal-coordinator.d.ts.map +1 -0
- package/dist/seal-coordinator.js +927 -0
- package/dist/seal-coordinator.js.map +1 -0
- package/dist/seal-escalation.d.ts +80 -0
- package/dist/seal-escalation.d.ts.map +1 -0
- package/dist/seal-escalation.js +283 -0
- package/dist/seal-escalation.js.map +1 -0
- package/dist/seal-evidence-root-check.d.ts +70 -0
- package/dist/seal-evidence-root-check.d.ts.map +1 -0
- package/dist/seal-evidence-root-check.js +199 -0
- package/dist/seal-evidence-root-check.js.map +1 -0
- package/dist/seal-failure-store.d.ts +139 -0
- package/dist/seal-failure-store.d.ts.map +1 -0
- package/dist/seal-failure-store.js +207 -0
- package/dist/seal-failure-store.js.map +1 -0
- package/dist/seal-flows.d.ts +107 -0
- package/dist/seal-flows.d.ts.map +1 -0
- package/dist/seal-flows.js +638 -0
- package/dist/seal-flows.js.map +1 -0
- package/dist/seal-frontier-verify.d.ts +103 -0
- package/dist/seal-frontier-verify.d.ts.map +1 -0
- package/dist/seal-frontier-verify.js +143 -0
- package/dist/seal-frontier-verify.js.map +1 -0
- package/dist/seal-leaf.d.ts +58 -0
- package/dist/seal-leaf.d.ts.map +1 -0
- package/dist/seal-leaf.js +112 -0
- package/dist/seal-leaf.js.map +1 -0
- package/dist/seal-legibility-tbs.d.ts +25 -0
- package/dist/seal-legibility-tbs.d.ts.map +1 -0
- package/dist/seal-legibility-tbs.js +77 -0
- package/dist/seal-legibility-tbs.js.map +1 -0
- package/dist/seal-local-terminus.d.ts +41 -0
- package/dist/seal-local-terminus.d.ts.map +1 -0
- package/dist/seal-local-terminus.js +170 -0
- package/dist/seal-local-terminus.js.map +1 -0
- package/dist/seal-receipt-upgrade.d.ts +30 -0
- package/dist/seal-receipt-upgrade.d.ts.map +1 -0
- package/dist/seal-receipt-upgrade.js +49 -0
- package/dist/seal-receipt-upgrade.js.map +1 -0
- package/dist/seal-relay-silence.d.ts +51 -0
- package/dist/seal-relay-silence.d.ts.map +1 -0
- package/dist/seal-relay-silence.js +76 -0
- package/dist/seal-relay-silence.js.map +1 -0
- package/dist/seal-settle.d.ts +38 -0
- package/dist/seal-settle.d.ts.map +1 -0
- package/dist/seal-settle.js +33 -0
- package/dist/seal-settle.js.map +1 -0
- package/dist/seal-upgrade.d.ts +107 -0
- package/dist/seal-upgrade.d.ts.map +1 -0
- package/dist/seal-upgrade.js +200 -0
- package/dist/seal-upgrade.js.map +1 -0
- package/dist/sealed-conversation.d.ts +36 -0
- package/dist/sealed-conversation.d.ts.map +1 -0
- package/dist/sealed-conversation.js +171 -0
- package/dist/sealed-conversation.js.map +1 -0
- package/dist/sealed-leaf-set.d.ts +92 -0
- package/dist/sealed-leaf-set.d.ts.map +1 -0
- package/dist/sealed-leaf-set.js +122 -0
- package/dist/sealed-leaf-set.js.map +1 -0
- package/dist/send-claims.d.ts +56 -0
- package/dist/send-claims.d.ts.map +1 -0
- package/dist/send-claims.js +50 -0
- package/dist/send-claims.js.map +1 -0
- package/dist/session-assignment-parser.d.ts +104 -0
- package/dist/session-assignment-parser.d.ts.map +1 -0
- package/dist/session-assignment-parser.js +345 -0
- package/dist/session-assignment-parser.js.map +1 -0
- package/dist/session-category.d.ts +19 -0
- package/dist/session-category.d.ts.map +1 -0
- package/dist/session-category.js +14 -0
- package/dist/session-category.js.map +1 -0
- package/dist/session-ceremony.d.ts +297 -0
- package/dist/session-ceremony.d.ts.map +1 -0
- package/dist/session-ceremony.js +964 -0
- package/dist/session-ceremony.js.map +1 -0
- package/dist/session-closed.d.ts +79 -0
- package/dist/session-closed.d.ts.map +1 -0
- package/dist/session-closed.js +189 -0
- package/dist/session-closed.js.map +1 -0
- package/dist/session-connection-gater.d.ts +148 -0
- package/dist/session-connection-gater.d.ts.map +1 -0
- package/dist/session-connection-gater.js +332 -0
- package/dist/session-connection-gater.js.map +1 -0
- package/dist/session-content-context.d.ts +162 -0
- package/dist/session-content-context.d.ts.map +1 -0
- package/dist/session-content-context.js +2 -0
- package/dist/session-content-context.js.map +1 -0
- package/dist/session-content-handlers.d.ts +82 -0
- package/dist/session-content-handlers.d.ts.map +1 -0
- package/dist/session-content-handlers.js +1382 -0
- package/dist/session-content-handlers.js.map +1 -0
- package/dist/session-content-ingest.d.ts +206 -0
- package/dist/session-content-ingest.d.ts.map +1 -0
- package/dist/session-content-ingest.js +2167 -0
- package/dist/session-content-ingest.js.map +1 -0
- package/dist/session-content-send.d.ts +191 -0
- package/dist/session-content-send.d.ts.map +1 -0
- package/dist/session-content-send.js +1360 -0
- package/dist/session-content-send.js.map +1 -0
- package/dist/session-delivery-acks.d.ts +169 -0
- package/dist/session-delivery-acks.d.ts.map +1 -0
- package/dist/session-delivery-acks.js +569 -0
- package/dist/session-delivery-acks.js.map +1 -0
- package/dist/session-ephemerals.d.ts +279 -0
- package/dist/session-ephemerals.d.ts.map +1 -0
- package/dist/session-ephemerals.js +591 -0
- package/dist/session-ephemerals.js.map +1 -0
- package/dist/session-leaf-records.d.ts +159 -0
- package/dist/session-leaf-records.d.ts.map +1 -0
- package/dist/session-leaf-records.js +408 -0
- package/dist/session-leaf-records.js.map +1 -0
- package/dist/session-lifecycle.d.ts +303 -0
- package/dist/session-lifecycle.d.ts.map +1 -0
- package/dist/session-lifecycle.js +1679 -0
- package/dist/session-lifecycle.js.map +1 -0
- package/dist/session-liveness.d.ts +135 -0
- package/dist/session-liveness.d.ts.map +1 -0
- package/dist/session-liveness.js +347 -0
- package/dist/session-liveness.js.map +1 -0
- package/dist/session-name.d.ts +35 -0
- package/dist/session-name.d.ts.map +1 -0
- package/dist/session-name.js +60 -0
- package/dist/session-name.js.map +1 -0
- package/dist/session-node-factory.d.ts +18 -0
- package/dist/session-node-factory.d.ts.map +1 -0
- package/dist/session-node-factory.js +182 -0
- package/dist/session-node-factory.js.map +1 -0
- package/dist/session-node-manager.d.ts +877 -0
- package/dist/session-node-manager.d.ts.map +1 -0
- package/dist/session-node-manager.js +2998 -0
- package/dist/session-node-manager.js.map +1 -0
- package/dist/session-node-types.d.ts +1057 -0
- package/dist/session-node-types.d.ts.map +1 -0
- package/dist/session-node-types.js +657 -0
- package/dist/session-node-types.js.map +1 -0
- package/dist/session-notify.d.ts +46 -0
- package/dist/session-notify.d.ts.map +1 -0
- package/dist/session-notify.js +116 -0
- package/dist/session-notify.js.map +1 -0
- package/dist/session-own-chain-store.d.ts +65 -0
- package/dist/session-own-chain-store.d.ts.map +1 -0
- package/dist/session-own-chain-store.js +75 -0
- package/dist/session-own-chain-store.js.map +1 -0
- package/dist/session-queries.d.ts +476 -0
- package/dist/session-queries.d.ts.map +1 -0
- package/dist/session-queries.js +1007 -0
- package/dist/session-queries.js.map +1 -0
- package/dist/session-read-handlers.d.ts +87 -0
- package/dist/session-read-handlers.d.ts.map +1 -0
- package/dist/session-read-handlers.js +689 -0
- package/dist/session-read-handlers.js.map +1 -0
- package/dist/session-records.d.ts +341 -0
- package/dist/session-records.d.ts.map +1 -0
- package/dist/session-records.js +858 -0
- package/dist/session-records.js.map +1 -0
- package/dist/session-relay-client.d.ts +659 -0
- package/dist/session-relay-client.d.ts.map +1 -0
- package/dist/session-relay-client.js +2876 -0
- package/dist/session-relay-client.js.map +1 -0
- package/dist/session-relay.d.ts +397 -0
- package/dist/session-relay.d.ts.map +1 -0
- package/dist/session-relay.js +1636 -0
- package/dist/session-relay.js.map +1 -0
- package/dist/session-salt-agreement.d.ts +331 -0
- package/dist/session-salt-agreement.d.ts.map +1 -0
- package/dist/session-salt-agreement.js +472 -0
- package/dist/session-salt-agreement.js.map +1 -0
- package/dist/session-salts.d.ts +432 -0
- package/dist/session-salts.d.ts.map +1 -0
- package/dist/session-salts.js +1540 -0
- package/dist/session-salts.js.map +1 -0
- package/dist/session-schema.d.ts +30 -0
- package/dist/session-schema.d.ts.map +1 -0
- package/dist/session-schema.js +877 -0
- package/dist/session-schema.js.map +1 -0
- package/dist/session-seal-leaf-store.d.ts +70 -0
- package/dist/session-seal-leaf-store.d.ts.map +1 -0
- package/dist/session-seal-leaf-store.js +105 -0
- package/dist/session-seal-leaf-store.js.map +1 -0
- package/dist/session-seal.d.ts +334 -0
- package/dist/session-seal.d.ts.map +1 -0
- package/dist/session-seal.js +1017 -0
- package/dist/session-seal.js.map +1 -0
- package/dist/session-terminal-refusal.d.ts +65 -0
- package/dist/session-terminal-refusal.d.ts.map +1 -0
- package/dist/session-terminal-refusal.js +87 -0
- package/dist/session-terminal-refusal.js.map +1 -0
- package/dist/session-tree.d.ts +110 -0
- package/dist/session-tree.d.ts.map +1 -0
- package/dist/session-tree.js +144 -0
- package/dist/session-tree.js.map +1 -0
- package/dist/session-views.d.ts +47 -0
- package/dist/session-views.d.ts.map +1 -0
- package/dist/session-views.js +278 -0
- package/dist/session-views.js.map +1 -0
- package/dist/signal-handlers.d.ts +63 -0
- package/dist/signal-handlers.d.ts.map +1 -0
- package/dist/signal-handlers.js +980 -0
- package/dist/signal-handlers.js.map +1 -0
- package/dist/signal-requirement-policy.d.ts +51 -0
- package/dist/signal-requirement-policy.d.ts.map +1 -0
- package/dist/signal-requirement-policy.js +89 -0
- package/dist/signal-requirement-policy.js.map +1 -0
- package/dist/signal-revocability.d.ts +49 -0
- package/dist/signal-revocability.d.ts.map +1 -0
- package/dist/signal-revocability.js +93 -0
- package/dist/signal-revocability.js.map +1 -0
- package/dist/signal-submission.d.ts +181 -0
- package/dist/signal-submission.d.ts.map +1 -0
- package/dist/signal-submission.js +368 -0
- package/dist/signal-submission.js.map +1 -0
- package/dist/signaling-connect.d.ts +118 -0
- package/dist/signaling-connect.d.ts.map +1 -0
- package/dist/signaling-connect.js +538 -0
- package/dist/signaling-connect.js.map +1 -0
- package/dist/signaling-wiring.d.ts +105 -0
- package/dist/signaling-wiring.d.ts.map +1 -0
- package/dist/signaling-wiring.js +393 -0
- package/dist/signaling-wiring.js.map +1 -0
- package/dist/singleton-lock.d.ts +85 -0
- package/dist/singleton-lock.d.ts.map +1 -0
- package/dist/singleton-lock.js +219 -0
- package/dist/singleton-lock.js.map +1 -0
- package/dist/sqlcipher-db.d.ts +139 -0
- package/dist/sqlcipher-db.d.ts.map +1 -0
- package/dist/sqlcipher-db.js +357 -0
- package/dist/sqlcipher-db.js.map +1 -0
- package/dist/standing-receivers.d.ts +319 -0
- package/dist/standing-receivers.d.ts.map +1 -0
- package/dist/standing-receivers.js +1295 -0
- package/dist/standing-receivers.js.map +1 -0
- package/dist/start-agent.d.ts +64 -0
- package/dist/start-agent.d.ts.map +1 -0
- package/dist/start-agent.js +136 -0
- package/dist/start-agent.js.map +1 -0
- package/dist/status-handler.d.ts +54 -0
- package/dist/status-handler.d.ts.map +1 -0
- package/dist/status-handler.js +61 -0
- package/dist/status-handler.js.map +1 -0
- package/dist/submission-retry.d.ts +208 -0
- package/dist/submission-retry.d.ts.map +1 -0
- package/dist/submission-retry.js +506 -0
- package/dist/submission-retry.js.map +1 -0
- package/dist/telegram-bot-client.d.ts +34 -0
- package/dist/telegram-bot-client.d.ts.map +1 -0
- package/dist/telegram-bot-client.js +36 -0
- package/dist/telegram-bot-client.js.map +1 -0
- package/dist/telegram-doorbell.d.ts +38 -0
- package/dist/telegram-doorbell.d.ts.map +1 -0
- package/dist/telegram-doorbell.js +140 -0
- package/dist/telegram-doorbell.js.map +1 -0
- package/dist/test-handlers.d.ts +62 -0
- package/dist/test-handlers.d.ts.map +1 -0
- package/dist/test-handlers.js +240 -0
- package/dist/test-handlers.js.map +1 -0
- package/dist/testing.d.ts +10 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +10 -0
- package/dist/testing.js.map +1 -0
- package/dist/transport-composition.d.ts +31 -0
- package/dist/transport-composition.d.ts.map +1 -0
- package/dist/transport-composition.js +55 -0
- package/dist/transport-composition.js.map +1 -0
- package/dist/transport-selector.d.ts +202 -0
- package/dist/transport-selector.d.ts.map +1 -0
- package/dist/transport-selector.js +196 -0
- package/dist/transport-selector.js.map +1 -0
- package/dist/trust-signal-pickup-listener.d.ts +43 -0
- package/dist/trust-signal-pickup-listener.d.ts.map +1 -0
- package/dist/trust-signal-pickup-listener.js +48 -0
- package/dist/trust-signal-pickup-listener.js.map +1 -0
- package/dist/trust-signal-store.d.ts +406 -0
- package/dist/trust-signal-store.d.ts.map +1 -0
- package/dist/trust-signal-store.js +939 -0
- package/dist/trust-signal-store.js.map +1 -0
- package/dist/trust-signal-sweep-tick.d.ts +60 -0
- package/dist/trust-signal-sweep-tick.d.ts.map +1 -0
- package/dist/trust-signal-sweep-tick.js +101 -0
- package/dist/trust-signal-sweep-tick.js.map +1 -0
- package/dist/trust-signal-sweep.d.ts +94 -0
- package/dist/trust-signal-sweep.d.ts.map +1 -0
- package/dist/trust-signal-sweep.js +147 -0
- package/dist/trust-signal-sweep.js.map +1 -0
- package/dist/type-registry.d.ts +42 -0
- package/dist/type-registry.d.ts.map +1 -0
- package/dist/type-registry.js +37 -0
- package/dist/type-registry.js.map +1 -0
- package/dist/types.d.ts +746 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/dist/unresolved-nodes-report.d.ts +14 -0
- package/dist/unresolved-nodes-report.d.ts.map +1 -0
- package/dist/unresolved-nodes-report.js +96 -0
- package/dist/unresolved-nodes-report.js.map +1 -0
- package/dist/vocabulary.d.ts +150 -0
- package/dist/vocabulary.d.ts.map +1 -0
- package/dist/vocabulary.js +386 -0
- package/dist/vocabulary.js.map +1 -0
- package/dist/who-label.d.ts +28 -0
- package/dist/who-label.d.ts.map +1 -0
- package/dist/who-label.js +31 -0
- package/dist/who-label.js.map +1 -0
- package/dist/who-resolver.d.ts +15 -0
- package/dist/who-resolver.d.ts.map +1 -0
- package/dist/who-resolver.js +47 -0
- package/dist/who-resolver.js.map +1 -0
- package/dist/wire-content-hash.d.ts +93 -0
- package/dist/wire-content-hash.d.ts.map +1 -0
- package/dist/wire-content-hash.js +116 -0
- package/dist/wire-content-hash.js.map +1 -0
- package/dist/withheld-content.d.ts +13 -0
- package/dist/withheld-content.d.ts.map +1 -0
- package/dist/withheld-content.js +46 -0
- package/dist/withheld-content.js.map +1 -0
- package/dist/witness-alerts.d.ts +40 -0
- package/dist/witness-alerts.d.ts.map +1 -0
- package/dist/witness-alerts.js +102 -0
- package/dist/witness-alerts.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,1052 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OUTBOUND session path: discovery, the session request, and cross-node setup.
|
|
3
|
+
*
|
|
4
|
+
* DOD-SPINE-5. The initiator asks the directory to find the counterparty, requests a session, and —
|
|
5
|
+
* when the counterparty lives on a DIFFERENT directory node — opens a transient VISITING connection
|
|
6
|
+
* to that node to broker the handshake.
|
|
7
|
+
*
|
|
8
|
+
* The visiting connection is the subtle part, and it has already produced one serious bug: it is a
|
|
9
|
+
* fully authenticated stream, so the directory will drain its DURABLE notification queue down it.
|
|
10
|
+
* Anything a home stream must handle, a visiting stream must handle too (see registerSealListeners,
|
|
11
|
+
* which is why the seal listeners are a single bundle and not three separate registrations).
|
|
12
|
+
*/
|
|
13
|
+
import { SignalingManager } from "@cello-protocol/transport";
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import { createTrustSignalSweep } from "./trust-signal-sweep.js";
|
|
16
|
+
import { createTrustSignalSweepTicker } from "./trust-signal-sweep-tick.js";
|
|
17
|
+
import { classifyOnlineResult } from "./cross-node-negotiation.js";
|
|
18
|
+
import { parseDiscoveryLookupResult, discoveryLookupErrorReason, parseSessionAssignment, sessionRequestErrorReason, sessionRequestErrorGuidance, resolveOutboundMoniker } from "./session-assignment-parser.js";
|
|
19
|
+
import { DbIdentityStore } from "./db-identity-store.js";
|
|
20
|
+
import { verifyAssignmentSignature } from "./assignment-verify.js";
|
|
21
|
+
import { createSignalingConnect } from "./signaling-connect.js";
|
|
22
|
+
import { wireSessionCeremonyHandler, wireSealCeremonyHandler } from "./session-ceremony.js";
|
|
23
|
+
import { TrustSignalStore } from "./trust-signal-store.js";
|
|
24
|
+
import { TIER } from "./contacts-tier-migration.js";
|
|
25
|
+
import { encodeTrustSignalEnvelope, hashTrustSignalEnvelope } from "@cello-protocol/protocol-types";
|
|
26
|
+
import { extractErrorMessage } from "./error-message.js";
|
|
27
|
+
/**
|
|
28
|
+
* How long a session initiation waits for signaling before giving up.
|
|
29
|
+
*
|
|
30
|
+
* EXPORTED because it is a CONSTRAINT ON SOMETHING ELSE, not just a local number: every probe
|
|
31
|
+
* budget on the reconnect path has to fit inside it. `DOD-M15-BOOTSTRAP-1` learned that the hard
|
|
32
|
+
* way — a retry budget chosen in isolation pushed the resolver past this wait and would have made
|
|
33
|
+
* `cello_initiate_session` fail on exactly the lossy links the retry was added to help.
|
|
34
|
+
*
|
|
35
|
+
* `dod-m15-bootstrap-1.test.ts` asserts the budgets against this value, so changing it here breaks
|
|
36
|
+
* that test rather than silently breaking the behaviour.
|
|
37
|
+
*/
|
|
38
|
+
export const SIGNALING_CONNECT_WAIT_MS = 10_000;
|
|
39
|
+
export function createOutboundSessions(deps) {
|
|
40
|
+
const { logger, sessionNodeManager, getKeyProvider, getPersistence, getAgentSignaling, waitForSignalingConnected, getFailoverEndpoint, resolveConsortiumRoster, registerSealListeners, registerPickupListener, sessionNegotiator, challengeVerifier, getManifestVersion, loadedAgents, getUnresolvedNodes, getDeclaredNodeCount, getOnlineAgents, recordSealFailure, } = deps;
|
|
41
|
+
/**
|
|
42
|
+
* A sentence naming the roster shortfall, or "" when the roster is whole.
|
|
43
|
+
*
|
|
44
|
+
* DOD-M15-ERRSTRING-1's first clause: **a roster below threshold says so.** Every failure below
|
|
45
|
+
* used to stay silent about it, so an operator holding "the session did not establish" had no way
|
|
46
|
+
* to know the real cause was that two of their five directories were unreachable — and a threshold
|
|
47
|
+
* ceremony needs a majority. `cello_status` reported it; the error they were actually holding did
|
|
48
|
+
* not, and the error is what people act on.
|
|
49
|
+
*
|
|
50
|
+
* Appended rather than substituted: it is CONTEXT for the observation, never a replacement for it.
|
|
51
|
+
* Overwriting the observed reason with "roster short" would be the same defect this line closes,
|
|
52
|
+
* pointing the other way.
|
|
53
|
+
*/
|
|
54
|
+
function rosterShortfallNote() {
|
|
55
|
+
const unresolved = getUnresolvedNodes?.() ?? [];
|
|
56
|
+
// LAZY BY DESIGN (review MEDIUM-1). The first version resolved the roster unconditionally at the
|
|
57
|
+
// head of every negotiation — a full parallel probe of every consortium node, on the persistent
|
|
58
|
+
// budget, purely to compute a string two failure branches might use. With one node down that was
|
|
59
|
+
// +16 s on EVERY initiate, healthy ones included; on a cross-node path it compounded past a
|
|
60
|
+
// typical MCP client timeout and the operator saw a hang with no output. The condition that
|
|
61
|
+
// makes this note worth printing is exactly the condition that made it expensive.
|
|
62
|
+
if (unresolved.length === 0)
|
|
63
|
+
return "";
|
|
64
|
+
const declared = getDeclaredNodeCount?.() ?? null;
|
|
65
|
+
// No verified manifest ⇒ no denominator. Name what is unreachable and say the threshold is
|
|
66
|
+
// unknown, rather than inventing one from a subset (review MEDIUM-2).
|
|
67
|
+
const names = unresolved.map((u) => `${u.nodeId} (${u.reason})`).join(", ");
|
|
68
|
+
if (declared === null || declared === 0) {
|
|
69
|
+
return ` ALSO: ${unresolved.length} directory node(s) were unreachable at the last sweep — ${names}. ` +
|
|
70
|
+
"This daemon has no verified manifest, so it cannot say whether that leaves the consortium below the " +
|
|
71
|
+
"threshold a signing ceremony needs — but a short roster is a common hidden cause of failures like this.";
|
|
72
|
+
}
|
|
73
|
+
const threshold = Math.floor(declared / 2) + 1;
|
|
74
|
+
const reachable = declared - unresolved.length;
|
|
75
|
+
return (` ALSO: ${unresolved.length} of this consortium's ${declared} directory nodes were unreachable at the last sweep — ${names}.` +
|
|
76
|
+
(reachable < threshold
|
|
77
|
+
? ` That leaves ${reachable} reachable against a threshold of ${threshold}, so signing ceremonies CANNOT complete and this failure is very likely a symptom of that, not of anything to do with your counterparty. Fix the roster first.`
|
|
78
|
+
: ` ${reachable} of ${threshold} needed are still reachable, so ceremonies can still complete — but the margin is thin.`));
|
|
79
|
+
}
|
|
80
|
+
// ─── DOD-SPINE-5: real client-side session negotiator (built internally) ──────
|
|
81
|
+
// The directory already brokers `session_request` → FROST-signed `session_assignment`
|
|
82
|
+
// live; the missing half was the CLIENT driver. This negotiator sends `session_request`
|
|
83
|
+
// over the CURRENT agent's OWN signaling stream (so the directory routes the signed
|
|
84
|
+
// assignment back to that agent — same per-agent routing SPINE-4 established), advertising
|
|
85
|
+
// the standing receiver's session endpoint (WIRE-001: the directory rejects a request
|
|
86
|
+
// with no initiator session Peer ID), then parses the returned assignment. Tests inject their own
|
|
87
|
+
// `sessionNegotiator`; the binary gets a real one.
|
|
88
|
+
// M3: a `session_assignment` frame carries no echoed request id, so two overlapping
|
|
89
|
+
// initiations on ONE agent's stream would race to resolve on whichever assignment
|
|
90
|
+
// arrives first — request A could complete with B's assignment. Guard with a per-agent
|
|
91
|
+
// single-flight slot (genuine single-slot, as the prior comment falsely claimed). Cross-
|
|
92
|
+
// agent concurrency is unaffected (separate streams). A directory-side echoed request id
|
|
93
|
+
// would allow true concurrency later; this is the correct minimum.
|
|
94
|
+
const negotiationInProgress = new Set();
|
|
95
|
+
// ─── Cross-node session establishment (Story B, item 2) ──────────────────────
|
|
96
|
+
// Helpers the negotiator composes: discover the target's home from replicated presence, then run
|
|
97
|
+
// the session_request over the RIGHT connection — the existing home stream when same-node, or a
|
|
98
|
+
// transient VISITING connection into the target's home node when cross-node. Directories never talk
|
|
99
|
+
// to each other; the client spans nodes on demand.
|
|
100
|
+
const sleepMs = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
101
|
+
/**
|
|
102
|
+
* Issue a discovery_lookup on `signaling` and await the 3-state answer (bounded). Distinguishes,
|
|
103
|
+
* for the caller's retry logic and truthful error surface:
|
|
104
|
+
* - send_failed (home stream down — TRANSPORT, never "old directory"),
|
|
105
|
+
* - timeout (no reply — old directory OR a slow/dropped reply on a new one; retry, fall back last),
|
|
106
|
+
* - error (directory DB fault — retryable, a DIRECTORY fault, not the counterparty being offline),
|
|
107
|
+
* - malformed (a reply that didn't parse — protocol anomaly, retryable, surfaced distinctly),
|
|
108
|
+
* - result (the 3-state answer).
|
|
109
|
+
* Emits the mandated observability events: directory.discovery.lookup on a result,
|
|
110
|
+
* directory.discovery.lookup.failed on a directory error / malformed reply.
|
|
111
|
+
*/
|
|
112
|
+
async function runDiscoveryLookup(signaling, targetHex, timeoutMs, correlationId) {
|
|
113
|
+
let resolveFrame;
|
|
114
|
+
const pending = new Promise((r) => { resolveFrame = r; });
|
|
115
|
+
const unregister = signaling.registerInboundHandler((frame) => {
|
|
116
|
+
const t = frame["type"];
|
|
117
|
+
if (t === "discovery_lookup_result" || t === "discovery_lookup_error")
|
|
118
|
+
resolveFrame(frame);
|
|
119
|
+
});
|
|
120
|
+
try {
|
|
121
|
+
const sent = await signaling.sendRaw({
|
|
122
|
+
type: "discovery_lookup",
|
|
123
|
+
target_pubkey: new Uint8Array(Buffer.from(targetHex, "hex")),
|
|
124
|
+
});
|
|
125
|
+
if (!sent.ok) {
|
|
126
|
+
// The home stream is down — cannot look up (and today's local-only fallback would fail the
|
|
127
|
+
// same way). Surface the real transport reason, not an "old directory" misdiagnosis.
|
|
128
|
+
return { kind: "send_failed", reason: sent.reason ?? "signaling_unavailable" };
|
|
129
|
+
}
|
|
130
|
+
let timer;
|
|
131
|
+
const timeoutP = new Promise((r) => { timer = setTimeout(() => r({ type: "__timeout__" }), timeoutMs); });
|
|
132
|
+
const frame = await Promise.race([pending, timeoutP]);
|
|
133
|
+
clearTimeout(timer);
|
|
134
|
+
if (frame["type"] === "__timeout__")
|
|
135
|
+
return { kind: "timeout" };
|
|
136
|
+
if (frame["type"] === "discovery_lookup_error") {
|
|
137
|
+
const reason = discoveryLookupErrorReason(frame);
|
|
138
|
+
logger.warn("directory.discovery.lookup.failed", { target: targetHex.slice(0, 16), reason, correlationId });
|
|
139
|
+
return { kind: "error", reason };
|
|
140
|
+
}
|
|
141
|
+
const parsed = parseDiscoveryLookupResult(frame);
|
|
142
|
+
if (!parsed) {
|
|
143
|
+
// A reply came back but did not parse — a protocol/version anomaly on a directory that DID
|
|
144
|
+
// respond. Surface it distinctly from a clean directory DB error, and never as availability.
|
|
145
|
+
logger.warn("directory.discovery.lookup.failed", { target: targetHex.slice(0, 16), reason: "malformed_reply", correlationId });
|
|
146
|
+
return { kind: "malformed" };
|
|
147
|
+
}
|
|
148
|
+
logger.info("directory.discovery.lookup", {
|
|
149
|
+
target: targetHex.slice(0, 16),
|
|
150
|
+
state: parsed.state,
|
|
151
|
+
owningNode: parsed.owningNodeIds[0] ?? null,
|
|
152
|
+
correlationId,
|
|
153
|
+
});
|
|
154
|
+
return { kind: "result", state: parsed.state, owningNodeIds: parsed.owningNodeIds };
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
unregister();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** Send a session_request over `signaling` and await the assignment / error (the extracted core). */
|
|
161
|
+
async function runSessionRequestOverSignaling(signaling, targetHex, sr, correlationId,
|
|
162
|
+
/**
|
|
163
|
+
* WHO WE ARE — name and key together, as ONE object, and the shape is the point (review F2).
|
|
164
|
+
*
|
|
165
|
+
* `ownPubkeyHex` is this agent's own K_local pubkey, hex. It is passed in rather than looked up
|
|
166
|
+
* here because every caller has already resolved it and already refuses (`agent_not_found`)
|
|
167
|
+
* when it cannot; a second lookup would invent an absence case at the one place that must not
|
|
168
|
+
* have one — the target check below has to compare against a real key or refuse, never against
|
|
169
|
+
* an empty string that happens to mismatch.
|
|
170
|
+
*
|
|
171
|
+
* ⚠️ AN OBJECT RATHER THAN TWO POSITIONAL STRINGS, because as positional parameters they sat
|
|
172
|
+
* adjacent to `targetHex` and `correlationId` and a swap COMPILED. Only one of the three call
|
|
173
|
+
* sites is unit-reachable (the cross-node one needs a live visiting connection), so a slip at
|
|
174
|
+
* either of the other two would have made every cross-region session refuse with
|
|
175
|
+
* `assignment_names_different_self` — a security-flavoured refusal for a wiring bug, on the
|
|
176
|
+
* exact path sovereign-node redundancy depends on, with nothing red. Named properties make that
|
|
177
|
+
* swap a type error instead of a test we do not have.
|
|
178
|
+
*/
|
|
179
|
+
identity, signalFilter,
|
|
180
|
+
/**
|
|
181
|
+
* `DOD-M15-UNILATERAL-1`: opt this conversation in to the HIGH-STAKES seal tier.
|
|
182
|
+
*
|
|
183
|
+
* It changes nothing about how the session is set up — only what a SOLO seal will later
|
|
184
|
+
* require. Declared at the start because it is a property of the conversation, and because
|
|
185
|
+
* neither the relay nor the directory can infer one: the relay is deliberately blind to content
|
|
186
|
+
* and the directory never sees it.
|
|
187
|
+
*/
|
|
188
|
+
highStakes) {
|
|
189
|
+
const { agentName, ownPubkeyHex } = identity;
|
|
190
|
+
let resolveFrame;
|
|
191
|
+
const pending = new Promise((r) => { resolveFrame = r; });
|
|
192
|
+
const unregister = signaling.registerInboundHandler((frame) => {
|
|
193
|
+
const t = frame["type"];
|
|
194
|
+
// M12-P18: a trusted counterparty may answer with WHY it refused (over-cap etc.) instead of
|
|
195
|
+
// the directory's generic error. Resolve on it too, so the reason reaches the operator.
|
|
196
|
+
if (t === "session_assignment" || t === "session_request_error" || t === "session_refused")
|
|
197
|
+
resolveFrame(frame);
|
|
198
|
+
});
|
|
199
|
+
// MONIKER-2 AC1: the outbound name rides the request (agent name, or the MONIKER-1
|
|
200
|
+
// override), re-validated at offer construction (defense in depth) and OMITTED — never
|
|
201
|
+
// an empty string — when absent or invalid. A failure reading it must never block the
|
|
202
|
+
// session (spec §8: the label degrades, the session forms), but it is logged loudly.
|
|
203
|
+
let moniker;
|
|
204
|
+
try {
|
|
205
|
+
const outboundName = new DbIdentityStore(sessionNodeManager.getDb(), logger).getOutboundName(agentName);
|
|
206
|
+
moniker = resolveOutboundMoniker(outboundName);
|
|
207
|
+
// Review F4: omit-not-send is the designed degradation, but an agent whose STORED name
|
|
208
|
+
// fails the rule should be visible, not silently nameless on the wire.
|
|
209
|
+
if (outboundName !== null && moniker === undefined) {
|
|
210
|
+
logger.debug("moniker.outbound.invalid_name_omitted", { agentName, correlationId });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
logger.warn("moniker.outbound.read_failed", {
|
|
215
|
+
agentName,
|
|
216
|
+
reason: extractErrorMessage(err),
|
|
217
|
+
correlationId,
|
|
218
|
+
});
|
|
219
|
+
moniker = undefined;
|
|
220
|
+
}
|
|
221
|
+
// DOD-PRESENT-1: selective disclosure — present wallet signals to KNOWN+ contacts, nothing to
|
|
222
|
+
// UNKNOWN/BLOCKED. A failure reading the wallet must never block the session (same rule as moniker).
|
|
223
|
+
let trustSignals;
|
|
224
|
+
try {
|
|
225
|
+
const tier = sessionNodeManager.getTier(agentName, targetHex);
|
|
226
|
+
if (tier >= TIER.KNOWN) {
|
|
227
|
+
// DOD-END-SCOPE-FIX-1: the wallet is daemon-wide and shared by every agent on the device,
|
|
228
|
+
// so the presenting agent's own K_local pubkey is what makes an agent-subject signal his to
|
|
229
|
+
// present. It is the SAME value an agent-subject envelope's `subject` holds — the directory
|
|
230
|
+
// authenticates the signaling stream with this key and resolves it against
|
|
231
|
+
// `agent_profiles.k_local_pubkey`, which is the column it joins `subject` on.
|
|
232
|
+
const agentRec = loadedAgents.find((a) => a.name === agentName);
|
|
233
|
+
if (!agentRec) {
|
|
234
|
+
// Not reachable through a loaded agent's own session, and refusing is the only safe
|
|
235
|
+
// answer: an unresolved presenter cannot be scoped, and presenting unscoped is the defect.
|
|
236
|
+
// NOTE: this throw is CAUGHT by this function's own catch below and downgraded to a warn — the
|
|
237
|
+
// session then forms with no signals. Not a refusal of the session; a refusal to PRESENT.
|
|
238
|
+
throw new Error(`presenting agent '${agentName}' is not loaded — cannot scope the wallet`);
|
|
239
|
+
}
|
|
240
|
+
const store = new TrustSignalStore(sessionNodeManager.getDb(), logger);
|
|
241
|
+
const eligible = store.listAllActive({
|
|
242
|
+
presentingAgentPubkeyHex: agentRec.pubkey,
|
|
243
|
+
include: signalFilter?.include,
|
|
244
|
+
exclude: signalFilter?.exclude,
|
|
245
|
+
});
|
|
246
|
+
// M10B / DOD-END-SURFACE-1 — the per-counterparty choice, applied LAST and only ever to
|
|
247
|
+
// NARROW. `listAllActive` has already enforced consent (accepted-only, in SQL) and the
|
|
248
|
+
// caller's per-call filter; this can drop a signal from that set but can never add one, so
|
|
249
|
+
// an explicit `present: true` for a contact cannot resurrect something consent excluded.
|
|
250
|
+
// A signal with no recorded choice keeps whatever the previous stages decided.
|
|
251
|
+
const prefs = sessionNodeManager.getContactSignalPrefs(agentName, targetHex);
|
|
252
|
+
const selected = prefs.size === 0 ? eligible : eligible.filter((s) => prefs.get(s.signalHash) !== false);
|
|
253
|
+
const omittedByContact = eligible.length - selected.length;
|
|
254
|
+
if (omittedByContact > 0) {
|
|
255
|
+
logger.info("signal.presentation.contact_omitted", {
|
|
256
|
+
agentName, target: targetHex.slice(0, 16), omitted: omittedByContact, correlationId,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (selected.length > 0) {
|
|
260
|
+
// ── PRESENT NOTHING YOU CANNOT REPRODUCE ────────────────────────────────────────────────
|
|
261
|
+
// Presentation re-encodes the envelope from the WALLET ROW and sends the notarized hash
|
|
262
|
+
// beside it. If any stored field does not round-trip, the bytes and the hash disagree — and
|
|
263
|
+
// the only party that finds out is the RECIPIENT, who logs `hash_mismatch` and drops it. From
|
|
264
|
+
// the presenter's side that is indistinguishable from success: the session forms, the log
|
|
265
|
+
// says `attached: 1`, and the operator's endorsement silently never counts anywhere.
|
|
266
|
+
//
|
|
267
|
+
// So the presenter re-derives its own hash and refuses to send a signal it cannot reproduce,
|
|
268
|
+
// naming the two hashes. A wallet round-trip defect is now a loud error at the party that
|
|
269
|
+
// owns the row, not a silent rejection one hop away.
|
|
270
|
+
const reproducible = [];
|
|
271
|
+
for (const s of selected) {
|
|
272
|
+
const blob = encodeTrustSignalEnvelope({
|
|
273
|
+
subject_kind: s.subjectKind,
|
|
274
|
+
subject: s.subject,
|
|
275
|
+
issuer_kind: s.issuerKind,
|
|
276
|
+
issuer_pubkey: s.issuerPubkey,
|
|
277
|
+
type: s.type,
|
|
278
|
+
schema_version: s.schemaVersion,
|
|
279
|
+
payload: s.payload,
|
|
280
|
+
issued_at: s.issuedAt,
|
|
281
|
+
expires_at: s.expiresAt,
|
|
282
|
+
supersedes_hash: s.supersedesHash === null ? null : new Uint8Array(Buffer.from(s.supersedesHash, "hex")),
|
|
283
|
+
// THE STORED VALUE, never a literal. Re-encoding with the wrong value changes the
|
|
284
|
+
// envelope bytes, so the hash stops matching what the directory notarized and the
|
|
285
|
+
// recipient rejects a perfectly valid signal.
|
|
286
|
+
same_operator: s.sameOperator,
|
|
287
|
+
});
|
|
288
|
+
const derived = Buffer.from(hashTrustSignalEnvelope({
|
|
289
|
+
subject_kind: s.subjectKind, subject: s.subject, issuer_kind: s.issuerKind,
|
|
290
|
+
issuer_pubkey: s.issuerPubkey, type: s.type, schema_version: s.schemaVersion,
|
|
291
|
+
payload: s.payload, issued_at: s.issuedAt, expires_at: s.expiresAt,
|
|
292
|
+
supersedes_hash: s.supersedesHash === null ? null : new Uint8Array(Buffer.from(s.supersedesHash, "hex")),
|
|
293
|
+
same_operator: s.sameOperator,
|
|
294
|
+
})).toString("hex");
|
|
295
|
+
if (derived !== s.signalHash) {
|
|
296
|
+
logger.error("signal.presentation.unreproducible", {
|
|
297
|
+
agentName,
|
|
298
|
+
notarized: s.signalHash.slice(0, 16),
|
|
299
|
+
derived: derived.slice(0, 16),
|
|
300
|
+
type: s.type,
|
|
301
|
+
issuerKind: s.issuerKind,
|
|
302
|
+
sameOperator: s.sameOperator,
|
|
303
|
+
expiresAt: s.expiresAt,
|
|
304
|
+
issuedAt: s.issuedAt,
|
|
305
|
+
subjectKind: s.subjectKind,
|
|
306
|
+
subject: s.subject,
|
|
307
|
+
issuerPubkey: s.issuerPubkey.slice(0, 16),
|
|
308
|
+
supersedesHash: s.supersedesHash,
|
|
309
|
+
schemaVersion: s.schemaVersion,
|
|
310
|
+
payloadLen: s.payload.length,
|
|
311
|
+
payloadSha: createHash("sha256").update(Buffer.from(s.payload)).digest("hex").slice(0, 16),
|
|
312
|
+
correlationId,
|
|
313
|
+
});
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
reproducible.push({ ...s, presentedBlob: blob });
|
|
317
|
+
}
|
|
318
|
+
trustSignals = reproducible.map((s) => ({
|
|
319
|
+
hash: s.signalHash,
|
|
320
|
+
blob: s.presentedBlob,
|
|
321
|
+
}));
|
|
322
|
+
if (trustSignals.length === 0) {
|
|
323
|
+
// EVERY selected signal failed its own reproducibility check. "attached: 0" would be a lie
|
|
324
|
+
// in the one case that matters most — the wallet held presentable rows and not one of them
|
|
325
|
+
// could be re-encoded — and it reads identically to holding nothing at all.
|
|
326
|
+
logger.error("signal.presentation.all_unreproducible", {
|
|
327
|
+
agentName, target: targetHex.slice(0, 16), selected: selected.length, correlationId,
|
|
328
|
+
});
|
|
329
|
+
trustSignals = undefined;
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
logger.info("signal.presentation.attached", {
|
|
333
|
+
agentName,
|
|
334
|
+
target: targetHex.slice(0, 16),
|
|
335
|
+
count: trustSignals.length,
|
|
336
|
+
// Non-zero means some rows were selected and then dropped — visible here rather than only
|
|
337
|
+
// in the per-signal errors above.
|
|
338
|
+
...(selected.length !== trustSignals.length ? { droppedUnreproducible: selected.length - trustSignals.length } : {}),
|
|
339
|
+
correlationId,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
// A ZERO-ROW PRESENTATION IS THE FAILURE THIS UNIT EXISTS TO PREVENT, so it must not be
|
|
345
|
+
// the one outcome that emits nothing. Without this line "the operator holds no signals"
|
|
346
|
+
// and "the operator's signals no longer match their key" are the same observation: silence
|
|
347
|
+
// plus a session that formed normally. The second is reachable — a key rotation, a
|
|
348
|
+
// re-registration, a signal minted against a stale `k_local_pubkey` — and it is exactly
|
|
349
|
+
// the shape the DoD describes as "goes green while matching ZERO rows in production".
|
|
350
|
+
// `heldTotal` is what makes them distinguishable: 0 means nothing to present, >0 means the
|
|
351
|
+
// wallet has rows that this agent's scoping excluded.
|
|
352
|
+
logger.info("signal.presentation.none_eligible", {
|
|
353
|
+
agentName,
|
|
354
|
+
target: targetHex.slice(0, 16),
|
|
355
|
+
heldTotal: store.listAllWalletSignals().length,
|
|
356
|
+
// Three different silences that used to look identical. `eligibleBeforeContactPrefs > 0`
|
|
357
|
+
// with nothing sent means the OPERATOR omitted them for this counterparty — a choice, not
|
|
358
|
+
// a fault — and without this field it reads the same as a scoping bug.
|
|
359
|
+
eligibleBeforeContactPrefs: eligible.length,
|
|
360
|
+
contactPrefsApplied: prefs.size,
|
|
361
|
+
filter: signalFilter ?? null,
|
|
362
|
+
correlationId,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
// DEGRADES, it does not refuse — and the comment on the throw above used to claim otherwise.
|
|
369
|
+
// A wallet read that fails (including the deliberate "agent not loaded" throw, which is
|
|
370
|
+
// annotated "refusing is the only safe answer") lands here, is downgraded to a warn, and the
|
|
371
|
+
// session proceeds with NO signals attached. That behaviour is CORRECT — presenting unscoped is
|
|
372
|
+
// the actual danger and presenting nothing is safe — but the code and the comment disagreed
|
|
373
|
+
// about which one happens, and a comment that misdescribes the control flow is worse than
|
|
374
|
+
// either, because the next reader trusts it instead of the code.
|
|
375
|
+
logger.warn("signal.presentation.read_failed", {
|
|
376
|
+
agentName,
|
|
377
|
+
reason: extractErrorMessage(err),
|
|
378
|
+
correlationId,
|
|
379
|
+
});
|
|
380
|
+
trustSignals = undefined;
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
const sent = await signaling.sendRaw({
|
|
384
|
+
type: "session_request",
|
|
385
|
+
target_pubkey: new Uint8Array(Buffer.from(targetHex, "hex")),
|
|
386
|
+
initiator_session_peer_id: sr.peerId,
|
|
387
|
+
initiator_session_addrs: sr.addrs,
|
|
388
|
+
wants_session_offer: true,
|
|
389
|
+
...(moniker !== undefined ? { moniker } : {}),
|
|
390
|
+
...(trustSignals !== undefined ? { trust_signals: trustSignals } : {}),
|
|
391
|
+
// DOD-M15-UNILATERAL-1: sent ONLY when opted in. The directory reads it strictly (`=== true`)
|
|
392
|
+
// and every other value, absence included, means the standard tier.
|
|
393
|
+
...(highStakes === true ? { high_stakes: true } : {}),
|
|
394
|
+
});
|
|
395
|
+
if (!sent.ok) {
|
|
396
|
+
return { ok: false, reason: sent.reason ?? "directory_unreachable", guidance: sent.guidance ?? "Could not send session_request over the directory signaling stream." };
|
|
397
|
+
}
|
|
398
|
+
let timer;
|
|
399
|
+
const timeoutP = new Promise((r) => { timer = setTimeout(() => r({ type: "__timeout__" }), 30_000); });
|
|
400
|
+
const frame = await Promise.race([pending, timeoutP]);
|
|
401
|
+
clearTimeout(timer);
|
|
402
|
+
if (frame["type"] === "__timeout__") {
|
|
403
|
+
return { ok: false, reason: "timeout", guidance: "The directory did not return a session assignment within 30s. Retry once cello status shows directory_signaling connected." };
|
|
404
|
+
}
|
|
405
|
+
if (frame["type"] === "session_request_error") {
|
|
406
|
+
const reason = sessionRequestErrorReason(frame);
|
|
407
|
+
return { ok: false, reason, guidance: sessionRequestErrorGuidance(reason) };
|
|
408
|
+
}
|
|
409
|
+
// M12-P18: the COUNTERPARTY refused, and told us why (only KNOWN+ senders get this — a
|
|
410
|
+
// stranger still gets silence, so no block/throttle oracle). Surface their guidance verbatim.
|
|
411
|
+
if (frame["type"] === "session_refused") {
|
|
412
|
+
const reason = typeof frame["reason"] === "string" ? frame["reason"] : "refused_by_counterparty";
|
|
413
|
+
const guidance = typeof frame["guidance"] === "string"
|
|
414
|
+
? frame["guidance"]
|
|
415
|
+
: `The counterparty refused this session (${reason}).`;
|
|
416
|
+
return { ok: false, reason, guidance };
|
|
417
|
+
}
|
|
418
|
+
const raw = frame["assignment"];
|
|
419
|
+
const assignment = raw ? parseSessionAssignment(raw) : null;
|
|
420
|
+
if (!assignment) {
|
|
421
|
+
return { ok: false, reason: "assignment_parse_failed", guidance: "The directory's session_assignment was missing or malformed." };
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* DOD-M15-ASSIGN-1 — VERIFY THE SIGNATURE BEFORE ANYTHING TRUSTS THE ASSIGNMENT.
|
|
425
|
+
*
|
|
426
|
+
* WHAT THIS STOPS, stated first because an earlier draft of this note undersold it into
|
|
427
|
+
* sounding like paperwork: **without it, whichever directory node this daemon is talking to
|
|
428
|
+
* can hand it a permission slip naming any peer id and addresses it likes, and the daemon
|
|
429
|
+
* dials them.** One compromised directory could put an operator into a session with an
|
|
430
|
+
* impostor, and everything downstream would look normal — the impostor is a real agent
|
|
431
|
+
* signing with its own real key. Verification means a single node cannot do that: the slip
|
|
432
|
+
* must carry a threshold signature from this agent's own quorum, which no one node can
|
|
433
|
+
* produce. That is the forgery-resistance property FROST exists for, and it was claimed in
|
|
434
|
+
* the design and enforced nowhere.
|
|
435
|
+
*
|
|
436
|
+
* THE BOUND, equally explicit: a THRESHOLD of this agent's own directories, colluding, could
|
|
437
|
+
* still produce a VALID SIGNATURE over an assignment naming the wrong counterparty. That is
|
|
438
|
+
* the designed-for limit of the signature check and it does not move. What that signature can
|
|
439
|
+
* no longer buy them is a session: `DOD-M15-ASSIGN-TARGET-1` below compares the participants
|
|
440
|
+
* the slip names against the two values this daemon already holds, and refuses before
|
|
441
|
+
* anything dials. Ingest-time wrong-signer detection (`DOD-M15-FRAME-1`) and relay
|
|
442
|
+
* corroboration (`DOD-M15-CORROBORATE-1`) still run — they are now the second line, not the
|
|
443
|
+
* first.
|
|
444
|
+
*/
|
|
445
|
+
const verified = await verifyAssignmentSignature(assignment, getPersistence(agentName), logger, agentName, correlationId);
|
|
446
|
+
if (!verified.ok) {
|
|
447
|
+
return { ok: false, reason: verified.reason, guidance: verified.guidance };
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* DOD-M15-ASSIGN-TARGET-1 — THE SLIP MUST NAME WHO YOU ASKED FOR.
|
|
451
|
+
*
|
|
452
|
+
* A verified signature says the slip is genuine. It says nothing about WHO it is about, and
|
|
453
|
+
* until now nothing on the client compared `participant_b` to the pubkey the operator typed.
|
|
454
|
+
* A colluding quorum could name an impostor, the signature would verify, and the daemon would
|
|
455
|
+
* dial them.
|
|
456
|
+
*
|
|
457
|
+
* ⚠️ IT MUST BE HERE, BEFORE THE RETURN — not at ingest, where the existing wrong-signer
|
|
458
|
+
* check lives.
|
|
459
|
+
*
|
|
460
|
+
* ─── AND THE REASON IS NOT "the first message goes out in plaintext" (review F3) ──────────
|
|
461
|
+
*
|
|
462
|
+
* That was the premise this check was written from and IT DOES NOT HOLD IN THIS TREE, which
|
|
463
|
+
* is worth stating because a reader who assumed it would think this guard is holding a line
|
|
464
|
+
* it is not. On the initiator side the content key is agreed from a peer ephemeral that must
|
|
465
|
+
* verify against the session's counterparty record, and that record is the operator's own
|
|
466
|
+
* `target_pubkey` — not `participant_b` (`session-ephemerals.ts`). A substituted counterparty
|
|
467
|
+
* cannot sign a passing half, and a send with no agreed key throws rather than going out
|
|
468
|
+
* (`content-encryption-status.ts` states the rule as absolute). No plaintext leaves.
|
|
469
|
+
*
|
|
470
|
+
* ─── What was actually wrong, which is a better reason than the one it replaces ──────────
|
|
471
|
+
*
|
|
472
|
+
* 1. ERROR SUBSTITUTION, and it is the serious one. A substituted counterparty surfaced to
|
|
473
|
+
* the operator as `session.key.refused`, whose guidance names *"something in the middle of
|
|
474
|
+
* your connection substituting its own key"* — pointing at the relay and the network for a
|
|
475
|
+
* fault that was entirely the directory's. The operator was told to distrust the right
|
|
476
|
+
* thing for the wrong reason and sent to investigate the wrong party.
|
|
477
|
+
* 2. THE DIAL ITSELF DISCLOSES. We hand the impostor our session peer id and, on a direct
|
|
478
|
+
* address, our IP — permanently — before anything has failed.
|
|
479
|
+
* 3. THE SEAL ANCHOR DISAGREES WITH THE SESSION. `recordSessionGenesis` anchors the
|
|
480
|
+
* transcript to `participant_a`/`participant_b` while the session's own counterparty
|
|
481
|
+
* record holds what the operator asked for. Refusing here keeps those two provably equal.
|
|
482
|
+
*
|
|
483
|
+
* The ingest wrong-signer check still runs and still freezes on a bad reply; it is now the
|
|
484
|
+
* second line rather than the first, and it is not what makes this safe.
|
|
485
|
+
*
|
|
486
|
+
* Both sides of both comparisons are LOCAL values: `targetHex` is the operator's own input
|
|
487
|
+
* and `ownPubkeyHex` is this daemon's loaded identity. Neither is influenced by anything the
|
|
488
|
+
* directory returned, which is what makes the comparison worth anything.
|
|
489
|
+
*
|
|
490
|
+
* Case-insensitive because `target_pubkey` is operator input, accepted as `[0-9a-fA-F]{64}`.
|
|
491
|
+
*/
|
|
492
|
+
const namedTarget = Buffer.from(assignment.participant_b.pubkey).toString("hex").toLowerCase();
|
|
493
|
+
if (namedTarget !== targetHex.toLowerCase()) {
|
|
494
|
+
logger.error("session.assignment.target_mismatch", {
|
|
495
|
+
agentName, correlationId,
|
|
496
|
+
askedForPrefix: targetHex.toLowerCase().slice(0, 16), namedPrefix: namedTarget.slice(0, 16),
|
|
497
|
+
impact: "the directory returned a validly-signed assignment naming a counterparty other than the one requested; it was refused before any dial, so no conversation was opened and nothing was sent",
|
|
498
|
+
});
|
|
499
|
+
return {
|
|
500
|
+
ok: false,
|
|
501
|
+
reason: "assignment_names_different_counterparty",
|
|
502
|
+
// ⚠️ LEAD WITH THE ATTRIBUTION, and the reason is measurable rather than stylistic (review
|
|
503
|
+
// F1). The directory echoes the `target_pubkey` we sent it straight back into
|
|
504
|
+
// `participant_b`, so an operator TYPO cannot produce this — a typo puts the same wrong
|
|
505
|
+
// key on both sides and matches. This fault is 100% directory-side. Guidance that opened
|
|
506
|
+
// by telling the operator to re-check the key sent them to the one place it cannot be.
|
|
507
|
+
guidance: "The directory returned a session assignment naming a counterparty other than the one you asked for. No conversation was opened and nothing you wrote was sent. This is not a typo on your side — the directory echoes back the key you sent it, so a mismatch means the node that answered departed from the protocol. Run cello_status to see which directory node answered, then retry cello_initiate_session; a different node will serve the request. Only if it repeats across nodes is it worth confirming the key with the counterparty's operator out of band.",
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
const namedSelf = Buffer.from(assignment.participant_a.pubkey).toString("hex").toLowerCase();
|
|
511
|
+
if (namedSelf !== ownPubkeyHex.toLowerCase()) {
|
|
512
|
+
logger.error("session.assignment.self_mismatch", {
|
|
513
|
+
agentName, correlationId,
|
|
514
|
+
ownPrefix: ownPubkeyHex.toLowerCase().slice(0, 16), namedPrefix: namedSelf.slice(0, 16),
|
|
515
|
+
impact: "the directory returned a validly-signed assignment putting a different agent in this agent's own seat; it was refused before any dial, so no conversation was opened and nothing was sent",
|
|
516
|
+
});
|
|
517
|
+
return {
|
|
518
|
+
ok: false,
|
|
519
|
+
reason: "assignment_names_different_self",
|
|
520
|
+
// Same correction as above (review F1): the directory derives `participant_a` from the key
|
|
521
|
+
// the signaling stream AUTHENTICATED with, not from anything the operator typed, so "check
|
|
522
|
+
// which agent is selected" names a cause that cannot produce this either.
|
|
523
|
+
guidance: "The directory returned a session assignment naming a different agent as this side of the conversation. No conversation was opened and nothing you wrote was sent. This is not a local selection problem — the directory derives this side from the key your daemon authenticated with, so a mismatch means the node that answered is brokering sessions in another agent's name. Run cello_status to see which directory node answered, then retry cello_initiate_session; a different node will serve the request.",
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
logger.info("session.negotiate.assignment.received", { agentName, correlationId, signatureType: assignment.signature_type, signatureVerified: true });
|
|
527
|
+
return { ok: true, assignment, counterpartyPrimaryHex: verified.counterpartyPrimaryHex };
|
|
528
|
+
}
|
|
529
|
+
finally {
|
|
530
|
+
unregister();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Open a transient VISITING signaling connection into a specific directory node (the target's home /
|
|
535
|
+
* broker) and wire what a cross-node initiator needs there:
|
|
536
|
+
* - the delegated-signer ceremony handler (the broker asks the initiator to co-sign the assignment
|
|
537
|
+
* over THIS connection); and
|
|
538
|
+
* - Fix #1 (cross-node seal-liveness): the seal ceremony handler + session_sealed/unilateral
|
|
539
|
+
* listeners, because for a cross-node CLOSE the broker ALSO pushes seal_verified then session_sealed
|
|
540
|
+
* over the connection the initiator holds to it. The seal FROST still runs over the agent's OWN
|
|
541
|
+
* roster (getNode: () => nodeRef) — only the control frames traverse this stream.
|
|
542
|
+
* Transient and initiator-only. The caller MUST stop() it after the assignment (setup path) or after
|
|
543
|
+
* the seal reaches a terminal outcome (close path).
|
|
544
|
+
*/
|
|
545
|
+
function openVisitingConnection(agentName, agentKeyProvider, agentPubkeyHex, endpoint, correlationId, nodeId) {
|
|
546
|
+
let nodeRef = null;
|
|
547
|
+
const connect = createSignalingConnect({
|
|
548
|
+
getDirectoryEndpoint: () => endpoint,
|
|
549
|
+
getAuthIdentity: () => ({ keyProvider: agentKeyProvider, pubkeyHex: agentPubkeyHex }),
|
|
550
|
+
logger,
|
|
551
|
+
challengeVerifier,
|
|
552
|
+
getManifestVersion,
|
|
553
|
+
visiting: true, // cross-node item 3: the directory must NOT write presence for this connection
|
|
554
|
+
publishNode: (n) => { nodeRef = n; },
|
|
555
|
+
/**
|
|
556
|
+
* ⚠️ **DOD-M15-TOKENRACE-1 — THIS CONNECTION IS NOW LOAD-BEARING FOR RELAY ACCESS, NOT ONLY
|
|
557
|
+
* FOR TRUST SIGNALS.**
|
|
558
|
+
*
|
|
559
|
+
* The relay refuses to talk to an agent at all without a directory-signed online token — one
|
|
560
|
+
* gate on the general auth path, so it covers taking a slot, sending a message, sealing, and
|
|
561
|
+
* parked mail alike. The directory mints one onto `signaling_auth_ok`, which is the HANDSHAKE,
|
|
562
|
+
* and it lives one hour. The signaling heartbeat keeps the home stream alive and reconnects
|
|
563
|
+
* when it dies, and it works — so a healthy connection never handshakes again and never gets
|
|
564
|
+
* another token. **The better the connection, the staler the credential.** Measured on a real
|
|
565
|
+
* daemon: 18 gaps over the one-hour lifetime in four days, worst case 8.4 hours, with no
|
|
566
|
+
* disconnect inside that window at all.
|
|
567
|
+
*
|
|
568
|
+
* This connection already authenticates to every other node every five minutes, and the
|
|
569
|
+
* directory issues a token on that auth_ok unconditionally — `#issueOnlineToken` is gated only
|
|
570
|
+
* on the agent having a profile at that node, not on `visiting` and not on presence. The token
|
|
571
|
+
* was arriving all along and being parsed, logged and dropped. Storing it is the whole fix: no
|
|
572
|
+
* new frame, no directory change, no deploy ordering.
|
|
573
|
+
*
|
|
574
|
+
* ⚠️ **AND THE COUPLING IS DELIBERATE, SO IT IS SAID OUT LOUD.** Credential freshness now
|
|
575
|
+
* depends on `SWEEP_TICK_INTERVAL_MS` continuing to be well under
|
|
576
|
+
* `ONLINE_TOKEN_ISSUE_LIFETIME_MS` — five minutes against sixty, a twelvefold margin. That is
|
|
577
|
+
* the same shape as a mailbox drain riding a receiver rebuild, which 056-SLOTDEAD spent a day
|
|
578
|
+
* removing, so it is pinned by a test rather than trusted. If the sweep is ever removed or
|
|
579
|
+
* slowed past the margin, this needs the durable version: the directory pushing a fresh
|
|
580
|
+
* credential down the live stream before the old one expires.
|
|
581
|
+
*/
|
|
582
|
+
onOnlineToken: (token) => { sessionNodeManager.setDirectoryOnlineToken(agentName, token); },
|
|
583
|
+
});
|
|
584
|
+
// maxReconnectAttempts: 1 — a transient connection should fail fast, not reconnect-loop forever.
|
|
585
|
+
const mgr = new SignalingManager({ connect, logger, maxReconnectAttempts: 1, maxBackoffMs: 3_000 });
|
|
586
|
+
wireSessionCeremonyHandler({
|
|
587
|
+
agentName,
|
|
588
|
+
persistence: getPersistence(agentName),
|
|
589
|
+
agentPubkeyHex,
|
|
590
|
+
// DOD-M15-SEALWIRE-1 bullet 2 (review F1): gate the co-signature on the root check.
|
|
591
|
+
verifyCertifiedRoot: (pub, sid, root, leaves, evidence) => sessionNodeManager.verifyCertifiedRoot(pub, sid, root, leaves, evidence),
|
|
592
|
+
keyProvider: agentKeyProvider,
|
|
593
|
+
getNode: () => nodeRef,
|
|
594
|
+
getDirectoryEndpoint: getFailoverEndpoint,
|
|
595
|
+
getConsortiumEndpoints: resolveConsortiumRoster,
|
|
596
|
+
signaling: mgr,
|
|
597
|
+
logger,
|
|
598
|
+
recordSealFailure,
|
|
599
|
+
});
|
|
600
|
+
// Fix #1 (cross-node seal-liveness): a visiting connection must ALSO be able to complete the SEAL.
|
|
601
|
+
// The broker (the node this visiting connection targets) pushes seal_verified then session_sealed to
|
|
602
|
+
// whichever stream the initiator holds to it — for a cross-node close that is THIS visiting stream.
|
|
603
|
+
// Run the seal FROST ceremony over the agent's own node (getNode: () => nodeRef, exactly as the
|
|
604
|
+
// session ceremony above) and reply/resolve on this stream. Harmless on a setup-only visiting
|
|
605
|
+
// connection: these frames never arrive before it is released after handoff.
|
|
606
|
+
wireSealCeremonyHandler({
|
|
607
|
+
agentName,
|
|
608
|
+
persistence: getPersistence(agentName),
|
|
609
|
+
agentPubkeyHex,
|
|
610
|
+
// DOD-M15-SEALWIRE-1 bullet 2 (review F1): gate the co-signature on the root check.
|
|
611
|
+
verifyCertifiedRoot: (pub, sid, root, leaves, evidence) => sessionNodeManager.verifyCertifiedRoot(pub, sid, root, leaves, evidence),
|
|
612
|
+
keyProvider: agentKeyProvider,
|
|
613
|
+
getNode: () => nodeRef,
|
|
614
|
+
getDirectoryEndpoint: getFailoverEndpoint,
|
|
615
|
+
getConsortiumEndpoints: resolveConsortiumRoster,
|
|
616
|
+
signaling: mgr,
|
|
617
|
+
logger,
|
|
618
|
+
recordSealFailure,
|
|
619
|
+
});
|
|
620
|
+
// FIX (Seam B review): this VISITING stream used to register only the sealed + unilateral-confirmed
|
|
621
|
+
// listeners. That silently lost seal receipts — the directory drains its DURABLE notification queue
|
|
622
|
+
// on ANY stream that authenticates, visiting included, and deletes each row once sent. A
|
|
623
|
+
// seal_unilateral_notification pushed down here hit no handler, was dropped, and its row was gone;
|
|
624
|
+
// the absent party never ratified and the seal stayed unilateral forever. Every stream that can
|
|
625
|
+
// carry a seal frame now gets every seal listener — and the coordinator no longer lets it be
|
|
626
|
+
// wired any other way.
|
|
627
|
+
registerSealListeners(mgr, agentName, agentPubkeyHex);
|
|
628
|
+
// 043-SIGNALDELIVERY C: AND the trust-signal pickups, for exactly the reason the seal listeners
|
|
629
|
+
// above are here. The directory drains its pickup queue down any authenticated stream, visiting
|
|
630
|
+
// included — it was pushing them at this connection all along and the daemon had no handler, so
|
|
631
|
+
// every frame was dropped and re-dropped on the next visit. Nothing was lost (the directory
|
|
632
|
+
// deletes on ACK, never on send), but nothing arrived either, and the operator's wallet stayed
|
|
633
|
+
// empty while the portal reported success.
|
|
634
|
+
const pickups = registerPickupListener(mgr, agentName, agentKeyProvider, nodeId);
|
|
635
|
+
logger.info("signaling.visiting.connected", { agentName, node: nodeId, correlationId });
|
|
636
|
+
return {
|
|
637
|
+
mgr,
|
|
638
|
+
stop: async (reason) => {
|
|
639
|
+
logger.info("signaling.visiting.released", { agentName, node: nodeId, reason, correlationId });
|
|
640
|
+
// Let an in-flight pickup finish acking before the stream goes. The handler opens the seal,
|
|
641
|
+
// writes the wallet and only then acks; a teardown that wins that race leaves the visited
|
|
642
|
+
// node's row unacked, so the same pickup is re-sent on every future visit. Bounded, so a
|
|
643
|
+
// wedged handler delays this close rather than preventing it.
|
|
644
|
+
await pickups.settle(2_000);
|
|
645
|
+
await mgr.stop();
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
// Fix #1 (cross-node seal-liveness): the broker node (the counterparty's home) for each cross-node
|
|
650
|
+
// session we initiated, keyed `${agentName}:${sessionIdHex}`. Set when runCrossNodeSetup establishes
|
|
651
|
+
// the session; read by cello_close_session so it can re-open a visiting connection to the broker and
|
|
652
|
+
// complete the seal there. The broker pushes seal_verified/session_sealed, but the initiator RELEASES
|
|
653
|
+
// its visiting connection after setup — so without reconnecting, the close times out
|
|
654
|
+
// (seal_unilateral_timeout) and the seal only completes whenever the daemon next happens to reconnect.
|
|
655
|
+
// Presence of an entry means the session is cross-node (same-node sessions never go through
|
|
656
|
+
// runCrossNodeSetup). In-memory only: a close in the SAME process (the common case) is covered; a close
|
|
657
|
+
// after a daemon restart falls back to the pre-fix behavior (deferred hardening: persist on the row).
|
|
658
|
+
// 043-SIGNALDELIVERY C2: the background collection sweep. Built HERE because every dep it needs
|
|
659
|
+
// is already in scope — the roster resolver, the logger, and openVisitingConnection itself, which
|
|
660
|
+
// is the only way to authenticate at another node and therefore the only thing that makes that
|
|
661
|
+
// node's drain run. Building it in the composition root would mean threading all three back out.
|
|
662
|
+
const sweepTrustSignals = createTrustSignalSweep({
|
|
663
|
+
logger,
|
|
664
|
+
resolveConsortiumRoster,
|
|
665
|
+
openVisitingConnection,
|
|
666
|
+
});
|
|
667
|
+
// 048-SWEEPTICK: the sweep above was wired to `onConnected` and to nothing else, so a daemon that
|
|
668
|
+
// stayed connected never collected again — an endorsement sat 23 minutes at a node it was not
|
|
669
|
+
// attached to and arrived only on restart. The ticker wraps it: sweep on connect exactly as
|
|
670
|
+
// before, AND every five minutes after. `sweepTrustSignalsAndTick` is what the wiring calls; the
|
|
671
|
+
// bare sweep is still exported for anything that wants one collection without arming a timer.
|
|
672
|
+
const trustSignalSweepTicker = createTrustSignalSweepTicker({
|
|
673
|
+
logger,
|
|
674
|
+
sweep: sweepTrustSignals,
|
|
675
|
+
isAgentOnline: (agentName) => getOnlineAgents().has(agentName),
|
|
676
|
+
});
|
|
677
|
+
const crossNodeBrokerBySession = new Map();
|
|
678
|
+
/**
|
|
679
|
+
* Resolve `owningNodeId` through the signed manifest and run the session_request over a transient
|
|
680
|
+
* visiting connection there. A node id that doesn't resolve in the roster is a hard error
|
|
681
|
+
* (discovery_node_unresolvable) — never a dial to an unvalidated endpoint.
|
|
682
|
+
*/
|
|
683
|
+
async function runCrossNodeSetup(agentName, agentKeyProvider, agentPubkeyHex, owningNodeId, targetHex, sr, correlationId, signalFilter,
|
|
684
|
+
/** DOD-M15-UNILATERAL-1: carried to the brokering node exactly as on the local path. */
|
|
685
|
+
highStakes) {
|
|
686
|
+
const roster = await resolveConsortiumRoster();
|
|
687
|
+
const target = roster?.find((e) => e.nodeId === owningNodeId) ?? null;
|
|
688
|
+
if (!target) {
|
|
689
|
+
/**
|
|
690
|
+
* DOD-M15-ERRSTRING-1 (review F1) — THIS IS NOT A MANIFEST MISS, and calling it one sent
|
|
691
|
+
* operators to investigate consortium membership over a lost packet.
|
|
692
|
+
*
|
|
693
|
+
* `resolveConsortiumRoster()` returns the REACHABLE subset: `manifestNodesToEndpoints` drops
|
|
694
|
+
* any node whose `/bootstrap` probe failed. So absence from this list has two very different
|
|
695
|
+
* causes and this code cannot tell them apart —
|
|
696
|
+
* 1. our own probe of that node did not answer during the last sweep (the common one, and
|
|
697
|
+
* before `DOD-M15-BOOTSTRAP-1` a single lost packet was enough to produce it);
|
|
698
|
+
* 2. the node genuinely is not in the signed manifest.
|
|
699
|
+
* The old message asserted (2) unconditionally, in the words "It may have left the
|
|
700
|
+
* consortium." The comment above it asserted the same thing to the next reader.
|
|
701
|
+
*
|
|
702
|
+
* Named for what was observed — it is not in the roster we could reach — with both causes in
|
|
703
|
+
* the order they actually occur, and the check that separates them.
|
|
704
|
+
*/
|
|
705
|
+
logger.warn("session.crossnode.failed", {
|
|
706
|
+
agentName,
|
|
707
|
+
brokerNode: owningNodeId,
|
|
708
|
+
reason: "home_node_not_in_reachable_roster",
|
|
709
|
+
rosterSize: roster?.length ?? 0,
|
|
710
|
+
correlationId,
|
|
711
|
+
});
|
|
712
|
+
return {
|
|
713
|
+
ok: false,
|
|
714
|
+
reason: "home_node_not_in_reachable_roster",
|
|
715
|
+
guidance: `The counterparty's home node (${owningNodeId}) is not in the set of directory nodes this daemon ` +
|
|
716
|
+
"could reach. Most often that means our own probe of it did not answer, not that it has left the " +
|
|
717
|
+
"consortium. Run cello_status: if it appears under directory_endpoints_unresolved, the problem is the " +
|
|
718
|
+
"path from here to it (attempts:1 there means it answered and the answer was unusable; 2 or more means " +
|
|
719
|
+
"it never answered). Only if it is absent from the manifest altogether has it actually left." +
|
|
720
|
+
rosterShortfallNote(),
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
logger.info("session.crossnode.initiated", { agentName, brokerNode: owningNodeId, correlationId });
|
|
724
|
+
const visiting = openVisitingConnection(agentName, agentKeyProvider, agentPubkeyHex, { peerId: target.peerId, multiaddr: target.multiaddr }, correlationId, owningNodeId);
|
|
725
|
+
// Track the release reason across the finally (result is block-scoped in the try).
|
|
726
|
+
let releaseReason = "failure";
|
|
727
|
+
try {
|
|
728
|
+
if (!(await waitForSignalingConnected(visiting.mgr, SIGNALING_CONNECT_WAIT_MS))) {
|
|
729
|
+
// The node RESOLVED but the visiting dial/auth didn't complete in time — a TRANSIENT
|
|
730
|
+
// cross-region condition (latency, blip, cold handshake), NOT a manifest miss. Distinct
|
|
731
|
+
// reason, and the caller's retry loop treats it as retryable (re-discover → retry).
|
|
732
|
+
logger.warn("session.crossnode.failed", { agentName, brokerNode: owningNodeId, reason: "visiting_connection_unreachable", correlationId });
|
|
733
|
+
return { ok: false, reason: "visiting_connection_unreachable", guidance: `Could not establish a visiting connection to the counterparty's home node (${owningNodeId}) within 10s. Retry.` };
|
|
734
|
+
}
|
|
735
|
+
const result = await runSessionRequestOverSignaling(visiting.mgr, targetHex, sr, correlationId, { agentName, ownPubkeyHex: agentPubkeyHex }, signalFilter, highStakes);
|
|
736
|
+
if (result.ok) {
|
|
737
|
+
releaseReason = "handoff-complete";
|
|
738
|
+
// Fix #1: remember the broker for this session so cello_close_session can reconnect to complete the seal.
|
|
739
|
+
const brokerSessionIdHex = Buffer.from(result.assignment.session_id).toString("hex");
|
|
740
|
+
crossNodeBrokerBySession.set(`${agentName}:${brokerSessionIdHex}`, owningNodeId);
|
|
741
|
+
logger.info("session.crossnode.established", { agentName, brokerNode: owningNodeId, correlationId });
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
logger.warn("session.crossnode.failed", { agentName, brokerNode: owningNodeId, reason: result.reason, correlationId });
|
|
745
|
+
}
|
|
746
|
+
return result;
|
|
747
|
+
}
|
|
748
|
+
finally {
|
|
749
|
+
await visiting.stop(releaseReason);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const resolvedSessionNegotiator = sessionNegotiator ?? {
|
|
753
|
+
negotiate: async (ctx) => {
|
|
754
|
+
const kp = getKeyProvider(ctx.agentName);
|
|
755
|
+
const agentRec = loadedAgents.find((a) => a.name === ctx.agentName);
|
|
756
|
+
if (!kp || !agentRec) {
|
|
757
|
+
return { ok: false, reason: "agent_not_found", guidance: `Agent '${ctx.agentName}' is not loaded on this daemon.` };
|
|
758
|
+
}
|
|
759
|
+
if (negotiationInProgress.has(ctx.agentName)) {
|
|
760
|
+
return {
|
|
761
|
+
ok: false,
|
|
762
|
+
reason: "session_negotiation_in_progress",
|
|
763
|
+
guidance: `Another session initiation is already in progress for agent '${ctx.agentName}'. Wait for it to finish, then retry.`,
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
const targetHex = typeof ctx.params["target_pubkey"] === "string"
|
|
767
|
+
? ctx.params["target_pubkey"]
|
|
768
|
+
: typeof ctx.params["counterparty_pubkey"] === "string"
|
|
769
|
+
? ctx.params["counterparty_pubkey"]
|
|
770
|
+
: "";
|
|
771
|
+
if (!/^[0-9a-fA-F]{64}$/.test(targetHex)) {
|
|
772
|
+
return {
|
|
773
|
+
ok: false,
|
|
774
|
+
reason: "invalid_target_pubkey",
|
|
775
|
+
guidance: "cello_initiate_session requires 'target_pubkey' as the counterparty's 32-byte hex K_local public key.",
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
// include_signals / exclude_signals: validated against the wallet below and passed to
|
|
779
|
+
// runSessionRequestOverSignaling so the presentation layer applies them.
|
|
780
|
+
const includeRaw = ctx.params["include_signals"];
|
|
781
|
+
const excludeRaw = ctx.params["exclude_signals"];
|
|
782
|
+
const includeTypes = Array.isArray(includeRaw) ? includeRaw.filter((s) => typeof s === "string") : undefined;
|
|
783
|
+
const excludeTypes = Array.isArray(excludeRaw) ? excludeRaw.filter((s) => typeof s === "string") : undefined;
|
|
784
|
+
// Validate include/exclude type strings against what THIS agent can actually present.
|
|
785
|
+
//
|
|
786
|
+
// Not against the whole wallet: the wallet is daemon-wide, so `listAllWalletSignals()` spans
|
|
787
|
+
// every agent on the device. Validating against it means Alice's `--include track_record`
|
|
788
|
+
// passes because BOB holds one, and she then presents nothing with no error and no guidance —
|
|
789
|
+
// the daemon telling her the type is fine and then silently omitting it. Before presentation
|
|
790
|
+
// was scoped that filter would have presented Bob's signal; scoping turned a leak into a lie,
|
|
791
|
+
// and this turns the lie back into the `unknown_signal_type` message that already exists.
|
|
792
|
+
// (It also stops the validator being a cross-agent enumeration oracle.)
|
|
793
|
+
if (includeTypes || excludeTypes) {
|
|
794
|
+
const store = new TrustSignalStore(sessionNodeManager.getDb(), logger);
|
|
795
|
+
const presenter = loadedAgents.find((a) => a.name === ctx.agentName);
|
|
796
|
+
if (!presenter) {
|
|
797
|
+
return {
|
|
798
|
+
ok: false,
|
|
799
|
+
reason: "agent_not_found",
|
|
800
|
+
guidance: `Agent '${ctx.agentName}' is not loaded on this daemon.`,
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
const known = new Set(store.listPresentableTypes(presenter.pubkey));
|
|
804
|
+
const check = includeTypes ?? excludeTypes ?? [];
|
|
805
|
+
const unknown = check.filter((t) => !known.has(t));
|
|
806
|
+
if (unknown.length > 0) {
|
|
807
|
+
return {
|
|
808
|
+
ok: false,
|
|
809
|
+
reason: "unknown_signal_type",
|
|
810
|
+
guidance: `Signal type(s) not found in wallet: ${unknown.map((t) => `'${t}'`).join(", ")}. See 'cello trust-signals list' for valid types. Use --include type1,type2 or --exclude type1,type2.`,
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const signalFilter = includeTypes || excludeTypes ? { include: includeTypes, exclude: excludeTypes } : undefined;
|
|
815
|
+
/**
|
|
816
|
+
* `DOD-M15-UNILATERAL-1`: the HIGH-STAKES seal tier, opted in per conversation.
|
|
817
|
+
*
|
|
818
|
+
* Strict `=== true` at every hop, including this one. The tier can WITHHOLD a receipt when
|
|
819
|
+
* the counterparty's absence is unproven, so a truthy-but-not-true value must never reach it —
|
|
820
|
+
* the operator has to have actually asked.
|
|
821
|
+
*/
|
|
822
|
+
const highStakes = ctx.params["high_stakes"] === true;
|
|
823
|
+
const sr = sessionNodeManager.getStandingReceiverInfo(ctx.agentName);
|
|
824
|
+
if (!sr) {
|
|
825
|
+
return {
|
|
826
|
+
ok: false,
|
|
827
|
+
reason: "standing_receiver_unavailable",
|
|
828
|
+
guidance: "The standing receiver is not ready, so no initiator session endpoint can be advertised. Retry once the daemon has finished starting.",
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
// DOD-M15-RELAYONLY-1: REFUSE LOUDLY rather than publish an empty address list.
|
|
832
|
+
//
|
|
833
|
+
// ⚠️ Under relay-only, `getStandingReceiverInfo` has already filtered to circuit addresses. If
|
|
834
|
+
// the relay granted no reservation — the `plain` fallback receiver, built whenever the relay
|
|
835
|
+
// rejects or times out — that set is EMPTY, and publishing an empty list is a MALFORMED frame:
|
|
836
|
+
// the directory rejects it and the operator is told **"Ensure the counterparty is registered
|
|
837
|
+
// and online"**, naming someone else's state for a fault that is a setting plus a missing
|
|
838
|
+
// reservation on THIS machine. Saying so here is the difference between a diagnosable refusal
|
|
839
|
+
// and a wild goose chase.
|
|
840
|
+
if (sr.addrs.length === 0) {
|
|
841
|
+
return {
|
|
842
|
+
ok: false,
|
|
843
|
+
reason: "relay_only_no_reservation",
|
|
844
|
+
guidance: "Relay-only routing is on for this agent, but it holds no relay reservation yet, so it " +
|
|
845
|
+
"has no circuit address to advertise and cannot be reached without revealing this node's " +
|
|
846
|
+
"own address. Retry once the standing receiver has a relay reservation, or turn " +
|
|
847
|
+
"transport.relay_only off with cello_settings_set if you accept direct connections.",
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
const { signaling } = getAgentSignaling(ctx.agentName, kp, agentRec.pubkey);
|
|
851
|
+
if (!(await waitForSignalingConnected(signaling, SIGNALING_CONNECT_WAIT_MS))) {
|
|
852
|
+
return {
|
|
853
|
+
ok: false,
|
|
854
|
+
reason: "directory_signaling_timeout",
|
|
855
|
+
guidance: `Agent '${ctx.agentName}' could not establish its directory signaling stream within 10s. Check CELLO_DIRECTORY_URL and that the directory is reachable, then retry.`,
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
// Single-flight claimed → the discover-first flow (one discovery + up to 3 session_request
|
|
859
|
+
// attempts, possibly over a transient visiting connection) runs under ONE slot; released in
|
|
860
|
+
// finally. A single directory-side echoed request id would allow true concurrency later.
|
|
861
|
+
negotiationInProgress.add(ctx.agentName);
|
|
862
|
+
try {
|
|
863
|
+
// DISCOVER FIRST. Because identity + presence are fully replicated, the agent's OWN home
|
|
864
|
+
// stream answers "where is the target?" authoritatively-enough (advisory — the target node's
|
|
865
|
+
// live #streams check stays the authority; a stale answer just triggers the retry below).
|
|
866
|
+
const homeNodeId = signaling.currentDirectoryNodeId;
|
|
867
|
+
const backoffs = [1_000, 3_000]; // between attempts — covers re-home + replication lag
|
|
868
|
+
const MAX_ATTEMPTS = 3;
|
|
869
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
870
|
+
const disc = await runDiscoveryLookup(signaling, targetHex, 5_000, ctx.correlationId);
|
|
871
|
+
// TRANSPORT: the home stream is down — retry (it may reconnect); local-only fallback would
|
|
872
|
+
// fail the same way. Exhausted → the TRUTHFUL transport reason, never a false "offline".
|
|
873
|
+
if (disc.kind === "send_failed") {
|
|
874
|
+
logger.warn("session.discovery.send_failed", { agentName: ctx.agentName, attempt, reason: disc.reason, correlationId: ctx.correlationId });
|
|
875
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
876
|
+
await sleepMs(backoffs[attempt - 1]);
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
return { ok: false, reason: "directory_unreachable", guidance: "The home directory stream is not connected, so the counterparty's location could not be looked up. Check cello status (directory_signaling), then retry." + rosterShortfallNote() };
|
|
880
|
+
}
|
|
881
|
+
// NO REPLY: an old directory (predates discovery) OR a slow/dropped reply on a new one. Retry;
|
|
882
|
+
// fall back to local-only behavior ONLY as a last resort (after the retries), so that a
|
|
883
|
+
// single dropped reply does not misroute a reachable cross-node peer to the home node.
|
|
884
|
+
if (disc.kind === "timeout") {
|
|
885
|
+
logger.warn("session.discovery.no_reply", { agentName: ctx.agentName, attempt, correlationId: ctx.correlationId });
|
|
886
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
887
|
+
await sleepMs(backoffs[attempt - 1]);
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
logger.info("session.discovery.unsupported_fallback", { agentName: ctx.agentName, correlationId: ctx.correlationId });
|
|
891
|
+
return await runSessionRequestOverSignaling(signaling, targetHex, sr, ctx.correlationId, { agentName: ctx.agentName, ownPubkeyHex: agentRec.pubkey }, signalFilter, highStakes);
|
|
892
|
+
}
|
|
893
|
+
// DIRECTORY-SIDE lookup fault (DB error / malformed reply): RETRYABLE — but a DIRECTORY fault,
|
|
894
|
+
// reported truthfully as directory_unreachable, NEVER as the counterparty being offline.
|
|
895
|
+
if (disc.kind === "error" || disc.kind === "malformed") {
|
|
896
|
+
logger.info("directory.discovery.lookup.retry", { agentName: ctx.agentName, attempt, kind: disc.kind, correlationId: ctx.correlationId });
|
|
897
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
898
|
+
await sleepMs(backoffs[attempt - 1]);
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
return { ok: false, reason: "directory_unreachable", guidance: "The directory could not resolve the counterparty's location (a directory-side lookup error) after several attempts. Retry shortly." + rosterShortfallNote() };
|
|
902
|
+
}
|
|
903
|
+
// A 3-state RESULT. Route it (pure classifier).
|
|
904
|
+
const action = classifyOnlineResult(disc.state, disc.owningNodeIds, homeNodeId);
|
|
905
|
+
// State 3: no such agent — a bad address, not a transient outage. NO retry (distinct code).
|
|
906
|
+
if (action.kind === "unknown_agent") {
|
|
907
|
+
return { ok: false, reason: "unknown_agent", guidance: "No agent is registered under that public key — the counterparty address is unknown. Verify the pubkey with the counterparty's operator." };
|
|
908
|
+
}
|
|
909
|
+
// State 2: known but offline. Definitive — NO retry storm.
|
|
910
|
+
if (action.kind === "offline") {
|
|
911
|
+
return { ok: false, reason: "counterparty_offline", guidance: "The counterparty exists but is not currently online. Have its operator bring it online (cello_status), then retry." };
|
|
912
|
+
}
|
|
913
|
+
// Online but no owner named — transient, re-discover.
|
|
914
|
+
if (action.kind === "retry") {
|
|
915
|
+
logger.info("directory.discovery.lookup.retry", { agentName: ctx.agentName, attempt, kind: "online_no_owner", homeNodeId, correlationId: ctx.correlationId });
|
|
916
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
917
|
+
await sleepMs(backoffs[attempt - 1]);
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* DOD-M15-ERRSTRING-1 — NAME WHAT WAS OBSERVED. This returned `counterparty_offline`,
|
|
922
|
+
* and its own guidance said the opposite in the next sentence: *the directory reported
|
|
923
|
+
* the counterparty ONLINE*. The counterparty is not the broken thing here; the
|
|
924
|
+
* directory answered "online" and then named nobody who could serve them.
|
|
925
|
+
*
|
|
926
|
+
* That mismatch is the whole defect this line exists to close. An operator told
|
|
927
|
+
* "counterparty offline" goes and asks their counterparty to check — the one place the
|
|
928
|
+
* fault is not.
|
|
929
|
+
*/
|
|
930
|
+
return {
|
|
931
|
+
ok: false,
|
|
932
|
+
reason: "directory_named_no_home",
|
|
933
|
+
guidance:
|
|
934
|
+
// LOW-1: `homeNodeId` is `string | null`, and this branch fires whenever the
|
|
935
|
+
// directory names no owner — including before signaling has learned its node id.
|
|
936
|
+
// "(null)" in an operator-facing sentence is worse than saying we do not know.
|
|
937
|
+
`The directory node you are connected to (${homeNodeId ?? "id not yet known"}) says this counterparty is online but names ` +
|
|
938
|
+
"no node that holds them, three times running. That is a directory-side inconsistency, not the " +
|
|
939
|
+
"counterparty being offline — there is nothing for them to fix on their side. Retry in a minute; " +
|
|
940
|
+
"if it repeats, that node's presence data is out of sync and cello_status will show which other " +
|
|
941
|
+
"directories are available." +
|
|
942
|
+
rosterShortfallNote(),
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
const result = action.kind === "same_node"
|
|
946
|
+
// SAME-NODE: the target is on the node we're already connected to. The existing path runs
|
|
947
|
+
// unchanged — ZERO visiting connections, ZERO new frames beyond the one discovery_lookup.
|
|
948
|
+
? await runSessionRequestOverSignaling(signaling, targetHex, sr, ctx.correlationId, { agentName: ctx.agentName, ownPubkeyHex: agentRec.pubkey }, signalFilter, highStakes)
|
|
949
|
+
// CROSS-NODE: reach into the target's home over a transient visiting connection.
|
|
950
|
+
: await runCrossNodeSetup(ctx.agentName, kp, agentRec.pubkey, action.owningNodeId, targetHex, sr, ctx.correlationId, signalFilter, highStakes);
|
|
951
|
+
// RETRY triggers: (a) the broker reported target_offline (stale replicated presence or a
|
|
952
|
+
// re-home between discovery and the request); (b) a transient visiting-connection failure
|
|
953
|
+
// (cross-region blip). Both re-discover → retry, bounded.
|
|
954
|
+
if (!result.ok && (result.reason === "target_offline" || result.reason === "visiting_connection_unreachable")) {
|
|
955
|
+
logger.info("session.crossnode.stale_discovery_retry", { agentName: ctx.agentName, attempt, reason: result.reason, correlationId: ctx.correlationId });
|
|
956
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
957
|
+
await sleepMs(backoffs[attempt - 1]);
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* DOD-M15-ERRSTRING-1 (review F2) — KEEP THE UPSTREAM OBSERVATION. This branch used to
|
|
962
|
+
* rewrite `target_offline` into `counterparty_offline`, and that rewrite is the most
|
|
963
|
+
* likely source of the 2026-08-16 incident.
|
|
964
|
+
*
|
|
965
|
+
* What the home node actually reported is `targetStreamFound === false`: it has no live
|
|
966
|
+
* stream registered for that agent. At least four different conditions produce it, and
|
|
967
|
+
* only one of them is "the counterparty is offline":
|
|
968
|
+
* - their standing receiver's REGISTRATION lapsed on a signaling reconnect — this
|
|
969
|
+
* daemon's own notes record 46 of 48 reconnects in one hour leaving every agent
|
|
970
|
+
* unregistered, while `cello status`, `agent.online` and the directory's own
|
|
971
|
+
* presence table all still showed them healthy;
|
|
972
|
+
* - the node garbage-collected the stream;
|
|
973
|
+
* - the agent is re-homing between discovery and the request;
|
|
974
|
+
* - they really are offline.
|
|
975
|
+
*
|
|
976
|
+
* Collapsing all four into "counterparty_offline" tells the operator to go and ask a
|
|
977
|
+
* person whose side is working, about a registration on OUR side of a stream on the
|
|
978
|
+
* DIRECTORY's side. Invariant 3 prescribes the fix literally: do not overwrite the
|
|
979
|
+
* upstream descriptive error.
|
|
980
|
+
*/
|
|
981
|
+
return result.reason === "visiting_connection_unreachable"
|
|
982
|
+
? { ok: false, reason: "visiting_connection_unreachable", guidance: "The counterparty's home node was reachable at discovery but its visiting connection could not be established after several attempts. Retry shortly." + rosterShortfallNote() }
|
|
983
|
+
: {
|
|
984
|
+
ok: false,
|
|
985
|
+
reason: "home_node_reports_no_receiver",
|
|
986
|
+
guidance: `The counterparty's home node (${action.kind === "cross_node" ? action.owningNodeId : homeNodeId}) reports no live receiver registered for them, ` +
|
|
987
|
+
"after several attempts. This is NOT the same as them being offline, and it is most often not their fault: " +
|
|
988
|
+
"the commonest cause is their receiver's registration lapsing on a signaling reconnect, which leaves their " +
|
|
989
|
+
"own cello_status showing perfectly healthy. Retry in a minute — a re-registration usually clears it. " +
|
|
990
|
+
"If it persists, have them restart their agent so it re-registers; only if that fails is their daemon actually down." +
|
|
991
|
+
rosterShortfallNote(),
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
return result;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* DOD-M15-ERRSTRING-1 — UNREACHABLE BACKSTOP, and it is worth saying so precisely because
|
|
998
|
+
* the previous value made it look like a live path.
|
|
999
|
+
*
|
|
1000
|
+
* Every branch in the loop above either `continue`s (only while `attempt < MAX_ATTEMPTS`) or
|
|
1001
|
+
* returns, and the body ends in `return result`. So the loop cannot complete normally; this
|
|
1002
|
+
* exists because the compiler cannot see that. It was checked by trace, not assumed — an
|
|
1003
|
+
* earlier draft of this comment claimed this line was the catch-all behind the 2026-08-16
|
|
1004
|
+
* incident, and it is not: the reachable false-offline was the no-home branch above.
|
|
1005
|
+
*
|
|
1006
|
+
* It still must not say `counterparty_offline`. A backstop that fires is by definition a
|
|
1007
|
+
* case nobody predicted, which is the worst possible moment to assert a specific party is
|
|
1008
|
+
* at fault — and if this ever DOES appear in a log, "the cause was not one this daemon can
|
|
1009
|
+
* name" is exactly the truth the reader needs.
|
|
1010
|
+
*/
|
|
1011
|
+
return {
|
|
1012
|
+
ok: false,
|
|
1013
|
+
reason: "session_setup_exhausted",
|
|
1014
|
+
guidance: "Three attempts to set up the session all failed, and the cause was not one this daemon can name — " +
|
|
1015
|
+
"so it is deliberately not guessing that the counterparty is offline. Retry in a minute. If it repeats: " +
|
|
1016
|
+
"run cello_status to check your own directory connection first (a node below threshold or mid-restart " +
|
|
1017
|
+
"presents exactly like this), and only then ask the counterparty to check theirs." +
|
|
1018
|
+
rosterShortfallNote(),
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
finally {
|
|
1022
|
+
negotiationInProgress.delete(ctx.agentName);
|
|
1023
|
+
}
|
|
1024
|
+
},
|
|
1025
|
+
};
|
|
1026
|
+
// DAEMON-003: Initialize RetryQueue and NonceDedupStore (AC-008).
|
|
1027
|
+
return {
|
|
1028
|
+
openVisitingConnection,
|
|
1029
|
+
crossNodeBrokerBySession,
|
|
1030
|
+
resolvedSessionNegotiator,
|
|
1031
|
+
// M14 / DOD-DOC-DELIVERY-2: exposed for the document delivery worker, the first NON-HANDLER
|
|
1032
|
+
// consumer of this module. It needs the same 3-state discovery answer `cello_initiate_session`
|
|
1033
|
+
// uses — online / offline / unknown_agent, kept distinct from "the lookup itself failed" — and
|
|
1034
|
+
// a second implementation of that distinction is how the two drift until one starts reporting a
|
|
1035
|
+
// directory outage as the peer being offline.
|
|
1036
|
+
runDiscoveryLookup,
|
|
1037
|
+
/**
|
|
1038
|
+
* 048-SWEEPTICK: the ONLY sweep that escapes this module, and deliberately so. It sweeps
|
|
1039
|
+
* immediately on connect — the 043 C2 behaviour, unchanged — and arms the five-minute tick that
|
|
1040
|
+
* covers a connection which never drops.
|
|
1041
|
+
*
|
|
1042
|
+
* The bare `sweepTrustSignals` used to be exported beside this "for anything that wants one
|
|
1043
|
+
* collection without arming a timer". Nothing wanted one, and its only live effect was to keep
|
|
1044
|
+
* the exact revert this unit exists to prevent — wiring the un-ticked sweep into
|
|
1045
|
+
* `getSweepTrustSignals` — a one-word edit that a source-text guard then had to defend. Removing
|
|
1046
|
+
* it makes that revert impossible to write rather than checked for.
|
|
1047
|
+
*/
|
|
1048
|
+
sweepTrustSignalsAndTick: trustSignalSweepTicker.sweepAndTick,
|
|
1049
|
+
trustSignalSweepTicker,
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
//# sourceMappingURL=outbound-sessions.js.map
|