1
0

runtime: publish CLUSTER-REFERENCE-MODE-SEMANTICS-153

This commit is contained in:
homelab-runtime-publisher
2026-07-22 07:56:07 +00:00
parent 67f3a2e3da
commit 326cd41f93
4 changed files with 773 additions and 116 deletions
File diff suppressed because one or more lines are too long
@@ -0,0 +1,378 @@
CHAT_OUTPUT_BEGIN
COMMAND_ID=CLUSTER-REFERENCE-MODE-SEMANTICS-153
STATUS=OK
RC=0
HOST=pve01
MODE=read-only
COMPONENT=cluster-audit
REFERENCE_REGISTER_CHECK=OK
REFERENCE_SHA256=5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e
ERROR_REGISTER_CHECK=OK
ERROR_REGISTER_SHA256=08b3a769b8e38eaa1acec915352fb328180d70caa7ff6cd9d1ec0da426262752
COMMAND_SHA256=8dc27e444049919a8436dfda6dbbf1e6f925075388cb73ecf483b2944f0dcf2e
DUPLICATE_FAILED_COMMAND_BLOCKED=false
EXECUTION_STARTED=true
CHANGES_MADE=false
SANITIZED=yes
SECRETS_INCLUDED=no
PRIVATE_ADDRESSES_INCLUDED=no
RAW_EVIDENCE_SHA256=5a67f7767c4daff72ce91f626619b8d4251dbd9378ec9d757183506a3e9345d0
SANITIZED_OUTPUT_SHA256=1d71af6fb931c9d83bc82e594516212e45cd585f292d30486e0939ba5708acd0
OUTPUT_BEGIN
PREVIOUS_RESULT=VERIFIED
PREVIOUS_IMMUTABLE_COMMIT=fa3878ea8aeec87002c4c8f179fb88e25184e924
=== RUNNER_HELP ===
Usage:
homelab-chat-run COMMAND_ID MODE COMPONENT -- command [arguments...]
MODE:
read-only
verify
change
transport-test
Environment:
HOMELAB_ALLOW_REPEAT=1 Allow exact repeat of a previously failed command.
HOMELAB_TIMEOUT=SECONDS Optional command timeout.
=== MODE_VALIDATION_AND_EFFECTS ===
1-#!/usr/bin/env bash
2-set -Eeuo pipefail
3-umask 077
4-
5-usage() {
6- cat >&2 <<'USAGE'
7-Usage:
8- homelab-chat-run COMMAND_ID MODE COMPONENT -- command [arguments...]
9-
10-MODE:
11: read-only
12: verify
13: change
14: transport-test
15-
16-Environment:
17- HOMELAB_ALLOW_REPEAT=1 Allow exact repeat of a previously failed command.
18- HOMELAB_TIMEOUT=SECONDS Optional command timeout.
19-USAGE
20- exit 64
21-}
22-
23-[ "$#" -ge 5 ] || usage
24-
25-COMMAND_ID="$1"
26-MODE="$2"
27-COMPONENT="$3"
28-shift 3
29-
30-[ "${1:-}" = '--' ] || usage
31-shift
32-[ "$#" -ge 1 ] || usage
33-
34-case "$COMMAND_ID" in
35- ''|*[!A-Za-z0-9._-]*)
36- echo 'RESULT=FAIL'
37- echo 'REASON=INVALID_COMMAND_ID'
38- exit 65
39- ;;
40-esac
41-
42-case "$COMPONENT" in
43- ''|*[!A-Za-z0-9._-]*)
44- echo 'RESULT=FAIL'
45- echo 'REASON=INVALID_COMPONENT'
46- exit 66
47- ;;
48-esac
49-
50:case "$MODE" in
51: read-only|verify|change|transport-test)
52- ;;
53- *)
54- echo 'RESULT=FAIL'
55: echo 'REASON=INVALID_MODE'
56- exit 67
57- ;;
58-esac
59-
60-for TOOL in \
61- awk \
62- curl \
63- flock \
64- python3 \
65- scp \
66- sha256sum \
67- ssh
68-do
69- command -v "$TOOL" >/dev/null 2>&1 || {
70- echo 'RESULT=FAIL'
71- echo "REASON=REQUIRED_TOOL_MISSING:$TOOL"
72- exit 68
73- }
74-done
75-
76-REFERENCE=/etc/pve/31_HOMELAB_REFERENCE.md
77-ERROR_REGISTER=/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md
78-
79-[ -r "$REFERENCE" ] || {
80- echo 'RESULT=FAIL'
81- echo 'REASON=REFERENCE_NOT_READABLE'
82- exit 69
83-}
84-
85-[ -r "$ERROR_REGISTER" ] || {
86- echo 'RESULT=FAIL'
87- echo 'REASON=ERROR_REGISTER_NOT_READABLE'
88- exit 70
89-}
90-
91-STATE_DIR=/var/lib/homelab-chat-run
92-RAW_DIR="$STATE_DIR/raw"
93-PUBLIC_DIR="$STATE_DIR/public"
94-LEDGER="$STATE_DIR/state/command-ledger.tsv"
95-LOCK="$STATE_DIR/state/runner.lock"
--
131-
132-PREVIOUS_FAILED_RC="$(
133- awk -F '\t' -v hash="$COMMAND_SHA256" '
134- $3 == hash && $4 != "0" {
135- value=$4
136- }
137-
138- END {
139- print value
140- }
141- ' "$LEDGER"
142-)"
143-
144-RUN_STARTED=true
145-REPEAT_BLOCKED=false
146:BLOCK_REASON=''
147:CHANGES_MADE=false
148-
149:case "$MODE" in
150: change)
151: CHANGES_MADE=true
152- ;;
153-esac
154-
155-if [ -n "$PREVIOUS_FAILED_RC" ] &&
156- [ "${HOMELAB_ALLOW_REPEAT:-0}" != 1 ]; then
157-
158- RUN_STARTED=false
159- REPEAT_BLOCKED=true
160: BLOCK_REASON='EXACT_FAILED_COMMAND_REPEAT'
161- RC=73
162- STATUS='BLOCKED'
163-
164- {
165- echo 'COMMAND_EXECUTION_BLOCKED=yes'
166- echo 'REASON=EXACT_FAILED_COMMAND_REPEAT'
167- echo "PREVIOUS_RC=$PREVIOUS_FAILED_RC"
168- echo "COMMAND_SHA256=$COMMAND_SHA256"
169- echo
170- echo 'Set HOMELAB_ALLOW_REPEAT=1 only after reviewing the previous failure.'
171- } >"$RAW_FILE"
172-else
173- set +e
174-
175- if [ -n "${HOMELAB_TIMEOUT:-}" ]; then
176- case "$HOMELAB_TIMEOUT" in
177- *[!0-9]*|'')
178- echo 'RESULT=FAIL'
179- echo 'REASON=INVALID_HOMELAB_TIMEOUT'
180- exit 74
181- ;;
182- esac
183-
184- timeout \
185- --foreground \
186- --preserve-status \
187- "$HOMELAB_TIMEOUT" \
188- "$@" >"$RAW_FILE" 2>&1
189- RC=$?
190- else
191- "$@" >"$RAW_FILE" 2>&1
192- RC=$?
193- fi
194-
195- set -e
196-
197- if [ "$RC" -eq 0 ]; then
198- STATUS='OK'
199- else
200- STATUS='FAIL'
--
317- "$COMMAND_ID" \
318- "$STATUS" \
319- "$RC" \
320- "$HOST_SHORT" \
321- "$MODE" \
322- "$COMPONENT" \
323- "$STARTED_AT" \
324- "$FINISHED_AT" \
325- "$REFERENCE_SHA256" \
326- "$ERROR_REGISTER_SHA256" \
327- "$COMMAND_SHA256" \
328- "$RAW_SHA256" \
329- "$SANITIZED_SHA256" \
330- "$RUN_STARTED" \
331- "$REPEAT_BLOCKED" \
332: "$BLOCK_REASON" \
333: "$CHANGES_MADE" <<'PY'
334-import json
335-import sys
336-from pathlib import Path
337-
338-(
339- json_path,
340- output_path,
341- command_id,
342- status,
343- rc,
344- host,
345- mode,
346- component,
347- started,
348- finished,
349- reference_sha,
350- error_sha,
351- command_sha,
352- raw_sha,
353- sanitized_sha,
354- run_started,
355- repeat_blocked,
356: block_reason,
357: changes_made,
358-) = sys.argv[1:]
359-
360-output = Path(output_path).read_text(
361- encoding="utf-8",
362- errors="replace",
363-)
364-
365-encoded = output.encode("utf-8")
366-json_limit = 300_000
367-output_truncated = len(encoded) > json_limit
368-
369-if output_truncated:
370- output_preview = encoded[:json_limit].decode(
371- "utf-8",
372- errors="ignore",
373- )
374-else:
375- output_preview = output
376-
377-document = {
378- "schema_version": 1,
379- "channel": "homelab-runtime",
380- "command_id": command_id,
381- "status": status,
382- "rc": int(rc),
383- "host": host,
384- "mode": mode,
385- "component": component,
386- "started_at_utc": started,
387- "finished_at_utc": finished,
388- "reference_register_checked": True,
389- "reference_sha256": reference_sha,
390- "error_register_checked": True,
391- "error_register_sha256": error_sha,
392- "command_sha256": command_sha,
393- "duplicate_failed_command_blocked": repeat_blocked == "true",
394: "block_reason": block_reason or None,
395: "execution_started": run_started == "true",
396: "changes_made": changes_made == "true",
397- "sanitized": True,
398- "secrets_included": False,
399- "private_addresses_included": False,
400- "raw_evidence_retained_locally": True,
401- "raw_evidence_sha256": raw_sha,
402- "sanitized_output_sha256": sanitized_sha,
403- "output_truncated_in_json": output_truncated,
404- "full_sanitized_output_url": (
405- "https://git.gram1.ru/.well-known/"
406- "homelab-runtime/latest.txt"
407- ),
408- "output": output_preview,
409-}
410-
411-Path(json_path).write_text(
412- json.dumps(
413- document,
414- ensure_ascii=False,
415- indent=2,
416- )
417- + "\n",
418- encoding="utf-8",
419-)
420-PY
421-
422-{
423- echo 'CHAT_OUTPUT_BEGIN'
424- echo "COMMAND_ID=$COMMAND_ID"
425- echo "STATUS=$STATUS"
426- echo "RC=$RC"
427- echo "HOST=$HOST_SHORT"
428- echo "MODE=$MODE"
429- echo "COMPONENT=$COMPONENT"
430- echo 'REFERENCE_REGISTER_CHECK=OK'
431- echo "REFERENCE_SHA256=$REFERENCE_SHA256"
432- echo 'ERROR_REGISTER_CHECK=OK'
433- echo "ERROR_REGISTER_SHA256=$ERROR_REGISTER_SHA256"
434- echo "COMMAND_SHA256=$COMMAND_SHA256"
435- echo "DUPLICATE_FAILED_COMMAND_BLOCKED=$REPEAT_BLOCKED"
436: echo "EXECUTION_STARTED=$RUN_STARTED"
437: echo "CHANGES_MADE=$CHANGES_MADE"
438- echo 'SANITIZED=yes'
439- echo 'SECRETS_INCLUDED=no'
440- echo 'PRIVATE_ADDRESSES_INCLUDED=no'
441- echo "RAW_EVIDENCE_SHA256=$RAW_SHA256"
442- echo "SANITIZED_OUTPUT_SHA256=$SANITIZED_SHA256"
443- echo 'OUTPUT_BEGIN'
444- cat "$SANITIZED_FILE"
445- echo
446- echo 'OUTPUT_END'
447- echo 'CHAT_OUTPUT_END'
448-} >"$TXT_FILE"
449-
450-chmod 0600 \
451- "$RAW_FILE" \
452- "$SANITIZED_FILE" \
453- "$JSON_FILE" \
454- "$TXT_FILE"
455-
456-TARGET=debian@[PRIVATE_IP]
457-REMOTE_PREFIX="/tmp/homelab-runtime-${COMMAND_ID}-$$"
458-
459-SSH_OPTS=(
460- -o BatchMode=yes
461- -o ConnectTimeout=10
462- -o ConnectionAttempts=1
463- -o StrictHostKeyChecking=yes
464- -o UserKnownHostsFile=[SENSITIVE_PATH]
465-)
466-
467-scp \
468- -q \
469- "${SSH_OPTS[@]}" \
470- "$JSON_FILE" \
471- "${TARGET}:${REMOTE_PREFIX}.json"
472-
473-scp \
474- -q \
475- "${SSH_OPTS[@]}" \
476- "$TXT_FILE" \
477- "${TARGET}:${REMOTE_PREFIX}.txt"
=== MODE_REFERENCES ===
11: read-only
12: verify
13: change
14: transport-test
51: read-only|verify|change|transport-test)
150: change)
357: changes_made,
396: "changes_made": changes_made == "true",
CLUSTER_REFERENCE_MODE_SEMANTICS=COMPLETE
OUTPUT_END
CHAT_OUTPUT_END
+7 -7
View File
File diff suppressed because one or more lines are too long
+358 -109
View File
@@ -1,5 +1,5 @@
CHAT_OUTPUT_BEGIN
COMMAND_ID=CLUSTER-REFERENCE-FOUNDATION-TRACE-152
COMMAND_ID=CLUSTER-REFERENCE-MODE-SEMANTICS-153
STATUS=OK
RC=0
HOST=pve01
@@ -9,121 +9,370 @@ REFERENCE_REGISTER_CHECK=OK
REFERENCE_SHA256=5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e
ERROR_REGISTER_CHECK=OK
ERROR_REGISTER_SHA256=08b3a769b8e38eaa1acec915352fb328180d70caa7ff6cd9d1ec0da426262752
COMMAND_SHA256=7f23950666a0d405fbab1573b753c142276f30c7ede87ab44916b926b84b8f2c
COMMAND_SHA256=8dc27e444049919a8436dfda6dbbf1e6f925075388cb73ecf483b2944f0dcf2e
DUPLICATE_FAILED_COMMAND_BLOCKED=false
EXECUTION_STARTED=true
CHANGES_MADE=false
SANITIZED=yes
SECRETS_INCLUDED=no
PRIVATE_ADDRESSES_INCLUDED=no
RAW_EVIDENCE_SHA256=01a9de51acd01ddfab74baadc89770328e8b6c9157ab718fecdf535dfcb50600
SANITIZED_OUTPUT_SHA256=01a9de51acd01ddfab74baadc89770328e8b6c9157ab718fecdf535dfcb50600
RAW_EVIDENCE_SHA256=5a67f7767c4daff72ce91f626619b8d4251dbd9378ec9d757183506a3e9345d0
SANITIZED_OUTPUT_SHA256=1d71af6fb931c9d83bc82e594516212e45cd585f292d30486e0939ba5708acd0
OUTPUT_BEGIN
=== COMMAND_151_LOCAL_LOG ===
FILE=/var/lib/homelab-chat-run/raw/CLUSTER-REFERENCE-FOUNDATION-151-20260722T073458Z.log SIZE=180 MTIME=2026-07-22 10:34:58.698144693 +0300
1:REFERENCE_ROOT=/etc/homelab/reference
2:REFERENCE_SECTIONS=24
3:REFERENCE_BACKUP=NONE
4:REFERENCE_SECRET_POLICY=ENFORCED
5:REFERENCE_FOUNDATION=READY
6:CLUSTER_REFERENCE_FOUNDATION=COMPLETE
=== COMMAND_151_ERROR_REGISTER ===
/var/lib/homelab-chat-run/state/command-ledger.tsv
/var/lib/homelab-chat-run/public/CLUSTER-REFERENCE-FOUNDATION-151-20260722T073458Z.json
/var/lib/homelab-chat-run/public/CLUSTER-REFERENCE-FOUNDATION-151-20260722T073458Z-latest.txt
ERROR_REGISTER_MATCH=FOUND
=== REFERENCE_STATE ===
REFERENCE_ROOT=PRESENT
d 750 4096 2026-07-22T10:34:58.6964026990 /etc/homelab/reference/public
d 750 4096 2026-07-22T10:34:58.6964026990 /etc/homelab/reference/runbooks
d 750 4096 2026-07-22T10:34:58.6964026990 /etc/homelab/reference/snapshots
d 750 4096 2026-07-22T10:34:58.6966043260 /etc/homelab/reference/schema
d 750 4096 2026-07-22T10:34:58.6970459770 /etc/homelab/reference/declared
d 750 4096 2026-07-22T10:34:58.6974681490 /etc/homelab/reference/observed
d 750 4096 2026-07-22T10:34:58.6978512600 /etc/homelab/reference/effective
d 750 4096 2026-07-22T10:34:58.6978691140 /etc/homelab/reference
d 750 4096 2026-07-22T10:34:58.6978834310 /etc/homelab/reference/state
f 640 148 2026-07-22T10:34:58.6979008110 /etc/homelab/reference/state/current.json
f 640 153 2026-07-22T10:34:58.6968014830 /etc/homelab/reference/declared/dns.json
f 640 153 2026-07-22T10:34:58.6976137370 /etc/homelab/reference/effective/dns.json
f 640 155 2026-07-22T10:34:58.6966969670 /etc/homelab/reference/declared/nodes.json
f 640 155 2026-07-22T10:34:58.6975198720 /etc/homelab/reference/effective/nodes.json
f 640 156 2026-07-22T10:34:58.6967158380 /etc/homelab/reference/declared/guests.json
f 640 156 2026-07-22T10:34:58.6969484530 /etc/homelab/reference/declared/access.json
f 640 156 2026-07-22T10:34:58.6969644820 /etc/homelab/reference/declared/owners.json
f 640 156 2026-07-22T10:34:58.6971898020 /etc/homelab/reference/observed/dns.json
f 640 156 2026-07-22T10:34:58.6975362380 /etc/homelab/reference/effective/guests.json
f 640 156 2026-07-22T10:34:58.6977531080 /etc/homelab/reference/effective/access.json
f 640 156 2026-07-22T10:34:58.6977693510 /etc/homelab/reference/effective/owners.json
f 640 157 2026-07-22T10:34:58.6966738300 /etc/homelab/reference/declared/cluster.json
f 640 157 2026-07-22T10:34:58.6968176220 /etc/homelab/reference/declared/domains.json
f 640 157 2026-07-22T10:34:58.6968507170 /etc/homelab/reference/declared/ingress.json
f 640 157 2026-07-22T10:34:58.6968665330 /etc/homelab/reference/declared/storage.json
f 640 157 2026-07-22T10:34:58.6968815380 /etc/homelab/reference/declared/backups.json
f 640 157 2026-07-22T10:34:58.6969804230 /etc/homelab/reference/declared/vendors.json
f 640 157 2026-07-22T10:34:58.6975026350 /etc/homelab/reference/effective/cluster.json
f 640 157 2026-07-22T10:34:58.6976296950 /etc/homelab/reference/effective/domains.json
f 640 157 2026-07-22T10:34:58.6976621460 /etc/homelab/reference/effective/ingress.json
f 640 157 2026-07-22T10:34:58.6976775090 /etc/homelab/reference/effective/storage.json
f 640 157 2026-07-22T10:34:58.6976922400 /etc/homelab/reference/effective/backups.json
f 640 157 2026-07-22T10:34:58.6977857550 /etc/homelab/reference/effective/vendors.json
f 640 158 2026-07-22T10:34:58.6967496500 /etc/homelab/reference/declared/services.json
f 640 158 2026-07-22T10:34:58.6967835450 /etc/homelab/reference/declared/networks.json
f 640 158 2026-07-22T10:34:58.6970122440 /etc/homelab/reference/declared/security.json
f 640 158 2026-07-22T10:34:58.6970626700 /etc/homelab/reference/declared/runbooks.json
f 640 158 2026-07-22T10:34:58.6970953450 /etc/homelab/reference/observed/nodes.json
f 640 158 2026-07-22T10:34:58.6975661010 /etc/homelab/reference/effective/services.json
f 640 158 2026-07-22T10:34:58.6975973330 /etc/homelab/reference/effective/networks.json
f 640 158 2026-07-22T10:34:58.6978179760 /etc/homelab/reference/effective/security.json
f 640 158 2026-07-22T10:34:58.6978691140 /etc/homelab/reference/effective/runbooks.json
f 640 159 2026-07-22T10:34:58.6969966040 /etc/homelab/reference/declared/contracts.json
f 640 159 2026-07-22T10:34:58.6971108280 /etc/homelab/reference/observed/guests.json
f 640 159 2026-07-22T10:34:58.6973313430 /etc/homelab/reference/observed/access.json
f 640 159 2026-07-22T10:34:58.6973523380 /etc/homelab/reference/observed/owners.json
f 640 159 2026-07-22T10:34:58.6978023050 /etc/homelab/reference/effective/contracts.json
f 640 160 2026-07-22T10:34:58.6967330550 /etc/homelab/reference/declared/containers.json
f 640 160 2026-07-22T10:34:58.6968977560 /etc/homelab/reference/declared/monitoring.json
f 640 160 2026-07-22T10:34:58.6970793370 /etc/homelab/reference/observed/cluster.json
f 640 160 2026-07-22T10:34:58.6972051460 /etc/homelab/reference/observed/domains.json
f 640 160 2026-07-22T10:34:58.6972368890 /etc/homelab/reference/observed/ingress.json
f 640 160 2026-07-22T10:34:58.6972525750 /etc/homelab/reference/observed/storage.json
f 640 160 2026-07-22T10:34:58.6972679110 /etc/homelab/reference/observed/backups.json
f 640 160 2026-07-22T10:34:58.6973681500 /etc/homelab/reference/observed/vendors.json
f 640 160 2026-07-22T10:34:58.6975511580 /etc/homelab/reference/effective/containers.json
f 640 160 2026-07-22T10:34:58.6977071740 /etc/homelab/reference/effective/monitoring.json
f 640 161 2026-07-22T10:34:58.6971427070 /etc/homelab/reference/observed/services.json
f 640 161 2026-07-22T10:34:58.6971740390 /etc/homelab/reference/observed/networks.json
f 640 161 2026-07-22T10:34:58.6974304020 /etc/homelab/reference/observed/security.json
f 640 161 2026-07-22T10:34:58.6974849460 /etc/homelab/reference/observed/runbooks.json
f 640 162 2026-07-22T10:34:58.6967668110 /etc/homelab/reference/declared/applications.json
f 640 162 2026-07-22T10:34:58.6968346450 /etc/homelab/reference/declared/certificates.json
f 640 162 2026-07-22T10:34:58.6969325220 /etc/homelab/reference/declared/dependencies.json
f 640 162 2026-07-22T10:34:58.6974142710 /etc/homelab/reference/observed/contracts.json
f 640 162 2026-07-22T10:34:58.6975824200 /etc/homelab/reference/effective/applications.json
f 640 162 2026-07-22T10:34:58.6976463310 /etc/homelab/reference/effective/certificates.json
f 640 162 2026-07-22T10:34:58.6977380340 /etc/homelab/reference/effective/dependencies.json
f 640 163 2026-07-22T10:34:58.6971273900 /etc/homelab/reference/observed/containers.json
f 640 163 2026-07-22T10:34:58.6972830790 /etc/homelab/reference/observed/monitoring.json
f 640 165 2026-07-22T10:34:58.6971583070 /etc/homelab/reference/observed/applications.json
f 640 165 2026-07-22T10:34:58.6972204840 /etc/homelab/reference/observed/certificates.json
f 640 165 2026-07-22T10:34:58.6973154820 /etc/homelab/reference/observed/dependencies.json
f 640 167 2026-07-22T10:34:58.6969164820 /etc/homelab/reference/declared/external_services.json
f 640 167 2026-07-22T10:34:58.6970278950 /etc/homelab/reference/declared/disaster_recovery.json
f 640 167 2026-07-22T10:34:58.6970459770 /etc/homelab/reference/declared/change_management.json
f 640 167 2026-07-22T10:34:58.6977227780 /etc/homelab/reference/effective/external_services.json
f 640 167 2026-07-22T10:34:58.6978337330 /etc/homelab/reference/effective/disaster_recovery.json
f 640 167 2026-07-22T10:34:58.6978512600 /etc/homelab/reference/effective/change_management.json
f 640 170 2026-07-22T10:34:58.6972985790 /etc/homelab/reference/observed/external_services.json
f 640 170 2026-07-22T10:34:58.6974490020 /etc/homelab/reference/observed/disaster_recovery.json
f 640 170 2026-07-22T10:34:58.6974681490 /etc/homelab/reference/observed/change_management.json
f 640 1743 2026-07-22T10:34:58.6966043260 /etc/homelab/reference/manifest.json
f 640 262 2026-07-22T10:34:58.6966410380 /etc/homelab/reference/schema/record.schema.json
f 640 603 2026-07-22T10:34:58.6978834310 /etc/homelab/reference/README.md
=== REFERENCE_BACKUPS ===
REFERENCE_BACKUPS=ABSENT
=== REFERENCE_CLI ===
REFERENCE_CLI=ABSENT
CLUSTER_REFERENCE_FOUNDATION_TRACE=COMPLETE
PREVIOUS_RESULT=VERIFIED
PREVIOUS_IMMUTABLE_COMMIT=fa3878ea8aeec87002c4c8f179fb88e25184e924
=== RUNNER_HELP ===
Usage:
homelab-chat-run COMMAND_ID MODE COMPONENT -- command [arguments...]
MODE:
read-only
verify
change
transport-test
Environment:
HOMELAB_ALLOW_REPEAT=1 Allow exact repeat of a previously failed command.
HOMELAB_TIMEOUT=SECONDS Optional command timeout.
=== MODE_VALIDATION_AND_EFFECTS ===
1-#!/usr/bin/env bash
2-set -Eeuo pipefail
3-umask 077
4-
5-usage() {
6- cat >&2 <<'USAGE'
7-Usage:
8- homelab-chat-run COMMAND_ID MODE COMPONENT -- command [arguments...]
9-
10-MODE:
11: read-only
12: verify
13: change
14: transport-test
15-
16-Environment:
17- HOMELAB_ALLOW_REPEAT=1 Allow exact repeat of a previously failed command.
18- HOMELAB_TIMEOUT=SECONDS Optional command timeout.
19-USAGE
20- exit 64
21-}
22-
23-[ "$#" -ge 5 ] || usage
24-
25-COMMAND_ID="$1"
26-MODE="$2"
27-COMPONENT="$3"
28-shift 3
29-
30-[ "${1:-}" = '--' ] || usage
31-shift
32-[ "$#" -ge 1 ] || usage
33-
34-case "$COMMAND_ID" in
35- ''|*[!A-Za-z0-9._-]*)
36- echo 'RESULT=FAIL'
37- echo 'REASON=INVALID_COMMAND_ID'
38- exit 65
39- ;;
40-esac
41-
42-case "$COMPONENT" in
43- ''|*[!A-Za-z0-9._-]*)
44- echo 'RESULT=FAIL'
45- echo 'REASON=INVALID_COMPONENT'
46- exit 66
47- ;;
48-esac
49-
50:case "$MODE" in
51: read-only|verify|change|transport-test)
52- ;;
53- *)
54- echo 'RESULT=FAIL'
55: echo 'REASON=INVALID_MODE'
56- exit 67
57- ;;
58-esac
59-
60-for TOOL in \
61- awk \
62- curl \
63- flock \
64- python3 \
65- scp \
66- sha256sum \
67- ssh
68-do
69- command -v "$TOOL" >/dev/null 2>&1 || {
70- echo 'RESULT=FAIL'
71- echo "REASON=REQUIRED_TOOL_MISSING:$TOOL"
72- exit 68
73- }
74-done
75-
76-REFERENCE=/etc/pve/31_HOMELAB_REFERENCE.md
77-ERROR_REGISTER=/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md
78-
79-[ -r "$REFERENCE" ] || {
80- echo 'RESULT=FAIL'
81- echo 'REASON=REFERENCE_NOT_READABLE'
82- exit 69
83-}
84-
85-[ -r "$ERROR_REGISTER" ] || {
86- echo 'RESULT=FAIL'
87- echo 'REASON=ERROR_REGISTER_NOT_READABLE'
88- exit 70
89-}
90-
91-STATE_DIR=/var/lib/homelab-chat-run
92-RAW_DIR="$STATE_DIR/raw"
93-PUBLIC_DIR="$STATE_DIR/public"
94-LEDGER="$STATE_DIR/state/command-ledger.tsv"
95-LOCK="$STATE_DIR/state/runner.lock"
--
131-
132-PREVIOUS_FAILED_RC="$(
133- awk -F '\t' -v hash="$COMMAND_SHA256" '
134- $3 == hash && $4 != "0" {
135- value=$4
136- }
137-
138- END {
139- print value
140- }
141- ' "$LEDGER"
142-)"
143-
144-RUN_STARTED=true
145-REPEAT_BLOCKED=false
146:BLOCK_REASON=''
147:CHANGES_MADE=false
148-
149:case "$MODE" in
150: change)
151: CHANGES_MADE=true
152- ;;
153-esac
154-
155-if [ -n "$PREVIOUS_FAILED_RC" ] &&
156- [ "${HOMELAB_ALLOW_REPEAT:-0}" != 1 ]; then
157-
158- RUN_STARTED=false
159- REPEAT_BLOCKED=true
160: BLOCK_REASON='EXACT_FAILED_COMMAND_REPEAT'
161- RC=73
162- STATUS='BLOCKED'
163-
164- {
165- echo 'COMMAND_EXECUTION_BLOCKED=yes'
166- echo 'REASON=EXACT_FAILED_COMMAND_REPEAT'
167- echo "PREVIOUS_RC=$PREVIOUS_FAILED_RC"
168- echo "COMMAND_SHA256=$COMMAND_SHA256"
169- echo
170- echo 'Set HOMELAB_ALLOW_REPEAT=1 only after reviewing the previous failure.'
171- } >"$RAW_FILE"
172-else
173- set +e
174-
175- if [ -n "${HOMELAB_TIMEOUT:-}" ]; then
176- case "$HOMELAB_TIMEOUT" in
177- *[!0-9]*|'')
178- echo 'RESULT=FAIL'
179- echo 'REASON=INVALID_HOMELAB_TIMEOUT'
180- exit 74
181- ;;
182- esac
183-
184- timeout \
185- --foreground \
186- --preserve-status \
187- "$HOMELAB_TIMEOUT" \
188- "$@" >"$RAW_FILE" 2>&1
189- RC=$?
190- else
191- "$@" >"$RAW_FILE" 2>&1
192- RC=$?
193- fi
194-
195- set -e
196-
197- if [ "$RC" -eq 0 ]; then
198- STATUS='OK'
199- else
200- STATUS='FAIL'
--
317- "$COMMAND_ID" \
318- "$STATUS" \
319- "$RC" \
320- "$HOST_SHORT" \
321- "$MODE" \
322- "$COMPONENT" \
323- "$STARTED_AT" \
324- "$FINISHED_AT" \
325- "$REFERENCE_SHA256" \
326- "$ERROR_REGISTER_SHA256" \
327- "$COMMAND_SHA256" \
328- "$RAW_SHA256" \
329- "$SANITIZED_SHA256" \
330- "$RUN_STARTED" \
331- "$REPEAT_BLOCKED" \
332: "$BLOCK_REASON" \
333: "$CHANGES_MADE" <<'PY'
334-import json
335-import sys
336-from pathlib import Path
337-
338-(
339- json_path,
340- output_path,
341- command_id,
342- status,
343- rc,
344- host,
345- mode,
346- component,
347- started,
348- finished,
349- reference_sha,
350- error_sha,
351- command_sha,
352- raw_sha,
353- sanitized_sha,
354- run_started,
355- repeat_blocked,
356: block_reason,
357: changes_made,
358-) = sys.argv[1:]
359-
360-output = Path(output_path).read_text(
361- encoding="utf-8",
362- errors="replace",
363-)
364-
365-encoded = output.encode("utf-8")
366-json_limit = 300_000
367-output_truncated = len(encoded) > json_limit
368-
369-if output_truncated:
370- output_preview = encoded[:json_limit].decode(
371- "utf-8",
372- errors="ignore",
373- )
374-else:
375- output_preview = output
376-
377-document = {
378- "schema_version": 1,
379- "channel": "homelab-runtime",
380- "command_id": command_id,
381- "status": status,
382- "rc": int(rc),
383- "host": host,
384- "mode": mode,
385- "component": component,
386- "started_at_utc": started,
387- "finished_at_utc": finished,
388- "reference_register_checked": True,
389- "reference_sha256": reference_sha,
390- "error_register_checked": True,
391- "error_register_sha256": error_sha,
392- "command_sha256": command_sha,
393- "duplicate_failed_command_blocked": repeat_blocked == "true",
394: "block_reason": block_reason or None,
395: "execution_started": run_started == "true",
396: "changes_made": changes_made == "true",
397- "sanitized": True,
398- "secrets_included": False,
399- "private_addresses_included": False,
400- "raw_evidence_retained_locally": True,
401- "raw_evidence_sha256": raw_sha,
402- "sanitized_output_sha256": sanitized_sha,
403- "output_truncated_in_json": output_truncated,
404- "full_sanitized_output_url": (
405- "https://git.gram1.ru/.well-known/"
406- "homelab-runtime/latest.txt"
407- ),
408- "output": output_preview,
409-}
410-
411-Path(json_path).write_text(
412- json.dumps(
413- document,
414- ensure_ascii=False,
415- indent=2,
416- )
417- + "\n",
418- encoding="utf-8",
419-)
420-PY
421-
422-{
423- echo 'CHAT_OUTPUT_BEGIN'
424- echo "COMMAND_ID=$COMMAND_ID"
425- echo "STATUS=$STATUS"
426- echo "RC=$RC"
427- echo "HOST=$HOST_SHORT"
428- echo "MODE=$MODE"
429- echo "COMPONENT=$COMPONENT"
430- echo 'REFERENCE_REGISTER_CHECK=OK'
431- echo "REFERENCE_SHA256=$REFERENCE_SHA256"
432- echo 'ERROR_REGISTER_CHECK=OK'
433- echo "ERROR_REGISTER_SHA256=$ERROR_REGISTER_SHA256"
434- echo "COMMAND_SHA256=$COMMAND_SHA256"
435- echo "DUPLICATE_FAILED_COMMAND_BLOCKED=$REPEAT_BLOCKED"
436: echo "EXECUTION_STARTED=$RUN_STARTED"
437: echo "CHANGES_MADE=$CHANGES_MADE"
438- echo 'SANITIZED=yes'
439- echo 'SECRETS_INCLUDED=no'
440- echo 'PRIVATE_ADDRESSES_INCLUDED=no'
441- echo "RAW_EVIDENCE_SHA256=$RAW_SHA256"
442- echo "SANITIZED_OUTPUT_SHA256=$SANITIZED_SHA256"
443- echo 'OUTPUT_BEGIN'
444- cat "$SANITIZED_FILE"
445- echo
446- echo 'OUTPUT_END'
447- echo 'CHAT_OUTPUT_END'
448-} >"$TXT_FILE"
449-
450-chmod 0600 \
451- "$RAW_FILE" \
452- "$SANITIZED_FILE" \
453- "$JSON_FILE" \
454- "$TXT_FILE"
455-
456-TARGET=debian@[PRIVATE_IP]
457-REMOTE_PREFIX="/tmp/homelab-runtime-${COMMAND_ID}-$$"
458-
459-SSH_OPTS=(
460- -o BatchMode=yes
461- -o ConnectTimeout=10
462- -o ConnectionAttempts=1
463- -o StrictHostKeyChecking=yes
464- -o UserKnownHostsFile=[SENSITIVE_PATH]
465-)
466-
467-scp \
468- -q \
469- "${SSH_OPTS[@]}" \
470- "$JSON_FILE" \
471- "${TARGET}:${REMOTE_PREFIX}.json"
472-
473-scp \
474- -q \
475- "${SSH_OPTS[@]}" \
476- "$TXT_FILE" \
477- "${TARGET}:${REMOTE_PREFIX}.txt"
=== MODE_REFERENCES ===
11: read-only
12: verify
13: change
14: transport-test
51: read-only|verify|change|transport-test)
150: change)
357: changes_made,
396: "changes_made": changes_made == "true",
CLUSTER_REFERENCE_MODE_SEMANTICS=COMPLETE
OUTPUT_END
CHAT_OUTPUT_END