@ouro.bot/cli 0.1.0-alpha.767 → 0.1.0-alpha.768

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/changelog.json CHANGED
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.768",
6
+ "changes": [
7
+ "Fix Sanctuary acceptance cursor snapshots for authenticated genesis and concurrent audit writes.",
8
+ "Cover fail-closed cursor schema, authority, and offset validation."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.767",
6
13
  "changes": [
@@ -102,7 +102,7 @@
102
102
  "fixed": {
103
103
  "allowedRoot": "/evidence",
104
104
  "evidencePath": "/evidence/cursor-snapshot.json",
105
- "adapters": [{ "schema": "telegram-cursor-v1", "executable": "/opt/ouro/deploy/unraid/sanctuary-acceptance-adapter.sh" }]
105
+ "adapters": [{ "schema": "telegram-cursor-v1", "executable": "/opt/ouro/deploy/unraid/sanctuary-acceptance-adapter.sh", "allowGenesis": false }]
106
106
  }
107
107
  },
108
108
  "cursor-delta": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.767",
2
+ "runtimeVersion": "0.1.0-alpha.768",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-08-30T00:00:00.000Z"
5
5
  }
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0"?>
2
2
  <Container version="2">
3
3
  <Name>Mendelow Cloud Butler</Name>
4
- <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.767</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.768</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -99,6 +99,7 @@ const NETWORK_TIMEOUT_MS = 10_000;
99
99
  const KEY_DIRECTORY = "/boot/config/plugins/dynamix.my.servers/keys";
100
100
  const SELECTED_KEY_RECORD = "/run/ouro-acceptance/unraid-key.json";
101
101
  const TELEGRAM_OFFSET = "/home/ouro/AgentBundles/sanctuary.ouro/state/senses/telegram/offset.json";
102
+ const TELEGRAM_IDENTITY_KEY = "/home/ouro/AgentBundles/sanctuary.ouro/state/senses/telegram/identity.key";
102
103
  const TELEGRAM_AUDIT = `/home/ouro/AgentBundles/sanctuary.ouro/${telegram_audit_ledger_1.TELEGRAM_ACCEPTANCE_AUDIT_RELATIVE_PATH}`;
103
104
  const TELEGRAM_AUDIT_HEAD = `/home/ouro/AgentBundles/sanctuary.ouro/${telegram_audit_ledger_1.TELEGRAM_ACCEPTANCE_AUDIT_HEAD_RELATIVE_PATH}`;
104
105
  const IMAGE_DIGEST_FILE = "/run/ouro-acceptance/image-digest";
@@ -1848,16 +1849,52 @@ async function storeTelegramBootstrap(payload, deps) {
1848
1849
  }
1849
1850
  return { stored: true };
1850
1851
  }
1851
- function cursorSnapshot(deps) {
1852
+ function cursorSnapshot(payload, deps) {
1853
+ exactKeys(payload, ["allowGenesis", "operation", "schema"], "Telegram cursor snapshot request");
1854
+ if (payload.schema !== "telegram-cursor-v1" || typeof payload.allowGenesis !== "boolean")
1855
+ throw new Error("Telegram cursor snapshot request is invalid");
1852
1856
  const offsetRaw = fixedFile(deps, TELEGRAM_OFFSET);
1853
- const auditRaw = fixedFile(deps, TELEGRAM_AUDIT);
1854
- const auditHeadRaw = fixedFile(deps, TELEGRAM_AUDIT_HEAD);
1857
+ const identityKey = fixedFile(deps, TELEGRAM_IDENTITY_KEY).trim();
1858
+ if (!/^[A-Za-z0-9_-]{43}$/u.test(identityKey) || Buffer.from(identityKey, "base64url").length !== 32) {
1859
+ throw new Error("Telegram identity key is invalid");
1860
+ }
1855
1861
  const offset = object(JSON.parse(offsetRaw), "Telegram offset");
1856
1862
  if (!Number.isSafeInteger(offset.nextUpdateId) || offset.nextUpdateId < 0)
1857
1863
  throw new Error("Telegram offset is invalid");
1864
+ let auditCursorDigest;
1865
+ let verificationFailure;
1866
+ let observedAuditState = false;
1867
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1868
+ const headBefore = optionalFixedFile(deps, TELEGRAM_AUDIT_HEAD);
1869
+ const auditRaw = optionalFixedFile(deps, TELEGRAM_AUDIT);
1870
+ const headAfter = optionalFixedFile(deps, TELEGRAM_AUDIT_HEAD);
1871
+ if (headBefore === null && auditRaw === null && headAfter === null) {
1872
+ if (payload.allowGenesis !== true)
1873
+ throw new Error("Telegram audit genesis is not authorized for this snapshot");
1874
+ if (!observedAuditState) {
1875
+ auditCursorDigest = sha256("ouroboros.telegram.acceptance.cursor.v1\0genesis");
1876
+ break;
1877
+ }
1878
+ continue;
1879
+ }
1880
+ observedAuditState = true;
1881
+ if (headBefore !== null && auditRaw !== null && headAfter !== null && headBefore === headAfter) {
1882
+ try {
1883
+ (0, telegram_audit_ledger_1.verifyTelegramAuditLedger)({ ledgerRaw: auditRaw, headRaw: headAfter, identityKey });
1884
+ auditCursorDigest = sha256(`ouroboros.telegram.acceptance.cursor.v1\0present\0${auditRaw}\0${headAfter}`);
1885
+ break;
1886
+ }
1887
+ catch (error) {
1888
+ verificationFailure = error;
1889
+ }
1890
+ }
1891
+ }
1892
+ if (auditCursorDigest === undefined) {
1893
+ throw new Error(`Telegram acceptance audit state could not be captured consistently${verificationFailure ? `: ${verificationFailure.message}` : ""}`, verificationFailure ? { cause: verificationFailure } : undefined);
1894
+ }
1858
1895
  return {
1859
1896
  offsetDigest: sha256(JSON.stringify({ nextUpdateId: offset.nextUpdateId })),
1860
- auditCursorDigest: sha256(`${auditRaw}\0${auditHeadRaw}`),
1897
+ auditCursorDigest,
1861
1898
  };
1862
1899
  }
1863
1900
  function telegramPollerQuiescence(payload, deps) {
@@ -2065,7 +2102,7 @@ function provenance(payload, deps) {
2065
2102
  const containerDigest = fixedFile(deps, CONTAINER_DIGEST_FILE).trim();
2066
2103
  if (!SHA256.test(imageDigest) || !SHA256.test(containerDigest))
2067
2104
  throw new Error("live provenance digest is invalid");
2068
- const cursor = cursorSnapshot(deps);
2105
+ const cursor = cursorSnapshot({ operation: "snapshot", schema: "telegram-cursor-v1", allowGenesis: false }, deps);
2069
2106
  return { imageDigest, containerDigest, cursorDigest: sha256(`${cursor.offsetDigest}\0${cursor.auditCursorDigest}`) };
2070
2107
  }
2071
2108
  function evidenceSnapshot(payload, deps) {
@@ -2078,7 +2115,11 @@ function evidenceSnapshot(payload, deps) {
2078
2115
  const imageDigest = fixedFile(deps, IMAGE_DIGEST_FILE).trim();
2079
2116
  if (!SHA256.test(imageDigest))
2080
2117
  throw new Error("container image digest is invalid");
2081
- return { healthy: health.healthy, containerImageDigest: imageDigest, telegramOffsetDigest: cursorSnapshot(deps).offsetDigest };
2118
+ return {
2119
+ healthy: health.healthy,
2120
+ containerImageDigest: imageDigest,
2121
+ telegramOffsetDigest: cursorSnapshot({ operation: "snapshot", schema: "telegram-cursor-v1", allowGenesis: false }, deps).offsetDigest,
2122
+ };
2082
2123
  }
2083
2124
  function bootId(deps) {
2084
2125
  const value = fixedFile(deps, BOOT_ID_FILE).trim();
@@ -2147,6 +2188,7 @@ function materializeConfig(payload, deps) {
2147
2188
  if (phase !== "before" && phase !== "after")
2148
2189
  throw new Error("cursor snapshot phase is invalid");
2149
2190
  config.evidencePath = `/evidence/cursor-${phase}.json`;
2191
+ config.adapters = config.adapters.map((adapter) => ({ ...adapter, allowGenesis: phase === "before" }));
2150
2192
  }
2151
2193
  else if (command === "unraid-key-rotate") {
2152
2194
  const closed = object(JSON.parse(fixedFile(deps, CLOSED_INVENTORY_FILE)), "closed Unraid inventory");
@@ -2188,7 +2230,7 @@ async function executeSanctuaryAcceptanceAdapter(rawPayload, deps = createSanctu
2188
2230
  result = telegramPollerQuiescence(payload, deps);
2189
2231
  break;
2190
2232
  case "snapshot":
2191
- result = cursorSnapshot(deps);
2233
+ result = cursorSnapshot(payload, deps);
2192
2234
  break;
2193
2235
  case "inject_callbacks_concurrently":
2194
2236
  result = await concurrentCallbackProbe(payload, deps);
@@ -1011,7 +1011,10 @@ async function cursorSnapshot(config, deps) {
1011
1011
  throw new Error("snapshot adapter schemas must be unique");
1012
1012
  schemas.add(schema);
1013
1013
  const executable = adapter(spec.executable, "snapshot adapter executable");
1014
- const payload = await deps.runAdapter(executable, { operation: "snapshot", schema });
1014
+ const allowGenesis = spec.allowGenesis === undefined ? false : boolean(spec.allowGenesis, "snapshot adapter allowGenesis");
1015
+ if (allowGenesis && evidencePath !== path.join(root, "cursor-before.json"))
1016
+ throw new Error("snapshot adapter genesis authority is only valid for cursor-before evidence");
1017
+ const payload = await deps.runAdapter(executable, { operation: "snapshot", schema, allowGenesis });
1015
1018
  const selected = fixedEvidenceValues(schema, payload);
1016
1019
  for (const [name, value] of Object.entries(selected))
1017
1020
  values[`${schema}.${name}`] = value;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.767",
3
+ "version": "0.1.0-alpha.768",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@ouro.bot/cli",
9
- "version": "0.1.0-alpha.767",
9
+ "version": "0.1.0-alpha.768",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.78.0",
12
12
  "@azure/identity": "^4.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.767",
3
+ "version": "0.1.0-alpha.768",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },