@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.
- package/assets/sanctuary-host-launcher.sh +16 -0
- package/changelog.json +10 -0
- package/deploy/unraid/Dockerfile +6 -0
- package/deploy/unraid/README.txt +280 -60
- package/deploy/unraid/docker-man-template-transaction.mjs +192 -7
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +1 -1
- package/deploy/unraid/sanctuary-acceptance-contract.json +7 -7
- package/deploy/unraid/sanctuary-authority-installation.json +36 -0
- package/deploy/unraid/sanctuary-authority-service.sh +30 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +62 -9
- package/deploy/unraid/sanctuary-unit16-run.sh +15 -15
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +2 -2
- package/deploy/unraid/sanctuary.xml +2 -1
- package/dist/heart/core.js +2 -1
- package/dist/heart/daemon/container-spec-auditor-main.js +8 -7
- package/dist/heart/daemon/container-spec-auditor.js +16 -9
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +95 -91
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +95 -169
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +4 -5
- package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
- package/dist/heart/daemon/sanctuary-authority-epoch.js +256 -0
- package/dist/heart/daemon/sanctuary-authority-installation.js +140 -0
- package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
- package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +780 -0
- package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
- package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
- package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +494 -0
- package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
- package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
- package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
- package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +445 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-service.js +615 -0
- package/dist/heart/daemon/sense-manager.js +20 -10
- package/dist/repertoire/tools-sanctuary-host.js +202 -0
- package/dist/repertoire/tools.js +15 -6
- package/dist/senses/root-host-approval-port.js +345 -0
- package/dist/senses/root-host-approval-runtime.js +393 -0
- package/dist/senses/sanctuary-authority-resident.js +102 -0
- package/dist/senses/telegram-admission.js +7 -0
- package/dist/senses/telegram-attachments.js +5 -1
- package/dist/senses/telegram-authority-transport.js +231 -0
- package/dist/senses/telegram-client.js +31 -8
- package/dist/senses/telegram-entry.js +1 -1
- package/dist/senses/telegram.js +168 -28
- package/npm-shrinkwrap.json +2 -2
- 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:
|
|
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 =
|
|
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:
|
|
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
|
|
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
|
|
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() : (
|
|
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.
|
|
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.
|
|
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({
|
|
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":
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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(
|
|
1937
|
-
exactKeys(fact, ["activePollers", "
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
2356
|
+
let gateway;
|
|
2336
2357
|
try {
|
|
2337
|
-
|
|
2358
|
+
gateway = dependency(deps.gateway, "Telegram root gateway")();
|
|
2338
2359
|
}
|
|
2339
2360
|
catch {
|
|
2340
|
-
throw new Error("Telegram
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
2610
|
-
|
|
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.
|
|
2615
|
-
const api =
|
|
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"); },
|