@ouro.bot/cli 0.1.0-alpha.746 → 0.1.0-alpha.748

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,20 @@
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.748",
6
+ "changes": [
7
+ "Reject any unexpected running Ouro image or writable Sanctuary-root overlap, including legacy and package-managed layouts, across zero-poller and active-poller deployment handoffs.",
8
+ "Remove the exact legacy Unraid boot hook that rewrites the managed Usenet cron entry after reboot."
9
+ ]
10
+ },
11
+ {
12
+ "version": "0.1.0-alpha.747",
13
+ "changes": [
14
+ "Create every nested host-backup directory as root-private before provenance verification.",
15
+ "Accept a missing OCI source label only on Sanctuary's exact historical alpha.742 image while retaining strict source provenance for every other image."
16
+ ]
17
+ },
4
18
  {
5
19
  "version": "0.1.0-alpha.746",
6
20
  "changes": [
@@ -141,7 +141,8 @@ Effective-spec audit helper:
141
141
  return 1
142
142
  }
143
143
  Define these fail-closed image and topology helpers in the same shell. They use
144
- exact Docker image IDs and exact container names; no mutation is performed:
144
+ exact Docker image IDs and container identity, image provenance, and writable
145
+ root overlap; no mutation is performed:
145
146
  validate_exact_image_id() {
146
147
  VALIDATE_IMAGE_ID=$1
147
148
  case "$VALIDATE_IMAGE_ID" in
@@ -155,18 +156,43 @@ Effective-spec audit helper:
155
156
  docker image inspect "$VALIDATE_IMAGE_ID" >/dev/null || return $?
156
157
  }
157
158
  assert_only_running_butler() {
159
+ (
158
160
  EXPECTED_RUNNING_BUTLER=$1
159
- RUNNING_BUTLER_NAMES=$(docker container ls --format '{{.Names}}') || return $?
160
- RUNNING_BUTLER_COUNTS=$(printf '%s\n' "$RUNNING_BUTLER_NAMES" | awk -v expected="$EXPECTED_RUNNING_BUTLER" '
161
- {
162
- if ($0 == "ouro-butler" || $0 == "ouro-butler-staging" || $0 == "ouro-butler-rollback" || $0 == "ouro-butler-legacy-evidence") {
163
- butlers++
164
- if ($0 == expected) matching++
161
+ case "$EXPECTED_RUNNING_BUTLER" in
162
+ -) EXPECTED_RUNNING_BUTLER_COUNT=0 ;;
163
+ ouro-butler|ouro-butler-staging) EXPECTED_RUNNING_BUTLER_COUNT=1 ;;
164
+ *) return 1 ;;
165
+ esac
166
+ RUNNING_CONTAINER_IDS=$(docker container ls -q) || return $?
167
+ MATCHING_RUNNING_BUTLERS=0
168
+ for RUNNING_CONTAINER_ID in $RUNNING_CONTAINER_IDS; do
169
+ RUNNING_CONTAINER_NAME=$(docker container inspect --format '{{.Name}}' "$RUNNING_CONTAINER_ID") || return $?
170
+ RUNNING_CONTAINER_NAME=${RUNNING_CONTAINER_NAME#/}
171
+ if test "$RUNNING_CONTAINER_NAME" = "$EXPECTED_RUNNING_BUTLER"; then
172
+ MATCHING_RUNNING_BUTLERS=$(( MATCHING_RUNNING_BUTLERS + 1 ))
173
+ continue
174
+ fi
175
+ RUNNING_CONTAINER_IMAGE_ID=$(docker container inspect --format '{{.Image}}' "$RUNNING_CONTAINER_ID") || return $?
176
+ RUNNING_CONTAINER_IMAGE_REF=$(docker container inspect --format '{{.Config.Image}}' "$RUNNING_CONTAINER_ID") || return $?
177
+ RUNNING_CONTAINER_IMAGE_SOURCE=$(docker image inspect --format '{{with .Config.Labels}}{{index . "org.opencontainers.image.source"}}{{end}}' "$RUNNING_CONTAINER_IMAGE_ID") || return $?
178
+ test "$RUNNING_CONTAINER_IMAGE_SOURCE" != https://github.com/ourostack/ouroboros || return 1
179
+ test "$RUNNING_CONTAINER_IMAGE_ID" != sha256:681449ad47a2621705cd339b481e6339236b31dc65e195b1cf5025d0f2191d7d || return 1
180
+ case "$RUNNING_CONTAINER_IMAGE_REF" in
181
+ ouro-butler|ouro-butler:*|ouro-butler@sha256:*|ouroboros-butler|ouroboros-butler:*|ouroboros-butler@sha256:*|ghcr.io/ourostack/ouroboros-butler|ghcr.io/ourostack/ouroboros-butler:*|ghcr.io/ourostack/ouroboros-butler@sha256:*) return 1 ;;
182
+ esac
183
+ RUNNING_WRITABLE_MOUNT_SOURCES=$(docker container inspect --format '{{range .Mounts}}{{if .RW}}{{println .Source}}{{end}}{{end}}' "$RUNNING_CONTAINER_ID") || return $?
184
+ if printf '%s\n' "$RUNNING_WRITABLE_MOUNT_SOURCES" | awk '
185
+ function overlaps(path, root) {
186
+ return length(path) > 0 && (path == root || index(path, root "/") == 1 || index(root, path "/") == 1)
165
187
  }
166
- }
167
- END { printf "%d %d", butlers + 0, matching + 0 }
168
- ') || return $?
169
- test "$RUNNING_BUTLER_COUNTS" = "1 1" || return $?
188
+ overlaps($0, "/mnt/user/appdata/ouro-butler/runtime") || overlaps($0, "/mnt/user/appdata/ouro-butler/runtime/.ouro-cli") || overlaps($0, "/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro") || overlaps($0, "/mnt/user/appdata/ouro-butler/AgentBundles") { found = 1 }
189
+ END { exit found ? 0 : 1 }
190
+ '; then
191
+ return 1
192
+ fi
193
+ done
194
+ test "$MATCHING_RUNNING_BUTLERS" -eq "$EXPECTED_RUNNING_BUTLER_COUNT" || return $?
195
+ )
170
196
  }
171
197
  assert_update_topology() {
172
198
  EXPECTED_ROLLBACK_IMAGE_ID=$1
@@ -311,7 +337,9 @@ Effective-spec audit helper:
311
337
  const image = JSON.parse(fs.readFileSync(imagePath, "utf8"));
312
338
  if (!Array.isArray(container) || container.length !== 1 || !container[0] || container[0].Image !== imageId) process.exit(1);
313
339
  if (!Array.isArray(image) || image.length !== 1 || !image[0] || image[0].Id !== imageId) process.exit(1);
314
- if (image[0].Config?.Labels?.["org.opencontainers.image.source"] !== "https://github.com/ourostack/ouroboros") process.exit(1);
340
+ const source = image[0].Config?.Labels?.["org.opencontainers.image.source"];
341
+ const legacyAlpha742 = "sha256:681449ad47a2621705cd339b481e6339236b31dc65e195b1cf5025d0f2191d7d";
342
+ if (source !== "https://github.com/ourostack/ouroboros" && !(source === undefined && imageId === legacyAlpha742)) process.exit(1);
315
343
  const expected = ["custom/usenet_health.sh", "custom/ouro-events/bootstrap-spool.sh", "custom/ouro-events/emit-event.mjs", "custom/ouro-events/emit-usenet-event.sh", "custom/ouro-events/install-usenet-guard.sh", "go.butler-lines", "crontab.butler-lines", "global-state"];
316
344
  const expectedUid = 0;
317
345
  const expectedGid = 0;
@@ -784,6 +812,7 @@ Effective-spec audit helper:
784
812
  && CURRENT_LEGACY_EVIDENCE_IMAGE_ID=$(docker inspect --format '{{.Image}}' ouro-butler-legacy-evidence) \
785
813
  && test "$CURRENT_LEGACY_EVIDENCE_IMAGE_ID" = "$LEGACY_STAGING_IMAGE_ID" \
786
814
  && test "$(docker inspect --format '{{.State.Running}}' ouro-butler-legacy-evidence)" = false \
815
+ && assert_only_running_butler - \
787
816
  && docker create --name ouro-butler-staging --network host --restart unless-stopped --user 10001:10001 \
788
817
  --mount "type=bind,src=/mnt/user/appdata/ouro-butler/runtime/.ouro-cli,dst=/home/ouro/.ouro-cli" \
789
818
  --mount "type=bind,src=/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro,dst=/home/ouro/AgentBundles/sanctuary.ouro" \
@@ -797,6 +826,7 @@ Effective-spec audit helper:
797
826
  && docker stop ouro-butler-staging \
798
827
  && test "$(docker inspect --format '{{.Image}}' ouro-butler-staging)" = "$IMAGE_ID" \
799
828
  && docker rm ouro-butler-staging \
829
+ && assert_only_running_butler - \
800
830
  && docker create --name ouro-butler --network host --restart unless-stopped --user 10001:10001 \
801
831
  --mount "type=bind,src=/mnt/user/appdata/ouro-butler/runtime/.ouro-cli,dst=/home/ouro/.ouro-cli" \
802
832
  --mount "type=bind,src=/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro,dst=/home/ouro/AgentBundles/sanctuary.ouro" \
@@ -1360,6 +1390,7 @@ Update:
1360
1390
  cmp -s "$EVENT_SCRIPT_STAGE/install-usenet-guard.sh" /boot/config/custom/ouro-events/install-usenet-guard.sh
1361
1391
  test "$(grep -Fxc '/bin/bash /boot/config/custom/ouro-events/install-usenet-guard.sh --boot --install-root /boot/config/custom' /boot/config/go)" = 1
1362
1392
  test "$(grep -Fxc '/boot/config/custom/ouro-events/bootstrap-spool.sh --mount' /boot/config/go || true)" = 0
1393
+ test "$(grep -Fxc '(crontab -l 2>/dev/null | grep -v usenet_health; echo "*/15 * * * * /bin/bash /boot/config/custom/usenet_health.sh") | crontab -' /boot/config/go || true)" = 0
1363
1394
  INSTALLED_GUARD_CRON=$(crontab -l)
1364
1395
  test "$(printf '%s\n' "$INSTALLED_GUARD_CRON" | grep -Fxc '*/15 * * * * /bin/bash /boot/config/custom/usenet_health.sh # ouro:usenet-health')" = 1
1365
1396
  INSTALLED_LEGACY_GUARD_COUNT=$(printf '%s\n' "$INSTALLED_GUARD_CRON" | grep -Fxc '*/15 * * * * /bin/bash /boot/config/custom/usenet_health.sh' || true)
@@ -1509,6 +1540,7 @@ ouro-butler-rollback
1509
1540
  && remove_stopped_rollback_if_present "$ROLLBACK_IMAGE_ID" \
1510
1541
  && docker rename ouro-butler ouro-butler-rollback \
1511
1542
  && test "$(docker inspect --format '{{.State.Running}}' ouro-butler-rollback)" = false \
1543
+ && assert_only_running_butler - \
1512
1544
  && migrate_sanctuary_package_managed_bundle "$IMAGE_ID" migrate "$ROLLBACK_IMAGE_ID"; then
1513
1545
  :
1514
1546
  else
@@ -1564,7 +1596,8 @@ ouro-butler-rollback
1564
1596
  && assert_only_running_butler ouro-butler-staging \
1565
1597
  && wait_butler_ready ouro-butler-staging \
1566
1598
  && docker stop ouro-butler-staging \
1567
- && docker rm ouro-butler-staging; then
1599
+ && docker rm ouro-butler-staging \
1600
+ && assert_only_running_butler -; then
1568
1601
  :
1569
1602
  else
1570
1603
  STAGING_ACTIVATION_STATUS=$?
@@ -1786,7 +1819,7 @@ Backup:
1786
1819
  if docker stop ouro-butler \
1787
1820
  && test "$(docker inspect --format '{{.State.Running}}' ouro-butler)" = false \
1788
1821
  && install -d -m 0700 -o 0 -g 0 "$BACKUP_TMP" "$BACKUP_TMP/runtime" "$BACKUP_TMP/agent" "$BACKUP_TMP/host" "$BACKUP_TMP/provenance" \
1789
- && install -d -m 0700 -o 0 -g 0 "$BACKUP_TMP/host/custom/ouro-events" \
1822
+ && install -d -m 0700 -o 0 -g 0 "$BACKUP_TMP/host/custom" "$BACKUP_TMP/host/custom/ouro-events" \
1790
1823
  && install -m 0600 -o 0 -g 0 /dev/null "$BACKUP_TMP/host/inventory" \
1791
1824
  && backup_host_file /boot/config/custom/usenet_health.sh custom/usenet_health.sh \
1792
1825
  && backup_host_file /boot/config/custom/ouro-events/bootstrap-spool.sh custom/ouro-events/bootstrap-spool.sh \
@@ -49,6 +49,7 @@ LEGACY_BOOT_HOOK="/boot/config/custom/ouro-events/bootstrap-spool.sh --mount"
49
49
  LEGACY_BASH_BOOT_HOOK="/bin/bash /boot/config/custom/ouro-events/bootstrap-spool.sh --mount"
50
50
  CANONICAL_PREVIOUS_BOOT_HOOK="/bin/bash /boot/config/custom/ouro-events/install-usenet-guard.sh --boot"
51
51
  CANONICAL_BOOT_HOOK="$CANONICAL_PREVIOUS_BOOT_HOOK --install-root /boot/config/custom"
52
+ LEGACY_CRON_BOOT_HOOK='(crontab -l 2>/dev/null | grep -v usenet_health; echo "*/15 * * * * /bin/bash /boot/config/custom/usenet_health.sh") | crontab -'
52
53
  printf -v HEALTH_SCRIPT_Q '%q' "$INSTALL_ROOT/usenet_health.sh"
53
54
  CRON_LINE="*/15 * * * * /bin/bash $HEALTH_SCRIPT_Q # ouro:usenet-health"
54
55
  LEGACY_CRON_LINE="*/15 * * * * /bin/bash $HEALTH_SCRIPT_Q"
@@ -125,7 +126,7 @@ render_inactive_cron() {
125
126
 
126
127
  is_canonical_butler_go_hook() {
127
128
  case "$1" in
128
- "$LEGACY_BOOT_HOOK"|"$LEGACY_BASH_BOOT_HOOK"|"$CANONICAL_PREVIOUS_BOOT_HOOK"|"$CANONICAL_BOOT_HOOK") return 0 ;;
129
+ "$LEGACY_BOOT_HOOK"|"$LEGACY_BASH_BOOT_HOOK"|"$CANONICAL_PREVIOUS_BOOT_HOOK"|"$CANONICAL_BOOT_HOOK"|"$PREVIOUS_INSTALLER_BOOT_HOOK"|"$BOOT_HOOK"|"$LEGACY_CRON_BOOT_HOOK") return 0 ;;
129
130
  *) return 1 ;;
130
131
  esac
131
132
  }
@@ -232,7 +233,7 @@ install_transaction() {
232
233
  printf '#!/bin/bash\n' > "$stage/go.base"
233
234
  fi
234
235
  go_candidate="$stage/go.candidate"
235
- grep -Fvx -e "$LEGACY_BOOT_HOOK" -e "$LEGACY_BASH_BOOT_HOOK" -e "$PREVIOUS_INSTALLER_BOOT_HOOK" -e "$BOOT_HOOK" "$stage/go.base" > "$stage/go.filtered" || true
236
+ render_go_without_owned "$stage/go.base" "$stage/go.filtered"
236
237
  if [ -s "$stage/go.filtered" ]; then
237
238
  awk -v hook="$BOOT_HOOK" 'NR == 1 && /^#!/ { print; print hook; next } NR == 1 { print hook } { print }' "$stage/go.filtered" > "$go_candidate"
238
239
  else
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.746",
2
+ "runtimeVersion": "0.1.0-alpha.748",
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.746</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.748</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.746",
3
+ "version": "0.1.0-alpha.748",
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.746",
9
+ "version": "0.1.0-alpha.748",
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.746",
3
+ "version": "0.1.0-alpha.748",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },