@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
|
@@ -132,7 +132,6 @@ IMAGE_FACT=$PRIVATE_ROOT/image-digest
|
|
|
132
132
|
CONTAINER_FACT=$PRIVATE_ROOT/container-digest
|
|
133
133
|
PROCESS_BINDING_FACT=$PRIVATE_ROOT/process-binding-digest
|
|
134
134
|
HEALTH_FACT=$PRIVATE_ROOT/postboot-health.json
|
|
135
|
-
POLLER_FACT=$PRIVATE_ROOT/telegram-poller-count.json
|
|
136
135
|
CONTAINER_INSPECT_FACT=$PRIVATE_ROOT/container-inspect.json
|
|
137
136
|
|
|
138
137
|
start_broker() {
|
|
@@ -280,13 +279,6 @@ stop_exact_production_container() {
|
|
|
280
279
|
test "$(/usr/bin/timeout -s KILL 20 /usr/bin/docker inspect --format '{{.State.Pid}}' "$EXPECTED_CONTAINER_ID")" = 0 || return 1
|
|
281
280
|
}
|
|
282
281
|
|
|
283
|
-
quiesce_production_telegram_poller() {
|
|
284
|
-
stop_exact_production_container
|
|
285
|
-
printf '%s\n' '{"activePollers":0,"productionContainerStopped":true}' >"$POLLER_FACT"
|
|
286
|
-
chmod 0444 "$POLLER_FACT"
|
|
287
|
-
chown 0:0 "$POLLER_FACT"
|
|
288
|
-
}
|
|
289
|
-
|
|
290
282
|
materialize_config() {
|
|
291
283
|
OUTPUT=$1
|
|
292
284
|
SNAPSHOT_PHASE=${2:-}
|
|
@@ -297,8 +289,9 @@ materialize_config() {
|
|
|
297
289
|
EXTRA_MOUNT=$PRIVATE_ROOT/container-inventory.json
|
|
298
290
|
fi
|
|
299
291
|
if test -n "$EXTRA_MOUNT"; then
|
|
300
|
-
/usr/bin/timeout -s KILL
|
|
292
|
+
/usr/bin/timeout -s KILL 90 /usr/bin/docker run --rm --pull=never --network none \
|
|
301
293
|
--user 10001:10001 --read-only \
|
|
294
|
+
--mount "type=bind,src=/run/ouro-authority,dst=/run/ouro-authority,readonly" \
|
|
302
295
|
--mount "type=bind,src=$EVIDENCE_ROOT,dst=/evidence,readonly" \
|
|
303
296
|
--mount "type=bind,src=$EXTRA_MOUNT,dst=/run/ouro-acceptance/closed-inventory.json,readonly" \
|
|
304
297
|
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli,readonly" \
|
|
@@ -306,16 +299,18 @@ materialize_config() {
|
|
|
306
299
|
--entrypoint /opt/ouro/deploy/unraid/sanctuary-acceptance-adapter.sh \
|
|
307
300
|
"$IMAGE_ID" materialize-config "$COMMAND" >"$OUTPUT"
|
|
308
301
|
elif test -n "$SNAPSHOT_PHASE"; then
|
|
309
|
-
/usr/bin/timeout -s KILL
|
|
302
|
+
/usr/bin/timeout -s KILL 90 /usr/bin/docker run --rm --pull=never --network none \
|
|
310
303
|
--user 10001:10001 --read-only \
|
|
304
|
+
--mount "type=bind,src=/run/ouro-authority,dst=/run/ouro-authority,readonly" \
|
|
311
305
|
--mount "type=bind,src=$EVIDENCE_ROOT,dst=/evidence,readonly" \
|
|
312
306
|
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli,readonly" \
|
|
313
307
|
--mount "type=bind,src=$BUNDLE_ROOT,dst=/home/ouro/AgentBundles/sanctuary.ouro,readonly" \
|
|
314
308
|
--entrypoint /opt/ouro/deploy/unraid/sanctuary-acceptance-adapter.sh \
|
|
315
309
|
"$IMAGE_ID" materialize-config "$COMMAND" "$SNAPSHOT_PHASE" >"$OUTPUT"
|
|
316
310
|
else
|
|
317
|
-
/usr/bin/timeout -s KILL
|
|
311
|
+
/usr/bin/timeout -s KILL 90 /usr/bin/docker run --rm --pull=never --network none \
|
|
318
312
|
--user 10001:10001 --read-only \
|
|
313
|
+
--mount "type=bind,src=/run/ouro-authority,dst=/run/ouro-authority,readonly" \
|
|
319
314
|
--mount "type=bind,src=$EVIDENCE_ROOT,dst=/evidence,readonly" \
|
|
320
315
|
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli,readonly" \
|
|
321
316
|
--mount "type=bind,src=$BUNDLE_ROOT,dst=/home/ouro/AgentBundles/sanctuary.ouro,readonly" \
|
|
@@ -362,11 +357,11 @@ materialize_config "$EXPECTED_CONFIG" "$SNAPSHOT_PHASE"
|
|
|
362
357
|
cmp -s "$EXPECTED_CONFIG" "$CONFIG_PATH" || exit 1
|
|
363
358
|
case "$COMMAND" in callback-inject) test -r /proc/self/fd/3 || exit 2 ;; esac
|
|
364
359
|
prepare_live_facts
|
|
365
|
-
if test "$COMMAND" = telegram-bootstrap; then
|
|
360
|
+
if test "$COMMAND" = telegram-bootstrap; then stop_exact_production_container; fi
|
|
366
361
|
if test "$COMMAND" = unraid-key-rotate; then stop_exact_production_container; fi
|
|
367
362
|
|
|
368
363
|
case "$COMMAND" in
|
|
369
|
-
telegram-bootstrap) TIME_LIMIT=900; NETWORK=
|
|
364
|
+
telegram-bootstrap) TIME_LIMIT=900; NETWORK=none; INPUT=no; BUNDLE_MODE=readonly; BROKER=no ;;
|
|
370
365
|
callback-inject) TIME_LIMIT=120; NETWORK=host; INPUT=yes; BUNDLE_MODE=rw; BROKER=no ;;
|
|
371
366
|
unraid-key-rotate) TIME_LIMIT=600; NETWORK=host; INPUT=no; BUNDLE_MODE=readonly; BROKER=yes ;;
|
|
372
367
|
evidence-snapshot) TIME_LIMIT=5700; NETWORK=host; INPUT=no; BUNDLE_MODE=readonly; BROKER=yes ;;
|
|
@@ -426,11 +421,12 @@ run_harness() {
|
|
|
426
421
|
if test "$COMMAND" = telegram-bootstrap; then
|
|
427
422
|
/usr/bin/timeout -s KILL "$TIME_LIMIT" /usr/bin/docker run --rm --pull=never --network "$NETWORK" \
|
|
428
423
|
--user 10001:10001 --read-only --cap-drop ALL --security-opt no-new-privileges \
|
|
424
|
+
--mount "type=bind,src=/run/ouro-authority,dst=/run/ouro-authority,readonly" \
|
|
429
425
|
--mount "type=bind,src=$CONFIG_PATH,dst=/run/ouro-acceptance/config.json,readonly" \
|
|
430
426
|
--mount "type=bind,src=$EVIDENCE_ROOT,dst=/evidence" \
|
|
431
|
-
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli" \
|
|
427
|
+
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli,readonly" \
|
|
432
428
|
--mount "type=bind,src=$BUNDLE_ROOT,dst=/home/ouro/AgentBundles/sanctuary.ouro$BUNDLE_SUFFIX" \
|
|
433
|
-
--mount "type=bind,src=$
|
|
429
|
+
--mount "type=bind,src=$SOCKET_ROOT,dst=/run/ouro-host-acceptance,readonly" \
|
|
434
430
|
--mount "type=bind,src=$IMAGE_FACT,dst=/run/ouro-acceptance/image-digest,readonly" \
|
|
435
431
|
--mount "type=bind,src=$CONTAINER_FACT,dst=/run/ouro-acceptance/container-digest,readonly" \
|
|
436
432
|
--mount "type=bind,src=$PROCESS_BINDING_FACT,dst=/run/ouro-acceptance/process-binding-digest,readonly" \
|
|
@@ -442,6 +438,7 @@ run_harness() {
|
|
|
442
438
|
elif test "$COMMAND" = callback-inject; then
|
|
443
439
|
/usr/bin/timeout -s KILL "$TIME_LIMIT" /usr/bin/docker run --rm -i --pull=never --network "$NETWORK" \
|
|
444
440
|
--user 10001:10001 --read-only --cap-drop ALL --security-opt no-new-privileges \
|
|
441
|
+
--mount "type=bind,src=/run/ouro-authority,dst=/run/ouro-authority,readonly" \
|
|
445
442
|
--mount "type=bind,src=$CONFIG_PATH,dst=/run/ouro-acceptance/config.json,readonly" \
|
|
446
443
|
--mount "type=bind,src=$EVIDENCE_ROOT,dst=/evidence" \
|
|
447
444
|
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli,readonly" \
|
|
@@ -457,6 +454,7 @@ run_harness() {
|
|
|
457
454
|
elif test "$COMMAND" = evidence-snapshot; then
|
|
458
455
|
/usr/bin/timeout -s KILL "$TIME_LIMIT" /usr/bin/docker run --rm --pull=never --network "$NETWORK" \
|
|
459
456
|
--user 10001:10001 --read-only --cap-drop ALL --security-opt no-new-privileges \
|
|
457
|
+
--mount "type=bind,src=/run/ouro-authority,dst=/run/ouro-authority,readonly" \
|
|
460
458
|
--mount "type=bind,src=$CONFIG_PATH,dst=/run/ouro-acceptance/config.json,readonly" \
|
|
461
459
|
--mount "type=bind,src=$EVIDENCE_ROOT,dst=/evidence" \
|
|
462
460
|
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli,readonly" \
|
|
@@ -475,6 +473,7 @@ run_harness() {
|
|
|
475
473
|
if test "$COMMAND" = reboot-request || test "$COMMAND" = reboot-resume; then ACCEPTANCE_MOUNT="--mount type=bind,src=$ACCEPTANCE_PIN_ROOT,dst=/home/ouro/AgentBundles/sanctuary.ouro/state/acceptance"; else ACCEPTANCE_MOUNT=; fi
|
|
476
474
|
/usr/bin/timeout -s KILL "$TIME_LIMIT" /usr/bin/docker run --rm --pull=never --network "$NETWORK" \
|
|
477
475
|
--user 10001:10001 --read-only --cap-drop ALL --security-opt no-new-privileges \
|
|
476
|
+
--mount "type=bind,src=/run/ouro-authority,dst=/run/ouro-authority,readonly" \
|
|
478
477
|
--mount "type=bind,src=$CONFIG_PATH,dst=/run/ouro-acceptance/config.json,readonly" \
|
|
479
478
|
--mount "type=bind,src=$EVIDENCE_ROOT,dst=/evidence" \
|
|
480
479
|
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli,readonly" \
|
|
@@ -492,6 +491,7 @@ run_harness() {
|
|
|
492
491
|
else
|
|
493
492
|
/usr/bin/timeout -s KILL "$TIME_LIMIT" /usr/bin/docker run --rm --pull=never --network "$NETWORK" \
|
|
494
493
|
--user 10001:10001 --read-only --cap-drop ALL --security-opt no-new-privileges \
|
|
494
|
+
--mount "type=bind,src=/run/ouro-authority,dst=/run/ouro-authority,readonly" \
|
|
495
495
|
--mount "type=bind,src=$CONFIG_PATH,dst=/run/ouro-acceptance/config.json,readonly" \
|
|
496
496
|
--mount "type=bind,src=$EVIDENCE_ROOT,dst=/evidence" \
|
|
497
497
|
--mount "type=bind,src=$RUNTIME_ROOT,dst=/home/ouro/.ouro-cli,readonly" \
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 2,
|
|
3
3
|
"profiles": {
|
|
4
4
|
"sanctuary-owner": {
|
|
5
|
-
"version":
|
|
5
|
+
"version": 9,
|
|
6
6
|
"contextScopes": ["household.status", "household.policy", "household.private"],
|
|
7
|
-
"toolNames": ["external_event_disposition", "query_active_work", "save_friend_note", "telegram_contact_manage", "query_cares", "care_manage", "await_condition", "resolve_await", "cancel_await", "list_recent_attachments", "materialize_attachment", "describe_image", "unraid_list_containers", "unraid_get_container_logs", "unraid_get_storage", "sanctuary_get_media_optimization", "sanctuary_search_media_catalog", "unraid_get_disks", "unraid_get_notifications", "unraid_get_system", "sanctuary_get_install_state", "unraid_check_services", "sanctuary_get_download_queue", "sanctuary_resume_download_queue", "unraid_restart_container", "steward_policy_manage", "send_message", "ponder", "rest", "settle", "speak", "shell", "shell_status", "shell_tail", "read_file", "write_file", "edit_file", "glob", "grep", "web_search", "search_facts", "consult_diary", "consult_notes", "get_friend_note", "session_summary", "query_session", "set_reasoning_effort", "restart_runtime", "revive_sense"],
|
|
7
|
+
"toolNames": ["external_event_disposition", "query_active_work", "save_friend_note", "telegram_contact_manage", "query_cares", "care_manage", "await_condition", "resolve_await", "cancel_await", "list_recent_attachments", "materialize_attachment", "describe_image", "unraid_list_containers", "unraid_get_container_logs", "unraid_get_storage", "sanctuary_get_media_optimization", "sanctuary_search_media_catalog", "unraid_get_disks", "unraid_get_notifications", "unraid_get_system", "sanctuary_get_install_state", "unraid_check_services", "sanctuary_get_download_queue", "sanctuary_resume_download_queue", "unraid_restart_container", "steward_policy_manage", "send_message", "ponder", "rest", "settle", "speak", "shell", "shell_status", "shell_tail", "read_file", "write_file", "edit_file", "glob", "grep", "web_search", "search_facts", "consult_diary", "consult_notes", "get_friend_note", "session_summary", "query_session", "set_reasoning_effort", "restart_runtime", "revive_sense", "sanctuary_host_execute"],
|
|
8
8
|
"effectScopes": ["telegram.proactive", "telegram.request_return", "telegram.owner_event"]
|
|
9
9
|
},
|
|
10
10
|
"sanctuary-household": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0"?>
|
|
2
2
|
<Container version="2">
|
|
3
3
|
<Name>ouro-butler</Name>
|
|
4
|
-
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.
|
|
4
|
+
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.817</Repository>
|
|
5
5
|
<Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
|
|
6
6
|
<Network>host</Network>
|
|
7
7
|
<Shell>sh</Shell>
|
|
@@ -24,4 +24,5 @@
|
|
|
24
24
|
<Config Name="Runtime state" Target="/home/ouro/.ouro-cli" Default="/mnt/user/appdata/ouro-butler/runtime/.ouro-cli" Mode="rw" Description="Machine-scoped runtime" Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/ouro-butler/runtime/.ouro-cli</Config>
|
|
25
25
|
<Config Name="Agent bundle" Target="/home/ouro/AgentBundles/sanctuary.ouro" Default="/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro" Mode="rw" Description="Sanctuary bundle" Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro</Config>
|
|
26
26
|
<Config Name="Privileged event spool" Target="/run/ouro-events" Default="/boot/config/custom/ouro-events/spool" Mode="ro" Description="Root-owned detector envelopes, read-only inside the Butler" Type="Path" Display="always" Required="true" Mask="false">/boot/config/custom/ouro-events/spool</Config>
|
|
27
|
+
<Config Name="Root authority socket" Target="/run/ouro-authority" Default="/run/ouro-authority" Mode="ro" Description="Root-owned gateway socket and public issuer pins only" Type="Path" Display="always" Required="true" Mask="false">/run/ouro-authority</Config>
|
|
27
28
|
</Container>
|
package/dist/heart/core.js
CHANGED
|
@@ -176,7 +176,8 @@ async function getProviderRuntime(facing = "human", owner) {
|
|
|
176
176
|
console.error(`\n[fatal] ${msg}\n`);
|
|
177
177
|
throw new Error(msg);
|
|
178
178
|
}
|
|
179
|
-
|
|
179
|
+
// The shared tool loop supplies approval continuation to every provider adapter.
|
|
180
|
+
return { ...runtime, capabilities: new Set([...runtime.capabilities, "approval-continuation"]) };
|
|
180
181
|
}
|
|
181
182
|
/**
|
|
182
183
|
* Clear cached provider construction inputs so the next access re-reads them
|
|
@@ -71,15 +71,15 @@ function runContainerSpecAuditorCli(args, deps = {}) {
|
|
|
71
71
|
const readFile = deps.readFile ?? ((filePath) => fs.readFileSync(filePath));
|
|
72
72
|
const readText = (filePath) => decodeUtf8(readFile(filePath));
|
|
73
73
|
const write = deps.write ?? ((text) => process.stdout.write(text));
|
|
74
|
-
const staged = parseModeArguments(args, ["--template", "--runtime-policy", "--expected-image"]);
|
|
75
|
-
const persistent = parseModeArguments(args, ["--persistent-template", "--runtime-policy", "--expected-image-reference"]);
|
|
76
|
-
const effective = parseModeArguments(args, ["--inspect", "--image-inspect", "--expected-image", "--expected-image-reference", "--expected-icon"]);
|
|
74
|
+
const staged = parseModeArguments(args, ["--template", "--runtime-policy", "--expected-image", "--mount-contract"]);
|
|
75
|
+
const persistent = parseModeArguments(args, ["--persistent-template", "--runtime-policy", "--expected-image-reference", "--mount-contract"]);
|
|
76
|
+
const effective = parseModeArguments(args, ["--inspect", "--image-inspect", "--expected-image", "--expected-image-reference", "--expected-icon", "--mount-contract"]);
|
|
77
77
|
const sourceCandidate = parseModeArguments(args, ["--inspect", "--image-inspect", "--expected-image", "--mount-contract"]);
|
|
78
78
|
const sourceContract = sourceCandidate?.["--mount-contract"];
|
|
79
79
|
const sourceEffective = sourceContract === "legacy-alpha742" ? sourceCandidate : null;
|
|
80
80
|
const selectedEffective = effective ?? sourceEffective;
|
|
81
81
|
if (!staged && !persistent && !selectedEffective) {
|
|
82
|
-
write(JSON.stringify({ ok: false, error: "usage: staged --template <path> --runtime-policy <path> --expected-image <id>; persistent --persistent-template <path> --runtime-policy <path> --expected-image-reference <tag>; effective --inspect <path> --image-inspect <path> --expected-image <id> --expected-image-reference <tag> --expected-icon <url>; source compatibility effective --inspect <path> --image-inspect <path> --expected-image <id> --mount-contract legacy-alpha742" }) + "\n");
|
|
82
|
+
write(JSON.stringify({ ok: false, error: "usage: all modes require --mount-contract canonical-pre-gateway|canonical-gateway; staged --template <path> --runtime-policy <path> --expected-image <id>; persistent --persistent-template <path> --runtime-policy <path> --expected-image-reference <tag>; effective --inspect <path> --image-inspect <path> --expected-image <id> --expected-image-reference <tag> --expected-icon <url>; source compatibility effective --inspect <path> --image-inspect <path> --expected-image <id> --mount-contract legacy-alpha742" }) + "\n");
|
|
83
83
|
(0, runtime_1.emitNervesEvent)({
|
|
84
84
|
level: "error",
|
|
85
85
|
component: "daemon",
|
|
@@ -129,7 +129,7 @@ function runContainerSpecAuditorCli(args, deps = {}) {
|
|
|
129
129
|
expectedEnvironment,
|
|
130
130
|
expectedImageReference: selectedEffective["--expected-image-reference"],
|
|
131
131
|
expectedIcon: selectedEffective["--expected-icon"],
|
|
132
|
-
mountContract:
|
|
132
|
+
mountContract: selectedEffective["--mount-contract"],
|
|
133
133
|
});
|
|
134
134
|
if (imageInspect.Id !== selectedEffective["--expected-image"]) {
|
|
135
135
|
result.ok = false;
|
|
@@ -139,6 +139,7 @@ function runContainerSpecAuditorCli(args, deps = {}) {
|
|
|
139
139
|
return result.ok ? 0 : 1;
|
|
140
140
|
}
|
|
141
141
|
const templateArguments = persistent ?? staged;
|
|
142
|
+
const mountContract = templateArguments["--mount-contract"];
|
|
142
143
|
const templatePath = persistent ? templateArguments["--persistent-template"] : templateArguments["--template"];
|
|
143
144
|
let templateXml;
|
|
144
145
|
let runtimePolicyText;
|
|
@@ -158,8 +159,8 @@ function runContainerSpecAuditorCli(args, deps = {}) {
|
|
|
158
159
|
return 2;
|
|
159
160
|
}
|
|
160
161
|
const result = persistent
|
|
161
|
-
? (0, container_spec_auditor_1.auditSanctuaryPersistentTemplate)({ templateXml, runtimePolicyText, expectedImageReference: persistent["--expected-image-reference"] })
|
|
162
|
-
: (0, container_spec_auditor_1.auditSanctuaryStagedFiles)({ templateXml, runtimePolicyText, expectedImage: staged["--expected-image"] });
|
|
162
|
+
? (0, container_spec_auditor_1.auditSanctuaryPersistentTemplate)({ templateXml, runtimePolicyText, expectedImageReference: persistent["--expected-image-reference"], mountContract })
|
|
163
|
+
: (0, container_spec_auditor_1.auditSanctuaryStagedFiles)({ templateXml, runtimePolicyText, expectedImage: staged["--expected-image"], mountContract });
|
|
163
164
|
write(JSON.stringify(result) + "\n");
|
|
164
165
|
return result.ok ? 0 : 1;
|
|
165
166
|
}
|
|
@@ -9,12 +9,16 @@ const EXPECTED_BINDS = [
|
|
|
9
9
|
"/mnt/user/appdata/ouro-butler/runtime/.ouro-cli:/home/ouro/.ouro-cli:rw",
|
|
10
10
|
"/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro:/home/ouro/AgentBundles/sanctuary.ouro:rw",
|
|
11
11
|
"/boot/config/custom/ouro-events/spool:/run/ouro-events:ro",
|
|
12
|
+
"/run/ouro-authority:/run/ouro-authority:ro",
|
|
12
13
|
];
|
|
13
14
|
const EXPECTED_MOUNTS = [
|
|
14
15
|
["/mnt/user/appdata/ouro-butler/runtime/.ouro-cli", "/home/ouro/.ouro-cli", true, "rprivate"],
|
|
15
16
|
["/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro", "/home/ouro/AgentBundles/sanctuary.ouro", true, "rprivate"],
|
|
16
17
|
["/boot/config/custom/ouro-events/spool", "/run/ouro-events", false, "rprivate"],
|
|
18
|
+
["/run/ouro-authority", "/run/ouro-authority", false, "rprivate"],
|
|
17
19
|
];
|
|
20
|
+
const PRE_GATEWAY_BINDS = EXPECTED_BINDS.slice(0, 3);
|
|
21
|
+
const PRE_GATEWAY_MOUNTS = EXPECTED_MOUNTS.slice(0, 3);
|
|
18
22
|
const LEGACY_ALPHA742_IMAGE = "sha256:681449ad47a2621705cd339b481e6339236b31dc65e195b1cf5025d0f2191d7d";
|
|
19
23
|
const LEGACY_ALPHA742_MOUNTS = EXPECTED_MOUNTS.slice(0, 2);
|
|
20
24
|
const EXPECTED_EXTRA_PARAMS = "--restart=unless-stopped --user=10001:10001";
|
|
@@ -51,14 +55,14 @@ function auditSanctuaryContainerSpec(value, options) {
|
|
|
51
55
|
else {
|
|
52
56
|
if (!EXACT_IMAGE.test(options.expectedImage))
|
|
53
57
|
violations.push("expected image must be an exact local Docker image ID");
|
|
54
|
-
const mountContract = options.mountContract
|
|
55
|
-
if (mountContract !== "canonical" && mountContract !== "legacy-alpha742")
|
|
58
|
+
const mountContract = options.mountContract;
|
|
59
|
+
if (mountContract !== "canonical-pre-gateway" && mountContract !== "canonical-gateway" && mountContract !== "legacy-alpha742")
|
|
56
60
|
violations.push("unsupported mount contract");
|
|
57
61
|
if (mountContract === "legacy-alpha742" && options.expectedImage !== LEGACY_ALPHA742_IMAGE)
|
|
58
62
|
violations.push("legacy mount exception requires the pinned alpha.742 image ID");
|
|
59
|
-
const expectedMounts = mountContract === "legacy-alpha742" ? LEGACY_ALPHA742_MOUNTS : EXPECTED_MOUNTS;
|
|
60
|
-
const expectedArgs = mountContract
|
|
61
|
-
const expectedEntrypoint = mountContract
|
|
63
|
+
const expectedMounts = mountContract === "legacy-alpha742" ? LEGACY_ALPHA742_MOUNTS : mountContract === "canonical-pre-gateway" ? PRE_GATEWAY_MOUNTS : EXPECTED_MOUNTS;
|
|
64
|
+
const expectedArgs = mountContract !== "legacy-alpha742" ? PACKAGE_DAEMON_ARGS : LEGACY_DAEMON_ARGS;
|
|
65
|
+
const expectedEntrypoint = mountContract !== "legacy-alpha742" ? PACKAGE_ENTRYPOINT : LEGACY_ENTRYPOINT;
|
|
62
66
|
if (root.Image !== options.expectedImage)
|
|
63
67
|
violations.push("image does not match the reviewed exact local Docker image ID");
|
|
64
68
|
if (root.Path !== "node")
|
|
@@ -103,9 +107,9 @@ function auditSanctuaryContainerSpec(value, options) {
|
|
|
103
107
|
violations.push("container must not publish all exposed ports");
|
|
104
108
|
if (!isEmptyRecord(network?.Ports))
|
|
105
109
|
violations.push("effective network ports must be empty");
|
|
106
|
-
if (mountContract
|
|
110
|
+
if (mountContract !== "legacy-alpha742" && root.Name !== `/${EXPECTED_NAME}`)
|
|
107
111
|
violations.push("container name must be /ouro-butler");
|
|
108
|
-
if (mountContract
|
|
112
|
+
if (mountContract !== "legacy-alpha742") {
|
|
109
113
|
if (!options.expectedImageReference || !VERSION_REFERENCE.test(options.expectedImageReference))
|
|
110
114
|
violations.push("expected image reference must be the canonical package-version tag");
|
|
111
115
|
if (config.Image !== options.expectedImageReference)
|
|
@@ -162,6 +166,9 @@ function singleTextChild(document, name) {
|
|
|
162
166
|
}
|
|
163
167
|
function auditTemplate(input, expectedRepository, repositoryIsValid, repositoryViolation) {
|
|
164
168
|
const violations = [];
|
|
169
|
+
if (input.mountContract !== "canonical-pre-gateway" && input.mountContract !== "canonical-gateway")
|
|
170
|
+
violations.push("unsupported template mount contract");
|
|
171
|
+
const expectedBinds = input.mountContract === "canonical-pre-gateway" ? PRE_GATEWAY_BINDS : EXPECTED_BINDS;
|
|
165
172
|
const template = parseDockerManTemplateXml(input.templateXml);
|
|
166
173
|
if (!template)
|
|
167
174
|
violations.push("canonical DockerMan XML structure is invalid");
|
|
@@ -181,8 +188,8 @@ function auditTemplate(input, expectedRepository, repositoryIsValid, repositoryV
|
|
|
181
188
|
const { Type: type, Target: target, Mode: mode } = entry.attributes;
|
|
182
189
|
return entry.form === "text" && type === "Path" && target && mode ? `${entry.text}:${target}:${mode}` : "invalid";
|
|
183
190
|
});
|
|
184
|
-
if (configEntries.length !==
|
|
185
|
-
|| JSON.stringify([...pathConfigs].sort()) !== JSON.stringify([...
|
|
191
|
+
if (configEntries.length !== expectedBinds.length
|
|
192
|
+
|| JSON.stringify([...pathConfigs].sort()) !== JSON.stringify([...expectedBinds].sort())) {
|
|
186
193
|
violations.push("template Config entries must equal the canonical path binds");
|
|
187
194
|
}
|
|
188
195
|
const postArgs = directChildren(template, "PostArgs");
|