@ouro.bot/cli 0.1.0-alpha.761 → 0.1.0-alpha.763
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 +12 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +23 -17
- package/deploy/unraid/sanctuary-unit16-run.sh +28 -4
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.xml +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
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.763",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Keep the Unit 16 host broker response channel open until asynchronous operations return a bounded success or failure envelope."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.762",
|
|
12
|
+
"changes": [
|
|
13
|
+
"Wait for the exact audited Sanctuary container and process to recover health before Unit 16 acceptance continues."
|
|
14
|
+
]
|
|
15
|
+
},
|
|
4
16
|
{
|
|
5
17
|
"version": "0.1.0-alpha.761",
|
|
6
18
|
"changes": [
|
|
@@ -1707,21 +1707,9 @@ function writeClosedInventory(file) {
|
|
|
1707
1707
|
chownSync(file, 0, 0)
|
|
1708
1708
|
}
|
|
1709
1709
|
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
const
|
|
1713
|
-
if (!profileName || !targetContainerId || !socket || !closedInventory || !expectedImage || !initialSnapshot || process.argv.length !== 8) throw new Error("usage: broker <staging|final> <target-container-id> <socket> <closed-inventory> <expected-image-id> <initial-snapshot>")
|
|
1714
|
-
activeProfile = targetProfile(profileName)
|
|
1715
|
-
activeContainer = activeProfile.containerName
|
|
1716
|
-
activeContainerId = text(targetContainerId, "attested target container id", SHA256)
|
|
1717
|
-
expectedImageId = text(expectedImage, "expected image id", IMAGE_ID)
|
|
1718
|
-
rmSync(socket, { force: true })
|
|
1719
|
-
writeClosedInventory(closedInventory)
|
|
1720
|
-
const snapshot = await containerSnapshot(expectedImageId)
|
|
1721
|
-
const snapshotFd = openSync(initialSnapshot, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600)
|
|
1722
|
-
try { writeFileSync(snapshotFd, `${JSON.stringify(snapshot)}\n`); fsyncSync(snapshotFd) } finally { closeSync(snapshotFd) }
|
|
1723
|
-
chownSync(initialSnapshot, 0, 0)
|
|
1724
|
-
const server = createServer((connection) => {
|
|
1710
|
+
function createBrokerServer(dispatchRequest = dispatch) {
|
|
1711
|
+
const dispatchDrain = createDispatchDrain()
|
|
1712
|
+
const server = createServer({ allowHalfOpen: true }, (connection) => {
|
|
1725
1713
|
let input = ""
|
|
1726
1714
|
connection.setEncoding("utf8")
|
|
1727
1715
|
connection.on("data", (chunk) => {
|
|
@@ -1730,14 +1718,31 @@ async function main() {
|
|
|
1730
1718
|
})
|
|
1731
1719
|
connection.on("end", async () => {
|
|
1732
1720
|
try {
|
|
1733
|
-
const result = await dispatchDrain.run(() =>
|
|
1721
|
+
const result = await dispatchDrain.run(() => dispatchRequest(JSON.parse(input)))
|
|
1734
1722
|
const scenario = result?.[ASYNC_RESTART_SCENARIO]
|
|
1735
1723
|
const completed = scenario ? armRestartAfterResponseClosed(connection, scenario) : undefined
|
|
1736
1724
|
connection.end(`${JSON.stringify({ ok: true, result })}\n`, completed)
|
|
1737
1725
|
} catch { connection.end(`${JSON.stringify({ ok: false, error: "host operation failed" })}\n`) }
|
|
1738
1726
|
})
|
|
1739
1727
|
})
|
|
1740
|
-
|
|
1728
|
+
return { server, dispatchDrain }
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
async function main() {
|
|
1732
|
+
if (process.getuid?.() !== 0) throw new Error("host broker must run as root")
|
|
1733
|
+
const [profileName, targetContainerId, socket, closedInventory, expectedImage, initialSnapshot] = process.argv.slice(2)
|
|
1734
|
+
if (!profileName || !targetContainerId || !socket || !closedInventory || !expectedImage || !initialSnapshot || process.argv.length !== 8) throw new Error("usage: broker <staging|final> <target-container-id> <socket> <closed-inventory> <expected-image-id> <initial-snapshot>")
|
|
1735
|
+
activeProfile = targetProfile(profileName)
|
|
1736
|
+
activeContainer = activeProfile.containerName
|
|
1737
|
+
activeContainerId = text(targetContainerId, "attested target container id", SHA256)
|
|
1738
|
+
expectedImageId = text(expectedImage, "expected image id", IMAGE_ID)
|
|
1739
|
+
rmSync(socket, { force: true })
|
|
1740
|
+
writeClosedInventory(closedInventory)
|
|
1741
|
+
const snapshot = await containerSnapshot(expectedImageId)
|
|
1742
|
+
const snapshotFd = openSync(initialSnapshot, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600)
|
|
1743
|
+
try { writeFileSync(snapshotFd, `${JSON.stringify(snapshot)}\n`); fsyncSync(snapshotFd) } finally { closeSync(snapshotFd) }
|
|
1744
|
+
chownSync(initialSnapshot, 0, 0)
|
|
1745
|
+
const { server, dispatchDrain } = createBrokerServer()
|
|
1741
1746
|
server.listen(socket, () => {
|
|
1742
1747
|
chownSync(socket, 0, 10001)
|
|
1743
1748
|
chmodSync(socket, 0o660)
|
|
@@ -1769,6 +1774,7 @@ export {
|
|
|
1769
1774
|
armRestartAfterResponseClosed,
|
|
1770
1775
|
completeHealthProbeFromReceipt,
|
|
1771
1776
|
createDispatchDrain,
|
|
1777
|
+
createBrokerServer,
|
|
1772
1778
|
createHealthProbeOperationCoordinator,
|
|
1773
1779
|
createOwnerMutationCoordinator,
|
|
1774
1780
|
createInteractiveRestartDriver,
|
|
@@ -129,14 +129,18 @@ start_broker() {
|
|
|
129
129
|
/usr/local/bin/node "$TARGET_AUDITOR" "$TARGET_PROFILE" "$IMAGE_ID" >"$PRIVATE_ROOT/deployment-target.json"
|
|
130
130
|
chmod 0400 "$PRIVATE_ROOT/deployment-target.json"
|
|
131
131
|
chown 0:0 "$PRIVATE_ROOT/deployment-target.json"
|
|
132
|
-
|
|
132
|
+
TARGET_CONTAINER_BINDING=$(/usr/local/bin/node -e '
|
|
133
133
|
const fs = require("node:fs"); const value = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); const deployment = value && value.deployment;
|
|
134
134
|
if (value?.schemaVersion !== "sanctuary-effective-deployment-v1" || deployment?.schemaVersion !== "sanctuary-deployment-target-v1"
|
|
135
135
|
|| deployment.profile !== process.argv[3] || deployment.targetContainerName !== process.argv[4]
|
|
136
|
-
|| deployment.targetImageId !== process.argv[2] || !/^[0-9a-f]{64}$/.test(deployment.targetContainerId)
|
|
137
|
-
|
|
136
|
+
|| deployment.targetImageId !== process.argv[2] || !/^[0-9a-f]{64}$/.test(deployment.targetContainerId)
|
|
137
|
+
|| !Number.isSafeInteger(deployment.targetPid) || deployment.targetPid <= 0 || deployment.targetPid > 4_194_304) process.exit(1);
|
|
138
|
+
process.stdout.write(`${deployment.targetContainerId} ${deployment.targetPid}`);
|
|
138
139
|
' "$PRIVATE_ROOT/deployment-target.json" "$IMAGE_ID" "$TARGET_PROFILE" "$PRODUCTION_CONTAINER")
|
|
139
|
-
|
|
140
|
+
set -- $TARGET_CONTAINER_BINDING
|
|
141
|
+
test "$#" -eq 2 || return 1
|
|
142
|
+
TARGET_CONTAINER_ID=$1
|
|
143
|
+
TARGET_CONTAINER_PID=$2
|
|
140
144
|
/usr/local/bin/node "$BROKER_PROGRAM" "$TARGET_PROFILE" "$TARGET_CONTAINER_ID" "$BROKER_SOCKET" "$CLOSED_INVENTORY" "$IMAGE_ID" "$BROKER_SNAPSHOT" </dev/null >/dev/null 2>&1 &
|
|
141
145
|
BROKER_PID=$!
|
|
142
146
|
ATTEMPT=0
|
|
@@ -149,6 +153,26 @@ start_broker() {
|
|
|
149
153
|
test "$(stat -c '%u:%g %a' "$BROKER_SOCKET")" = "0:10001 660" || return 1
|
|
150
154
|
test "$(stat -c '%u:%g %a' "$CLOSED_INVENTORY")" = "0:0 600" || return 1
|
|
151
155
|
test "$(stat -c '%u:%g %a' "$BROKER_SNAPSHOT")" = "0:0 600" || return 1
|
|
156
|
+
await_post_audit_health
|
|
157
|
+
refresh_live_facts
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await_post_audit_health() {
|
|
161
|
+
POST_AUDIT_ATTEMPT=0
|
|
162
|
+
while test "$POST_AUDIT_ATTEMPT" -lt 120; do
|
|
163
|
+
POST_AUDIT_STATE=$(/usr/bin/timeout -s KILL 20 /usr/bin/docker inspect --format '{{.Id}} {{.Name}} {{.Image}} {{.State.Running}} {{.State.Paused}} {{.State.Restarting}} {{.State.Dead}} {{.State.Pid}} {{if .State.Health}}{{.State.Health.Status}}{{else}}missing{{end}}' "$TARGET_CONTAINER_ID") || return 1
|
|
164
|
+
set -- $POST_AUDIT_STATE
|
|
165
|
+
test "$#" -eq 9 || return 1
|
|
166
|
+
test "$1" = "$TARGET_CONTAINER_ID" && test "$2" = "/$PRODUCTION_CONTAINER" && test "$3" = "$IMAGE_ID" || return 1
|
|
167
|
+
test "$4" = true && test "$6" = false && test "$7" = false || return 1
|
|
168
|
+
test "$8" = "$TARGET_CONTAINER_PID" || return 1
|
|
169
|
+
if test "$5" = false && test "$9" = healthy; then return 0; fi
|
|
170
|
+
test "$5" = true || test "$5" = false || return 1
|
|
171
|
+
test "$9" = starting || test "$9" = unhealthy || return 1
|
|
172
|
+
POST_AUDIT_ATTEMPT=$((POST_AUDIT_ATTEMPT + 1))
|
|
173
|
+
sleep 1
|
|
174
|
+
done
|
|
175
|
+
return 1
|
|
152
176
|
}
|
|
153
177
|
|
|
154
178
|
prepare_live_facts() {
|
|
@@ -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.
|
|
4
|
+
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.763</Repository>
|
|
5
5
|
<Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
|
|
6
6
|
<Network>host</Network>
|
|
7
7
|
<Shell>sh</Shell>
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.763",
|
|
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.
|
|
9
|
+
"version": "0.1.0-alpha.763",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
12
12
|
"@azure/identity": "^4.13.0",
|