@ouro.bot/cli 0.1.0-alpha.816 → 0.1.0-alpha.817

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.
Files changed (49) hide show
  1. package/assets/sanctuary-host-launcher.sh +16 -0
  2. package/changelog.json +10 -0
  3. package/deploy/unraid/Dockerfile +6 -0
  4. package/deploy/unraid/README.txt +280 -60
  5. package/deploy/unraid/docker-man-template-transaction.mjs +192 -7
  6. package/deploy/unraid/sanctuary-acceptance-adapter.sh +1 -1
  7. package/deploy/unraid/sanctuary-acceptance-contract.json +7 -7
  8. package/deploy/unraid/sanctuary-authority-installation.json +36 -0
  9. package/deploy/unraid/sanctuary-authority-service.sh +30 -0
  10. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +62 -9
  11. package/deploy/unraid/sanctuary-unit16-run.sh +15 -15
  12. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
  13. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +2 -2
  14. package/deploy/unraid/sanctuary.xml +2 -1
  15. package/dist/heart/core.js +2 -1
  16. package/dist/heart/daemon/container-spec-auditor-main.js +8 -7
  17. package/dist/heart/daemon/container-spec-auditor.js +16 -9
  18. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +95 -91
  19. package/dist/heart/daemon/sanctuary-acceptance-harness.js +95 -169
  20. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +4 -5
  21. package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
  22. package/dist/heart/daemon/sanctuary-authority-epoch.js +256 -0
  23. package/dist/heart/daemon/sanctuary-authority-installation.js +140 -0
  24. package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
  25. package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +780 -0
  26. package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
  27. package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
  28. package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +494 -0
  29. package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
  30. package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
  31. package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
  32. package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
  33. package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +445 -0
  34. package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
  35. package/dist/heart/daemon/sanctuary-telegram-authority-service.js +615 -0
  36. package/dist/heart/daemon/sense-manager.js +20 -10
  37. package/dist/repertoire/tools-sanctuary-host.js +202 -0
  38. package/dist/repertoire/tools.js +15 -6
  39. package/dist/senses/root-host-approval-port.js +345 -0
  40. package/dist/senses/root-host-approval-runtime.js +393 -0
  41. package/dist/senses/sanctuary-authority-resident.js +102 -0
  42. package/dist/senses/telegram-admission.js +7 -0
  43. package/dist/senses/telegram-attachments.js +5 -1
  44. package/dist/senses/telegram-authority-transport.js +231 -0
  45. package/dist/senses/telegram-client.js +31 -8
  46. package/dist/senses/telegram-entry.js +1 -1
  47. package/dist/senses/telegram.js +168 -28
  48. package/npm-shrinkwrap.json +2 -2
  49. package/package.json +1 -1
@@ -64,8 +64,8 @@ const node_util_1 = require("node:util");
64
64
  const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
65
65
  const friends_1 = require("@ouro.bot/friends");
66
66
  const runtime_1 = require("../../nerves/runtime");
67
+ const sanctuary_authority_resident_1 = require("../../senses/sanctuary-authority-resident");
67
68
  const telegram_approval_runtime_1 = require("../../senses/telegram-approval-runtime");
68
- const telegram_client_1 = require("../../senses/telegram-client");
69
69
  const telegram_effect_adapter_1 = require("../../senses/telegram-effect-adapter");
70
70
  const telegram_admission_1 = require("../../senses/telegram-admission");
71
71
  const sanctuary_interactive_control_1 = require("../../senses/sanctuary-interactive-control");
@@ -105,7 +105,6 @@ const ADAPTER_TIMEOUT_MS = 240_000;
105
105
  const NETWORK_TIMEOUT_MS = 10_000;
106
106
  const KEY_DIRECTORY = "/boot/config/plugins/dynamix.my.servers/keys";
107
107
  const SELECTED_KEY_RECORD = "/run/ouro-acceptance/unraid-key.json";
108
- const TELEGRAM_OFFSET = "/home/ouro/AgentBundles/sanctuary.ouro/state/senses/telegram/offset.json";
109
108
  const CALLBACK_PLAYBACK_JOURNAL = "state/approvals/sanctuary-callback-playback.sqlite";
110
109
  const TELEGRAM_IDENTITY_KEY = "/home/ouro/AgentBundles/sanctuary.ouro/state/senses/telegram/identity.key";
111
110
  const TELEGRAM_AUDIT = `/home/ouro/AgentBundles/sanctuary.ouro/${telegram_audit_ledger_1.TELEGRAM_ACCEPTANCE_AUDIT_RELATIVE_PATH}`;
@@ -115,7 +114,6 @@ const CONTAINER_DIGEST_FILE = "/run/ouro-acceptance/container-digest";
115
114
  const PROCESS_BINDING_DIGEST_FILE = "/run/ouro-acceptance/process-binding-digest";
116
115
  const POSTBOOT_HEALTH_FILE = "/run/ouro-acceptance/postboot-health.json";
117
116
  const BOOT_ID_FILE = "/run/ouro-acceptance/boot-id";
118
- const TELEGRAM_POLLER_COUNT_FILE = "/run/ouro-acceptance/telegram-poller-count.json";
119
117
  const CONTRACT_FILE = "/opt/ouro/deploy/unraid/sanctuary-acceptance-contract.json";
120
118
  const CLOSED_INVENTORY_FILE = "/run/ouro-acceptance/closed-inventory.json";
121
119
  const HOST_BROKER_SOCKET = "/run/ouro-host-acceptance/adapter.sock";
@@ -236,11 +234,31 @@ async function defaultHostRequest(payload, socketPath, timeoutMs) {
236
234
  socket.end(`${JSON.stringify(payload)}\n`);
237
235
  });
238
236
  }
237
+ function acceptanceTelegramIdentity() {
238
+ const gateway = (0, sanctuary_authority_resident_1.openSanctuaryResidentAuthority)({}, {});
239
+ try {
240
+ return gateway.credentials;
241
+ }
242
+ finally {
243
+ gateway.authorityTransport.api.stop();
244
+ }
245
+ }
239
246
  function createSanctuaryAcceptanceAdapterDependencies(secretFd = 3, options = {}) {
240
247
  const keyDirectory = options.keyDirectory ?? KEY_DIRECTORY;
241
248
  const adapterTimeoutMs = options.adapterTimeoutMs ?? ADAPTER_TIMEOUT_MS;
242
249
  const hostBrokerSocket = options.hostBrokerSocket ?? HOST_BROKER_SOCKET;
243
250
  const dependencies = {
251
+ gateway: () => (0, sanctuary_authority_resident_1.openSanctuaryResidentAuthority)({}, {}),
252
+ gatewayCursor: async () => {
253
+ const gateway = (0, sanctuary_authority_resident_1.openSanctuaryResidentAuthority)({}, {});
254
+ try {
255
+ const snapshot = await gateway.cursorSnapshot();
256
+ return { nextUpdateId: snapshot.cursor, progressDigest: snapshot.progressDigest };
257
+ }
258
+ finally {
259
+ gateway.authorityTransport.api.stop();
260
+ }
261
+ },
244
262
  readKeyFiles: () => readKeyDirectory(keyDirectory),
245
263
  readKeyRecords: () => readKeyRecords(keyDirectory),
246
264
  readDescriptor: () => (0, node_fs_1.readFileSync)(secretFd, "utf8"),
@@ -255,8 +273,7 @@ function createSanctuaryAcceptanceAdapterDependencies(secretFd = 3, options = {}
255
273
  callbackPlaybackSnapshot: (coordinateDigest) => callbackPlaybackSnapshot((0, identity_1.getAgentRoot)(TARGET_ID), coordinateDigest),
256
274
  interactiveRuntime: executeSanctuaryInteractiveRuntimeOperation,
257
275
  hostRequest: options.hostRequest ?? ((payload) => defaultHostRequest(payload, hostBrokerSocket, adapterTimeoutMs)),
258
- telegramCredentials: () => (0, telegram_1.loadTelegramSenseCredentials)(TARGET_ID),
259
- createTelegramApi: telegram_client_1.createTelegramBotApi,
276
+ telegramCredentials: acceptanceTelegramIdentity,
260
277
  readLiveGrounding: readIndependentSanctuaryGrounding,
261
278
  runProductionBoundaryProbe: runSanctuaryProductionBoundaryProbe,
262
279
  providerRuntime: core_1.getProviderRuntime,
@@ -529,7 +546,7 @@ async function projectTelegramAdmissions(agentRoot, credentials, identityKey) {
529
546
  const effectStore = new telegram_effect_adapter_1.FileTelegramEffectJournal(effectsRoot);
530
547
  try {
531
548
  const effects = new Map(effectStore.list().map((artifact) => [artifact.id, artifact]));
532
- const botId = (0, telegram_1.telegramBotIdFromToken)(credentials.botToken);
549
+ const botId = credentials.botId;
533
550
  const ownerSessionKey = `telegram:${(0, telegram_1.opaqueTelegramSubject)(identityKey, botId, credentials.authorizedUserId, credentials.authorizedChatId)}`;
534
551
  const friendsRoot = path.join(agentRoot, "friends");
535
552
  const friendStore = (0, node_fs_1.existsSync)(friendsRoot) ? new friends_1.FileFriendStore(friendsRoot) : null;
@@ -657,10 +674,6 @@ function buildPostbootIntegritySnapshot(input) {
657
674
  return JSON.stringify(value);
658
675
  };
659
676
  const digest = (value) => (0, node_crypto_1.createHash)("sha256").update(canonical(value)).digest("hex");
660
- const offset = input.offsetRaw === null ? { nextUpdateId: 0 } : object(JSON.parse(input.offsetRaw), "Telegram offset state");
661
- if (JSON.stringify(Object.keys(offset).sort()) !== JSON.stringify(["nextUpdateId"]) || !Number.isSafeInteger(offset.nextUpdateId) || Number(offset.nextUpdateId) < 0) {
662
- throw new Error("Telegram offset state is invalid");
663
- }
664
677
  const checkpoints = input.checkpointsRaw === null ? {} : object(JSON.parse(input.checkpointsRaw), "approval checkpoint state");
665
678
  const approvalCheckpoints = Object.entries(checkpoints).map(([id, record]) => ({ idDigest: digest(id), recordDigest: digest(record) }))
666
679
  .sort((left, right) => left.idDigest.localeCompare(right.idDigest));
@@ -675,7 +688,7 @@ function buildPostbootIntegritySnapshot(input) {
675
688
  }));
676
689
  return {
677
690
  schemaVersion: "sanctuary-postboot-integrity-v2", activeScenarioHandleDigest: input.activeScenarioHandleDigest,
678
- telegramNextUpdateId: Number(offset.nextUpdateId), approvalCheckpoints,
691
+ telegramNextUpdateId: input.telegramNextUpdateId, approvalCheckpoints,
679
692
  approvalExecutionCount: new Set(restartAttempts.filter((row) => row.state !== "attempt_not_started").map((row) => row.idDigest)).size, restartAttempts,
680
693
  fingerprintDigest: digest(input.cronRaw), sweeps, deliveries,
681
694
  audits: input.auditLedgerEntries.map((row) => ({ idDigest: digest(row), recordDigest: digest(row), scenarioHandleDigest: typeof row.meta.scenarioHandleDigest === "string" ? row.meta.scenarioHandleDigest : null, scenarioRelevant: scenarioRelevantEvents.has(row.event) })),
@@ -781,7 +794,7 @@ function parseHealthProbeReceipt(raw, label, scenarioHandleDigest, identityKey)
781
794
  return { ...validated, phases };
782
795
  }
783
796
  function auditContainsSensitiveMaterial(raw, credentials) {
784
- const knownValues = credentials ? [credentials.botToken, credentials.authorizedUserId, credentials.authorizedChatId] : [];
797
+ const knownValues = credentials ? [credentials.botToken ?? "", credentials.authorizedUserId, credentials.authorizedChatId] : [];
785
798
  return knownValues.some((value) => value.length > 0 && raw.includes(value))
786
799
  || /\b\d{5,16}:[A-Za-z0-9_-]{20,}\b/u.test(raw)
787
800
  || /"(?:authorized_?user_?id|authorized_?chat_?id|transport_?user_?id|transport_?chat_?id|user_?id|chat_?id|update_?id|message_?id)"\s*:\s*"?\d{5,16}"?/iu.test(raw)
@@ -1233,7 +1246,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1233
1246
  const identityRaw = optionalFixedFile(deps, pathFor(agentRoot, "state/senses/telegram/identity.key"));
1234
1247
  const auditRaw = optionalFixedFile(deps, TELEGRAM_AUDIT);
1235
1248
  const auditHeadRaw = optionalFixedFile(deps, TELEGRAM_AUDIT_HEAD);
1236
- const offsetRaw = optionalFixedFile(deps, TELEGRAM_OFFSET);
1249
+ const gatewayCursor = await readGatewayCursor(deps);
1237
1250
  const checkpointsRaw = optionalFixedFile(deps, pathFor(agentRoot, "state/approvals/checkpoints.json"));
1238
1251
  const restartAttemptsRaw = optionalFixedFile(deps, pathFor(agentRoot, "state/acceptance/restart-attempts.ndjson")) ?? "";
1239
1252
  const telegramTurnsRaw = optionalFixedFile(deps, pathFor(agentRoot, "state/acceptance/telegram-turns.ndjson")) ?? "";
@@ -1331,11 +1344,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1331
1344
  });
1332
1345
  const restartAttempts = parseRestartAttempts(restartAttemptsRaw, scenarioHandleDigest);
1333
1346
  const telegramTurns = parseTelegramTurnReceipts(telegramTurnsRaw, scenarioHandleDigest, identityRaw?.trim() ?? null);
1334
- const telegramOffsetState = offsetRaw === null ? { nextUpdateId: 0 } : object(JSON.parse(offsetRaw), "Telegram offset state");
1335
- if (JSON.stringify(Object.keys(telegramOffsetState).sort()) !== JSON.stringify(["nextUpdateId"])
1336
- || !Number.isSafeInteger(telegramOffsetState.nextUpdateId) || Number(telegramOffsetState.nextUpdateId) < 0)
1337
- throw new Error("Telegram offset state is invalid");
1338
- const telegramNextUpdateId = Number(telegramOffsetState.nextUpdateId);
1347
+ const telegramNextUpdateId = gatewayCursor.nextUpdateId;
1339
1348
  const groundingTool = label === "unit-16d-whats-up" ? "unraid_get_system" : label === "unit-16d-1-space" ? "unraid_get_storage" : null;
1340
1349
  const liveGrounding = groundingTool && deps.readLiveGrounding ? await deps.readLiveGrounding(groundingTool) : undefined;
1341
1350
  let identity;
@@ -1343,7 +1352,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1343
1352
  throw new Error("Telegram approval identity key is missing or malformed");
1344
1353
  }
1345
1354
  if (identityRaw && /^[A-Za-z0-9_-]{43}\n?$/u.test(identityRaw)) {
1346
- const credentials = deps.telegramCredentials ? deps.telegramCredentials() : (0, telegram_1.loadTelegramSenseCredentials)(TARGET_ID);
1355
+ const credentials = deps.telegramCredentials ? deps.telegramCredentials() : acceptanceTelegramIdentity();
1347
1356
  const identityKey = identityRaw.trim();
1348
1357
  const auditSchema = label === "unit-16d-whats-up" || label === "unit-16d-1-space" ? "sanctuary-telegram-turn-receipt-v4" : "sanctuary-telegram-turn-receipt-v3";
1349
1358
  for (const entry of auditEntries.filter((candidate) => candidate.event === "senses.telegram_turn_start" || candidate.event === "senses.telegram_turn_end" || candidate.event === "senses.telegram_turn_error")) {
@@ -1374,10 +1383,10 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1374
1383
  }
1375
1384
  // Unknown admission needs the same surfaces for its zero-work digest.
1376
1385
  if (sanctuary_acceptance_harness_1.SANCTUARY_SCENARIO_SOURCES[label].includes("identity-surface-audit") || label === "unit-16d-2-unknown-admission") {
1377
- const expectedSubject = (0, telegram_1.opaqueTelegramSubject)(identityKey, credentials.botToken, credentials.authorizedUserId, credentials.authorizedChatId);
1386
+ const expectedSubject = (0, telegram_1.opaqueTelegramSubject)(identityKey, credentials.botId, credentials.authorizedUserId, credentials.authorizedChatId);
1378
1387
  const observedSubjects = auditEntries.flatMap((entry) => typeof entry.meta.subject === "string" ? [entry.meta.subject] : []);
1379
1388
  const approvalSubjects = approvalRecords.map((projection) => projection.approval).filter((record) => record.transport === "telegram").map((record) => record.requesterId);
1380
- const rawValues = [...new Set([credentials.botToken, credentials.authorizedUserId, credentials.authorizedChatId])];
1389
+ const rawValues = [...new Set([credentials.authorizedUserId, credentials.authorizedChatId])];
1381
1390
  const persistedIdentitySurfaces = readBoundedIdentitySurfaces(agentRoot);
1382
1391
  const surfacePairs = Array.from({ length: Math.floor(persistedIdentitySurfaces.length / 2) }, (_, index) => ({
1383
1392
  relativePath: persistedIdentitySurfaces[index * 2],
@@ -1409,7 +1418,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1409
1418
  const surfaceRecords = [...persistedIdentitySurfaces, auditRaw ?? "", auditHeadRaw ?? "", JSON.stringify(approvalRecords)];
1410
1419
  const surfaceSubjects = surfaceRecords.flatMap((raw) => raw.match(/tg_[A-Za-z0-9_-]{43}/gu) ?? []);
1411
1420
  const structuredRawId = /"(?:authorizedUserId|authorizedChatId|transportUserId|transportChatId|userId|chatId|updateId|messageId)"\s*:\s*"?\d{1,20}"?/gu;
1412
- const rawLeakCount = surfaceRecords.reduce((count, raw) => count + rawValues.filter((value) => raw.includes(value)).length + (raw.match(structuredRawId)?.length ?? 0), 0);
1421
+ const rawLeakCount = surfaceRecords.reduce((count, raw) => count + rawValues.filter((value) => raw.includes(value)).length + (raw.match(structuredRawId)?.length ?? 0) + Number(auditContainsSensitiveMaterial(raw)), 0);
1413
1422
  const mismatchCount = [...surfaceSubjects, ...observedSubjects, ...approvalSubjects].filter((subject) => subject !== expectedSubject).length;
1414
1423
  identity = {
1415
1424
  keyPresent: true,
@@ -1462,7 +1471,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1462
1471
  }
1463
1472
  const health = parseHealthAcceptanceState(healthRaw);
1464
1473
  const activeMarker = (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)(TARGET_ID, agentRoot);
1465
- const postbootIntegrity = buildPostbootIntegritySnapshot({ offsetRaw, checkpointsRaw, restartAttempts: parseRestartAttempts(restartAttemptsRaw, null), cronRaw, health, auditLedgerEntries, activeScenarioHandleDigest: activeMarker?.scenarioHandleDigest ?? null });
1474
+ const postbootIntegrity = buildPostbootIntegritySnapshot({ telegramNextUpdateId, checkpointsRaw, restartAttempts: parseRestartAttempts(restartAttemptsRaw, null), cronRaw, health, auditLedgerEntries, activeScenarioHandleDigest: activeMarker?.scenarioHandleDigest ?? null });
1466
1475
  const prebootIntegrity = rebootCheckpoint && typeof rebootCheckpoint.prebootIntegrity === "object" && !Array.isArray(rebootCheckpoint.prebootIntegrity)
1467
1476
  ? rebootCheckpoint.prebootIntegrity : undefined;
1468
1477
  const healthProbe = parseHealthProbeReceipt(healthProbeRaw, label, scenarioHandleDigest, identityRaw?.trim() ?? null);
@@ -1550,7 +1559,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1550
1559
  const selection = select(context);
1551
1560
  const schemas = (0, tools_1.toolSelectionSchemas)(selection);
1552
1561
  const schemaToolNames = schemas.map((tool) => tool.function.name);
1553
- const expectedNames = profile.toolNames.filter((name) => (inner || name !== "rest") && (name !== "set_reasoning_effort" || runtime.capabilities.has("reasoning-effort")));
1562
+ const expectedNames = profile.toolNames.filter((name) => name !== "sanctuary_host_execute" && (inner || name !== "rest") && (name !== "set_reasoning_effort" || runtime.capabilities.has("reasoning-effort")));
1554
1563
  const excludedNames = ["vault_get", "mcp_call", "exec", "credential_get", ...(profileId === "sanctuary-owner" ? [] : tools_1.SANCTUARY_OWNER_ADDITIONS)];
1555
1564
  const poisoned = select({ ...context, relationshipAuthorization: {
1556
1565
  ...context.relationshipAuthorization, advertisedToolNames: [...profile.toolNames, ...excludedNames, "mcp__containment_poison"],
@@ -1635,7 +1644,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1635
1644
  ? await projectTelegramAdmissions(agentRoot, dependency(deps.telegramCredentials, "Telegram credentials")(), identityRaw?.trim() ?? null)
1636
1645
  : [];
1637
1646
  const sourceValues = {
1638
- "identity-key": identityRaw, "telegram-audit": auditEntries, "telegram-offset": offsetRaw,
1647
+ "identity-key": identityRaw, "telegram-audit": auditEntries, "telegram-offset": gatewayCursor.progressDigest,
1639
1648
  "approval-journal": approvals, "approval-checkpoints": checkpointsRaw, "container-inspect": container,
1640
1649
  "provider-live-check": liveProvider ?? null, "cron-runtime": cronRaw, "health-runtime": health, "restart-attempt-ledger": restartAttempts,
1641
1650
  "digest-runtime": health, "reboot-checkpoint": rebootCheckpoint, "telegram-turn-receipts": telegramTurns, "read-only-denial-receipt": denialReceipt ?? null,
@@ -1703,7 +1712,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1703
1712
  },
1704
1713
  };
1705
1714
  }
1706
- function postbootIntegritySnapshot(deps) {
1715
+ async function postbootIntegritySnapshot(deps) {
1707
1716
  const agentRoot = (0, identity_1.getAgentRoot)(TARGET_ID);
1708
1717
  const identityRaw = optionalFixedFile(deps, pathFor(agentRoot, "state/senses/telegram/identity.key"));
1709
1718
  const auditRaw = optionalFixedFile(deps, TELEGRAM_AUDIT);
@@ -1719,7 +1728,7 @@ function postbootIntegritySnapshot(deps) {
1719
1728
  });
1720
1729
  const restartAttemptsRaw = optionalFixedFile(deps, pathFor(agentRoot, "state/acceptance/restart-attempts.ndjson")) ?? "";
1721
1730
  return buildPostbootIntegritySnapshot({
1722
- offsetRaw: optionalFixedFile(deps, TELEGRAM_OFFSET), checkpointsRaw: optionalFixedFile(deps, pathFor(agentRoot, "state/approvals/checkpoints.json")),
1731
+ telegramNextUpdateId: (await readGatewayCursor(deps)).nextUpdateId, checkpointsRaw: optionalFixedFile(deps, pathFor(agentRoot, "state/approvals/checkpoints.json")),
1723
1732
  restartAttempts: parseRestartAttempts(restartAttemptsRaw, null), cronRaw: optionalFixedFile(deps, "/home/ouro/.ouro-cli/scheduler/sanctuary.crontab"),
1724
1733
  health: parseHealthAcceptanceState(optionalFixedFile(deps, pathFor(agentRoot, "state/health/sanctuary-health.json"))), auditLedgerEntries,
1725
1734
  activeScenarioHandleDigest: (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)(TARGET_ID)?.scenarioHandleDigest ?? null,
@@ -1881,18 +1890,21 @@ async function runtimeConfig(reader, label, ...args) {
1881
1890
  throw new Error(`${label} is unavailable`);
1882
1891
  return result.config;
1883
1892
  }
1884
- function cursorSnapshot(payload, deps) {
1893
+ async function readGatewayCursor(deps) {
1894
+ const cursor = await dependency(deps.gatewayCursor, "signed gateway cursor")();
1895
+ if (!cursor || !Number.isSafeInteger(cursor.nextUpdateId) || cursor.nextUpdateId < 0 || typeof cursor.progressDigest !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(cursor.progressDigest))
1896
+ throw new Error("signed gateway cursor is invalid");
1897
+ return cursor;
1898
+ }
1899
+ async function cursorSnapshot(payload, deps) {
1885
1900
  exactKeys(payload, ["allowGenesis", "operation", "schema"], "Telegram cursor snapshot request");
1886
1901
  if (payload.schema !== "telegram-cursor-v1" || typeof payload.allowGenesis !== "boolean")
1887
1902
  throw new Error("Telegram cursor snapshot request is invalid");
1888
- const offsetRaw = fixedFile(deps, TELEGRAM_OFFSET);
1903
+ const cursor = await readGatewayCursor(deps);
1889
1904
  const identityKey = fixedFile(deps, TELEGRAM_IDENTITY_KEY).trim();
1890
1905
  if (!/^[A-Za-z0-9_-]{43}$/u.test(identityKey) || Buffer.from(identityKey, "base64url").length !== 32) {
1891
1906
  throw new Error("Telegram identity key is invalid");
1892
1907
  }
1893
- const offset = object(JSON.parse(offsetRaw), "Telegram offset");
1894
- if (!Number.isSafeInteger(offset.nextUpdateId) || offset.nextUpdateId < 0)
1895
- throw new Error("Telegram offset is invalid");
1896
1908
  let auditCursorDigest;
1897
1909
  let verificationFailure;
1898
1910
  let observedAuditState = false;
@@ -1925,19 +1937,33 @@ function cursorSnapshot(payload, deps) {
1925
1937
  throw new Error(`Telegram acceptance audit state could not be captured consistently${verificationFailure ? `: ${verificationFailure.message}` : ""}`, verificationFailure ? { cause: verificationFailure } : undefined);
1926
1938
  }
1927
1939
  return {
1928
- offsetDigest: sha256(JSON.stringify({ nextUpdateId: offset.nextUpdateId })),
1940
+ offsetDigest: cursor.progressDigest.slice("sha256:".length),
1929
1941
  auditCursorDigest,
1930
1942
  };
1931
1943
  }
1932
- function telegramPollerQuiescence(payload, deps) {
1944
+ async function telegramPollerQuiescence(payload, deps) {
1933
1945
  if (payload.expectedState !== "stopped" || JSON.stringify(Object.keys(payload).sort()) !== JSON.stringify(["expectedState", "operation"])) {
1934
1946
  throw new Error("Telegram poller quiescence request is invalid");
1935
1947
  }
1936
- const fact = object(JSON.parse(fixedFile(deps, TELEGRAM_POLLER_COUNT_FILE)), "Telegram poller count");
1937
- exactKeys(fact, ["activePollers", "productionContainerStopped"], "Telegram poller count");
1938
- if (fact.activePollers !== 0 || fact.productionContainerStopped !== true)
1939
- throw new Error("Telegram poller is not quiescent");
1940
- return { quiesced: true, activePollers: 0 };
1948
+ const fact = object(await dependency(deps.hostRequest, "Sanctuary host broker")({ operation: "telegram_gateway_quiescence", targetId: TARGET_ID }), "Telegram root poller proof");
1949
+ exactKeys(fact, ["schemaVersion", "activePollers", "residentStopped", "processBindingDigest", "keyId", "publicKeyDigest", "botId", "observedAt"], "Telegram root poller proof");
1950
+ const gateway = dependency(deps.gateway, "Telegram root gateway")();
1951
+ try {
1952
+ if (!await gateway.authorityTransport.hostApproval?.refresh())
1953
+ throw new Error("Telegram root gateway health is unavailable");
1954
+ const cursor = await gateway.cursorSnapshot();
1955
+ const now = (deps.now ?? Date.now)();
1956
+ if (fact.schemaVersion !== 1 || fact.activePollers !== 1 || fact.residentStopped !== true
1957
+ || typeof fact.processBindingDigest !== "string" || !SHA256.test(fact.processBindingDigest)
1958
+ || fact.keyId !== cursor.keyId || fact.publicKeyDigest !== cursor.publicKeyDigest || fact.botId !== cursor.botId
1959
+ || typeof fact.observedAt !== "string" || !Number.isFinite(Date.parse(fact.observedAt))
1960
+ || Date.parse(fact.observedAt) > now || now - Date.parse(fact.observedAt) > 30_000)
1961
+ throw new Error("Telegram root poller proof is invalid or stale");
1962
+ return { quiesced: true, activePollers: 1 };
1963
+ }
1964
+ finally {
1965
+ gateway.authorityTransport.api.stop();
1966
+ }
1941
1967
  }
1942
1968
  function callbackUpdate(value) {
1943
1969
  const update = object(value, "callback update");
@@ -2013,13 +2039,10 @@ function callbackCoordinate(update) {
2013
2039
  digest: sha256(`ouroboros.sanctuary.callback-coordinate.v1\0${JSON.stringify(coordinate)}`),
2014
2040
  };
2015
2041
  }
2016
- function callbackPlaybackPreflight(payload, deps) {
2042
+ async function callbackPlaybackPreflight(payload, deps) {
2017
2043
  exactKeys(payload, ["operation", "update"], "callback playback preflight request");
2018
2044
  const coordinate = callbackCoordinate(callbackUpdate(payload.update));
2019
- const offset = object(JSON.parse(fixedFile(deps, TELEGRAM_OFFSET)), "Telegram offset");
2020
- exactKeys(offset, ["nextUpdateId"], "Telegram offset");
2021
- if (!Number.isSafeInteger(offset.nextUpdateId) || Number(offset.nextUpdateId) < 0)
2022
- throw new Error("Telegram offset is invalid");
2045
+ const offset = await readGatewayCursor(deps);
2023
2046
  const snapshot = object(dependency(deps.callbackPlaybackSnapshot, "callback playback journal")(coordinate.digest), "callback playback journal snapshot");
2024
2047
  exactKeys(snapshot, ["coordinateDigest", "journalDigest", "playbackCount"], "callback playback journal snapshot");
2025
2048
  if (snapshot.coordinateDigest !== coordinate.digest || typeof snapshot.journalDigest !== "string" || !SHA256.test(snapshot.journalDigest)
@@ -2221,17 +2244,17 @@ async function probeRevokedKey(payload, deps) {
2221
2244
  throw new Error("revoked Unraid key recovery cleanup failed");
2222
2245
  return { valid: false, status: response.status, id };
2223
2246
  }
2224
- function provenance(payload, deps) {
2247
+ async function provenance(payload, deps) {
2225
2248
  if (payload.schema !== "sanctuary-unit-16-provenance-v1")
2226
2249
  throw new Error("provenance schema is invalid");
2227
2250
  const imageDigest = fixedFile(deps, IMAGE_DIGEST_FILE).trim();
2228
2251
  const containerDigest = fixedFile(deps, CONTAINER_DIGEST_FILE).trim();
2229
2252
  if (!SHA256.test(imageDigest) || !SHA256.test(containerDigest))
2230
2253
  throw new Error("live provenance digest is invalid");
2231
- const cursor = cursorSnapshot({ operation: "snapshot", schema: "telegram-cursor-v1", allowGenesis: false }, deps);
2254
+ const cursor = await cursorSnapshot({ operation: "snapshot", schema: "telegram-cursor-v1", allowGenesis: false }, deps);
2232
2255
  return { imageDigest, containerDigest, cursorDigest: sha256(`${cursor.offsetDigest}\0${cursor.auditCursorDigest}`) };
2233
2256
  }
2234
- function evidenceSnapshot(payload, deps) {
2257
+ async function evidenceSnapshot(payload, deps) {
2235
2258
  if (payload.schema !== "postboot-health-v1")
2236
2259
  throw new Error("evidence schema is invalid");
2237
2260
  const health = object(JSON.parse(fixedFile(deps, POSTBOOT_HEALTH_FILE)), "postboot health");
@@ -2244,7 +2267,7 @@ function evidenceSnapshot(payload, deps) {
2244
2267
  return {
2245
2268
  healthy: health.healthy,
2246
2269
  containerImageDigest: imageDigest,
2247
- telegramOffsetDigest: cursorSnapshot({ operation: "snapshot", schema: "telegram-cursor-v1", allowGenesis: false }, deps).offsetDigest,
2270
+ telegramOffsetDigest: (await cursorSnapshot({ operation: "snapshot", schema: "telegram-cursor-v1", allowGenesis: false }, deps)).offsetDigest,
2248
2271
  };
2249
2272
  }
2250
2273
  function bootId(deps) {
@@ -2296,7 +2319,7 @@ function pollReboot(payload, deps) {
2296
2319
  throw new Error("requestId is invalid");
2297
2320
  return { targetId: TARGET_ID, requestId, state: "ready", bootId: bootId(deps) };
2298
2321
  }
2299
- function materializeConfig(payload, deps) {
2322
+ async function materializeConfig(payload, deps) {
2300
2323
  const command = text(payload.command, "materializer command");
2301
2324
  const contract = object(JSON.parse(fixedFile(deps, CONTRACT_FILE)), "acceptance contract");
2302
2325
  const templates = object(contract.configTemplates, "acceptance config templates");
@@ -2304,9 +2327,7 @@ function materializeConfig(payload, deps) {
2304
2327
  const config = { ...object(template.fixed, "acceptance fixed config") };
2305
2328
  const adapterPath = "/opt/ouro/deploy/unraid/sanctuary-acceptance-adapter.sh";
2306
2329
  if (command === "telegram-bootstrap") {
2307
- const offset = object(JSON.parse(fixedFile(deps, TELEGRAM_OFFSET)), "Telegram offset");
2308
- if (!Number.isSafeInteger(offset.nextUpdateId) || offset.nextUpdateId < 0)
2309
- throw new Error("Telegram offset is invalid");
2330
+ const offset = await readGatewayCursor(deps);
2310
2331
  Object.assign(config, { expectedBotId: "8541786263", expectedUsername: "MendelowCloudButlerBot", currentOffset: offset.nextUpdateId });
2311
2332
  }
2312
2333
  else if (command === "cursor-snapshot") {
@@ -2332,36 +2353,23 @@ function materializeConfig(payload, deps) {
2332
2353
  }
2333
2354
  async function telegramReadiness(payload, deps) {
2334
2355
  exactKeys(payload, ["operation"], "Telegram readiness payload");
2335
- let refreshed;
2356
+ let gateway;
2336
2357
  try {
2337
- refreshed = await dependency(deps.refreshRuntime, "runtime credential refresher")(TARGET_ID);
2358
+ gateway = dependency(deps.gateway, "Telegram root gateway")();
2338
2359
  }
2339
2360
  catch {
2340
- throw new Error("Telegram runtime credentials are unavailable; actor: human-required; unlock or repair vault runtime/config");
2341
- }
2342
- if (!refreshed.ok)
2343
- throw new Error("Telegram runtime credentials are unavailable; actor: human-required; unlock or repair vault runtime/config");
2344
- let credentials;
2345
- try {
2346
- credentials = dependency(deps.telegramCredentials, "Telegram credentials")();
2347
- (0, telegram_1.telegramBotIdFromToken)(credentials.botToken);
2348
- }
2349
- catch {
2350
- throw new Error("Telegram runtime credentials are invalid; actor: human-required; repair vault runtime/config");
2351
- }
2352
- let api;
2353
- try {
2354
- api = dependency(deps.createTelegramApi, "Telegram API factory")({ token: credentials.botToken });
2355
- }
2356
- catch {
2357
- throw new Error("Telegram client initialization failed; actor: agent-runnable; retry Telegram readiness");
2361
+ throw new Error("Telegram gateway inventory or pins are unavailable; actor: agent-runnable; inspect root migration");
2358
2362
  }
2363
+ const api = gateway.authorityTransport.api;
2359
2364
  let bot;
2360
2365
  try {
2366
+ if (!await gateway.authorityTransport.hostApproval?.refresh())
2367
+ throw new Error("root authority is unhealthy");
2368
+ await gateway.cursorSnapshot();
2361
2369
  bot = await api.request("getMe", {}, AbortSignal.timeout(30_000));
2362
2370
  }
2363
2371
  catch {
2364
- throw new Error("Telegram getMe failed; actor: agent-runnable; retry Telegram readiness");
2372
+ throw new Error("Telegram gateway health or identity probe failed; actor: agent-runnable; inspect root authority");
2365
2373
  }
2366
2374
  finally {
2367
2375
  try {
@@ -2372,9 +2380,9 @@ async function telegramReadiness(payload, deps) {
2372
2380
  }
2373
2381
  }
2374
2382
  if (!bot || typeof bot !== "object" || Array.isArray(bot)
2375
- || String(bot.id) !== "8541786263"
2383
+ || String(bot.id) !== gateway.credentials.botId || gateway.credentials.botId !== "8541786263"
2376
2384
  || bot.username !== "MendelowCloudButlerBot") {
2377
- throw new Error("Telegram bot identity mismatch; actor: human-required; repair vault runtime/config");
2385
+ throw new Error("Telegram bot identity mismatch; actor: human-required; repair root gateway identity");
2378
2386
  }
2379
2387
  return { ready: true, identityMatches: true };
2380
2388
  }
@@ -2398,13 +2406,13 @@ async function executeSanctuaryAcceptanceAdapter(rawPayload, deps = createSanctu
2398
2406
  result = await revokedKeyAuthRejection(payload, deps);
2399
2407
  break;
2400
2408
  case "quiesce_telegram_poller":
2401
- result = telegramPollerQuiescence(payload, deps);
2409
+ result = await telegramPollerQuiescence(payload, deps);
2402
2410
  break;
2403
2411
  case "snapshot":
2404
- result = cursorSnapshot(payload, deps);
2412
+ result = await cursorSnapshot(payload, deps);
2405
2413
  break;
2406
2414
  case "callback_playback_preflight":
2407
- result = callbackPlaybackPreflight(payload, deps);
2415
+ result = await callbackPlaybackPreflight(payload, deps);
2408
2416
  break;
2409
2417
  case "inject_callbacks_concurrently":
2410
2418
  result = await concurrentCallbackProbe(payload, deps);
@@ -2449,10 +2457,10 @@ async function executeSanctuaryAcceptanceAdapter(rawPayload, deps = createSanctu
2449
2457
  result = await probeRevokedKey(payload, deps);
2450
2458
  break;
2451
2459
  case "evidence_snapshot":
2452
- result = evidenceSnapshot(payload, deps);
2460
+ result = await evidenceSnapshot(payload, deps);
2453
2461
  break;
2454
2462
  case "capture_evidence_provenance":
2455
- result = provenance(payload, deps);
2463
+ result = await provenance(payload, deps);
2456
2464
  break;
2457
2465
  case "capture_acceptance_scenario":
2458
2466
  result = await captureAcceptanceScenario(payload, deps);
@@ -2468,7 +2476,7 @@ async function executeSanctuaryAcceptanceAdapter(rawPayload, deps = createSanctu
2468
2476
  result = await rebootPreflight(payload, deps);
2469
2477
  break;
2470
2478
  case "postboot_integrity_snapshot":
2471
- result = postbootIntegritySnapshot(deps);
2479
+ result = await postbootIntegritySnapshot(deps);
2472
2480
  break;
2473
2481
  case "request_reboot":
2474
2482
  result = await requestReboot(payload, deps);
@@ -2477,7 +2485,7 @@ async function executeSanctuaryAcceptanceAdapter(rawPayload, deps = createSanctu
2477
2485
  result = pollReboot(payload, deps);
2478
2486
  break;
2479
2487
  case "materialize_config":
2480
- result = materializeConfig(payload, deps);
2488
+ result = await materializeConfig(payload, deps);
2481
2489
  break;
2482
2490
  case "telegram_readiness":
2483
2491
  result = await telegramReadiness(payload, deps);
@@ -2596,23 +2604,19 @@ async function executeSanctuaryInteractiveRuntimeOperation(rawPayload, supplied)
2596
2604
  return (0, sanctuary_interactive_control_1.executeSanctuaryInteractiveEngine)(rawPayload, deps);
2597
2605
  }
2598
2606
  async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, replay, deps = {
2599
- refresh: runtime_credentials_1.refreshRuntimeCredentialConfig,
2600
- credentials: telegram_1.loadTelegramSenseCredentials,
2607
+ gateway: () => (0, sanctuary_authority_resident_1.openSanctuaryResidentAuthority)({}, {}),
2601
2608
  identityKey: telegram_1.readOrCreateTelegramIdentityKey,
2602
- createApi: telegram_client_1.createTelegramBotApi,
2603
2609
  createRuntime: telegram_approval_runtime_1.createTelegramApprovalRuntime,
2604
2610
  toolContext: sanctuary_runtime_1.createSanctuaryToolContext,
2605
2611
  effects: createAcceptanceProbeEffects,
2606
2612
  recordCallbackPlayback: (coordinateDigest) => recordCallbackPlayback((0, identity_1.getAgentRoot)(TARGET_ID), coordinateDigest),
2607
2613
  }) {
2608
2614
  const update = callbackUpdate(rawUpdate);
2609
- const refreshed = await deps.refresh(TARGET_ID);
2610
- if (!refreshed.ok)
2611
- throw new Error("Telegram runtime credentials are unavailable");
2612
- const credentials = deps.credentials(TARGET_ID);
2615
+ const gateway = deps.gateway();
2616
+ const credentials = gateway.credentials;
2613
2617
  const identityKey = deps.identityKey((0, identity_1.getAgentRoot)(TARGET_ID));
2614
- const subject = (0, telegram_1.opaqueTelegramSubject)(identityKey, credentials.botToken, credentials.authorizedUserId, credentials.authorizedChatId);
2615
- const api = deps.createApi({ token: credentials.botToken });
2618
+ const subject = (0, telegram_1.opaqueTelegramSubject)(identityKey, credentials.botId, credentials.authorizedUserId, credentials.authorizedChatId);
2619
+ const api = gateway.authorityTransport.api;
2616
2620
  const effectBoundary = deps.effects?.({ agentRoot: (0, identity_1.getAgentRoot)(TARGET_ID), api, subject, chatId: credentials.authorizedChatId });
2617
2621
  const unavailableEffects = {
2618
2622
  sendText: async () => { throw new Error("Telegram acceptance probe effect boundary is unavailable"); },