@cello-protocol/daemon 0.0.17 → 0.0.19

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.
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAQrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAoB7G,OAAO,KAAK,EAAE,kBAAkB,EAA+C,MAAM,yBAAyB,CAAC;AAM/G,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAqInF,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;;;;;OAQG;IACH,gBAAgB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;CACtC;AAyCD,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAyzH7E"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAQrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAqB7G,OAAO,KAAK,EAAE,kBAAkB,EAA+C,MAAM,yBAAyB,CAAC;AAM/G,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAqInF,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;;;;;OAQG;IACH,gBAAgB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;CACtC;AAyCD,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAg6H7E"}
package/dist/daemon.js CHANGED
@@ -51,7 +51,7 @@ import { MAX_CONTENT_BYTES, computeGenesisPrevRoot, buildAgentRevocationTbs } fr
51
51
  import { resolveCelloEnv, createTransportSelector, isProductionVariant, } from "./transport-composition.js";
52
52
  import { selectAdvertisedAddress } from "./transport-selector.js";
53
53
  import { parseSessionAssignment, sessionRequestErrorReason } from "./session-assignment-parser.js";
54
- import { wireSessionCeremonyHandler, wireSessionOfferHandler, wireSealCeremonyHandler, verifyUnilateralCertificate, verifyBilateralSealCertificate } from "./session-ceremony.js";
54
+ import { wireSessionCeremonyHandler, wireSessionOfferHandler, wireSealCeremonyHandler, verifyUnilateralCertificate, verifyBilateralSealCertificate, runAgentRefresh } from "./session-ceremony.js";
55
55
  import { reDeriveFrontiers, findInflatedFrontier } from "./seal-frontier-verify.js";
56
56
  import { LocalAutoNatStub } from "@cello-protocol/transport";
57
57
  import { startHttpManifestPoll } from "./http-manifest-poll.js";
@@ -387,12 +387,42 @@ export async function startDaemon(config) {
387
387
  if (!kp || !endpoint || !endpoint.peer_id || !endpoint.multiaddrs || endpoint.multiaddrs.length === 0) {
388
388
  return undefined;
389
389
  }
390
+ // FED-OPTIONB-SETUP-001 (Option B): when the directory included the per-node relay-assignment
391
+ // signature, carry the assignment so the client presents it to its chosen relay (replacing the
392
+ // directory→relay dial). Built only for relay-mode assignments that carry relay_directory_signature;
393
+ // absent ⇒ the client skips client_record_assignment (direct/legacy/pre-M8B).
394
+ const relayDirSig = assignment.relay_directory_signature;
395
+ // FED-OPTIONB-SETUP-001 (fallback-finder #1/#5): a relay-mode assignment MUST carry a
396
+ // relay_directory_signature — the directory always signs one. If it is absent or malformed (the
397
+ // parser dropped it to undefined) for a relay-mode session, the session silently degrades to "no
398
+ // relay witness" and looks indistinguishable from a legitimate direct-mode session. Warn LOUD so the
399
+ // missing/corrupt witness has a named cause (this is the only diagnosable signal on a PURE RECEIVER,
400
+ // which never submits and so never surfaces relay_unavailable). Unwitnessed is an allowed sovereign-
401
+ // redundancy state, but it must not be invisible.
402
+ if (assignment.transport_mode === "relay" && !relayDirSig) {
403
+ logger.warn("session.relay.assignment.signature.missing", {
404
+ agentName,
405
+ sessionId: Buffer.from(assignment.session_id).toString("hex").slice(0, 16),
406
+ reason: "relay_mode_assignment_without_directory_signature",
407
+ });
408
+ }
409
+ const carry = relayDirSig
410
+ ? {
411
+ participantA: assignment.participant_a.pubkey,
412
+ participantB: assignment.participant_b.pubkey,
413
+ sessionTimestamp: assignment.session_timestamp,
414
+ initiatorSessionPeerId: assignment.initiator_session_peer_id,
415
+ counterpartySessionPeerId: assignment.counterparty_session_peer_id,
416
+ assignmentSignature: relayDirSig,
417
+ }
418
+ : undefined;
390
419
  return {
391
420
  relayPeerId: endpoint.peer_id,
392
421
  relayAddrs: endpoint.multiaddrs,
393
422
  keyProvider: kp,
394
423
  senderPubkey: await kp.getPublicKey(),
395
424
  sessionIdBytes: assignment.session_id,
425
+ assignment: carry,
396
426
  };
397
427
  };
398
428
  // Stub: all connections marked as 'unverified' until connection validation is wired
@@ -1776,6 +1806,60 @@ export async function startDaemon(config) {
1776
1806
  void attemptSealUpgrade(signaling, agentName, agentPubkeyHex, sidHex, frame);
1777
1807
  });
1778
1808
  }
1809
+ // ─── M8B DOD-REFRESH-1: cello_refresh_shares — proactive share refresh / epoch rollover ───
1810
+ handlers.set("cello_refresh_shares", async (params, connectionId) => {
1811
+ const connState = perConnectionState.get(connectionId);
1812
+ const agentName = params?.name ?? connState?.currentAgent;
1813
+ if (!agentName) {
1814
+ return { ok: false, reason: "no_current_agent", guidance: "Select an agent with cello_use_agent, or pass { name }." };
1815
+ }
1816
+ const loaded = loadedAgents.find((a) => a.name === agentName);
1817
+ if (!loaded) {
1818
+ return { ok: false, reason: "agent_not_found", guidance: `No agent named '${agentName}'. Create + register it first.` };
1819
+ }
1820
+ // The refresh ceremony reaches the consortium over the agent's signaling node — ensure it is up.
1821
+ const entry = getAgentSignaling(agentName, loaded.keyProvider, loaded.pubkey);
1822
+ const connected = await waitForSignalingConnected(entry.signaling, 15_000);
1823
+ if (!connected) {
1824
+ return { ok: false, reason: "directory_unreachable", guidance: "The agent's directory signaling is not connected; start the agent and retry." };
1825
+ }
1826
+ const result = await runAgentRefresh({
1827
+ agentName,
1828
+ persistence: getPersistence(agentName),
1829
+ agentPubkeyHex: loaded.pubkey,
1830
+ getNode: entry.getNode,
1831
+ getDirectoryEndpoint: async () => (directoryEndpointResolver ? (await directoryEndpointResolver()) ?? null : null),
1832
+ getConsortiumEndpoints: resolveConsortiumRoster,
1833
+ signaling: entry.signaling,
1834
+ logger,
1835
+ });
1836
+ if (!result.ok) {
1837
+ return { ok: false, reason: result.reason, guidance: "Share refresh did not complete — see the daemon log (refresh.ceremony.*) for the cause." };
1838
+ }
1839
+ return { ok: true, epoch: result.toEpochN, primary_pubkey: result.primaryPubkey, verifying_shares_digest: result.verifyingSharesDigest };
1840
+ });
1841
+ // ─── M8B DOD-RELAYSIG-1: cello_get_relay_receipts — the agent's stored relay ordering receipts ───
1842
+ handlers.set("cello_get_relay_receipts", async (params, connectionId) => {
1843
+ const connState = perConnectionState.get(connectionId);
1844
+ const agentName = params?.name ?? connState?.currentAgent;
1845
+ if (!agentName) {
1846
+ return { ok: false, reason: "no_current_agent", guidance: "Select an agent with cello_use_agent, or pass { name }." };
1847
+ }
1848
+ const loaded = loadedAgents.find((a) => a.name === agentName);
1849
+ if (!loaded) {
1850
+ return { ok: false, reason: "agent_not_found", guidance: `No agent named '${agentName}'.` };
1851
+ }
1852
+ const sessionIdHex = typeof params?.session_id === "string" ? params.session_id : undefined;
1853
+ const receipts = sessionNodeManager.getRelayReceipts(loaded.pubkey, sessionIdHex).map((r) => ({
1854
+ hash_hex: r.hashHex,
1855
+ session_id: r.sessionIdHex,
1856
+ relay_id: r.relayId,
1857
+ sequence_number: r.sequenceNumber,
1858
+ timestamp: r.timestamp,
1859
+ signature_hex: r.signatureHex,
1860
+ }));
1861
+ return { ok: true, receipts };
1862
+ });
1779
1863
  // ─── MCP-001: cello_status (per-connection perspective) ───
1780
1864
  handlers.set("cello_status", async (_params, connectionId) => {
1781
1865
  return {
@@ -2082,11 +2166,28 @@ export async function startDaemon(config) {
2082
2166
  let resolveUni;
2083
2167
  const uniP = new Promise((r) => { resolveUni = r; });
2084
2168
  pendingUnilateralWaiters.set(sessionId, resolveUni);
2169
+ // FED-OPTIONB-SEAL-001 (Option B): carry the full leaf chain (both parties) + the relay receipts so
2170
+ // the directory rebuilds + verifies the tree OFFLINE — no directory→relay getSealLeaves dial. The
2171
+ // store is keyed by the agent's K_local pubkey (the same key the relay client recorded under).
2172
+ const sealAgentKp = keyProviders.get(record.agent_name);
2173
+ const sealAgentPubkeyHex = sealAgentKp ? Buffer.from(await sealAgentKp.getPublicKey()).toString("hex") : "";
2174
+ const sealCarry = sealAgentPubkeyHex ? sessionNodeManager.getSealCarry(sealAgentPubkeyHex, sessionId) : [];
2175
+ const seal_leaves = sealCarry.map((l) => ({
2176
+ sequence_number: l.sequenceNumber,
2177
+ leaf_kind: l.leafKind,
2178
+ structure2_cbor: l.structure2Cbor,
2179
+ structure1_cbor: l.structure1Cbor,
2180
+ // Relay receipt (present only for the present party's OWN leaves — the seq-pinning teeth).
2181
+ relay_id: l.relayId,
2182
+ relay_timestamp: l.relayTimestamp,
2183
+ relay_signature: l.relaySignatureHex ? new Uint8Array(Buffer.from(l.relaySignatureHex, "hex")) : undefined,
2184
+ }));
2085
2185
  const sent = await sendOver(record.agent_name, {
2086
2186
  type: "seal_unilateral",
2087
2187
  session_id: new Uint8Array(Buffer.from(sessionId, "hex")),
2088
2188
  reported_root: new Uint8Array(Buffer.from(submit.reportedRootHex, "hex")),
2089
2189
  reported_seq: submit.sequenceNumber,
2190
+ seal_leaves,
2090
2191
  });
2091
2192
  if (!sent.ok) {
2092
2193
  pendingUnilateralWaiters.delete(sessionId);