runtime: publish RUNTIME-RECEIVER-FULL-023
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,322 @@
|
|||||||
|
CHAT_OUTPUT_BEGIN
|
||||||
|
COMMAND_ID=RUNTIME-RECEIVER-FULL-023
|
||||||
|
STATUS=OK
|
||||||
|
RC=0
|
||||||
|
HOST=pve01
|
||||||
|
MODE=read-only
|
||||||
|
COMPONENT=gitea-runtime
|
||||||
|
REFERENCE_REGISTER_CHECK=OK
|
||||||
|
REFERENCE_SHA256=f6f7749fda946015116cdbbd754c7e7f20356b4a6cf4df330a1b0b8fdf59cbb2
|
||||||
|
ERROR_REGISTER_CHECK=OK
|
||||||
|
ERROR_REGISTER_SHA256=ab8308549db90d235b591af0581580c3c7ba1b287b129cc6111c2eef0a0f9ecc
|
||||||
|
COMMAND_SHA256=d17752c0235202b5d9f1822d70680ea57dd4578bde6bbc906c7569c0d0463bb0
|
||||||
|
DUPLICATE_FAILED_COMMAND_BLOCKED=false
|
||||||
|
EXECUTION_STARTED=true
|
||||||
|
CHANGES_MADE=false
|
||||||
|
SANITIZED=yes
|
||||||
|
SECRETS_INCLUDED=no
|
||||||
|
PRIVATE_ADDRESSES_INCLUDED=no
|
||||||
|
RAW_EVIDENCE_SHA256=2c183b9ced4334e5ff12c1ba35dceb1f7f6ef113298d44bf3fb80ca07305b8b3
|
||||||
|
SANITIZED_OUTPUT_SHA256=cd4ccc98c3b9dff7bd1f5557b41fb60d073f889a31f3fd7d86900eecbe15dff4
|
||||||
|
OUTPUT_BEGIN
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
umask 027
|
||||||
|
|
||||||
|
HOST_DATA='/opt/stacks/gitea/data'
|
||||||
|
BARE_CONTAINER='/data/git/repositories/homelab-admin/homelab-public-context.git'
|
||||||
|
|
||||||
|
PUBLIC_DIR="${HOST_DATA}/gitea/public/.well-known/homelab-runtime"
|
||||||
|
STAGE_DIR="${HOST_DATA}/gitea/runtime-stage"
|
||||||
|
WORK_ROOT="${HOST_DATA}/gitea/runtime-work"
|
||||||
|
|
||||||
|
LOCK='/run/lock/homelab-runtime-receive.lock'
|
||||||
|
|
||||||
|
[ "$#" -eq 3 ] || {
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=USAGE'
|
||||||
|
exit 64
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_ID="$1"
|
||||||
|
JSON_FILE="$2"
|
||||||
|
TXT_FILE="$3"
|
||||||
|
|
||||||
|
case "$COMMAND_ID" in
|
||||||
|
''|*[!A-Za-z0-9._-]*)
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=INVALID_COMMAND_ID'
|
||||||
|
exit 65
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[ -f "$JSON_FILE" ] || {
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=JSON_FILE_MISSING'
|
||||||
|
exit 66
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -f "$TXT_FILE" ] || {
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=TXT_FILE_MISSING'
|
||||||
|
exit 67
|
||||||
|
}
|
||||||
|
|
||||||
|
exec 9>"$LOCK"
|
||||||
|
flock -w 60 9 || {
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=RECEIVER_LOCK_TIMEOUT'
|
||||||
|
exit 68
|
||||||
|
}
|
||||||
|
|
||||||
|
python3 - "$JSON_FILE" "$TXT_FILE" "$COMMAND_ID" <<'PY'
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
json_path = Path(sys.argv[1])
|
||||||
|
text_path = Path(sys.argv[2])
|
||||||
|
expected_id = sys.argv[3]
|
||||||
|
|
||||||
|
if json_path.stat().st_size > 524_288:
|
||||||
|
raise SystemExit("JSON_TOO_LARGE")
|
||||||
|
|
||||||
|
if text_path.stat().st_size > 1_048_576:
|
||||||
|
raise SystemExit("TEXT_TOO_LARGE")
|
||||||
|
|
||||||
|
json_text = json_path.read_text(encoding="utf-8")
|
||||||
|
text = text_path.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
data = json.loads(json_text)
|
||||||
|
|
||||||
|
if data.get("command_id") != expected_id:
|
||||||
|
raise SystemExit("COMMAND_ID_MISMATCH")
|
||||||
|
|
||||||
|
if data.get("sanitized") is not True:
|
||||||
|
raise SystemExit("SANITIZED_FLAG_REQUIRED")
|
||||||
|
|
||||||
|
if data.get("secrets_included") is not False:
|
||||||
|
raise SystemExit("SECRETS_FLAG_INVALID")
|
||||||
|
|
||||||
|
if data.get("private_addresses_included") is not False:
|
||||||
|
raise SystemExit("PRIVATE_ADDRESS_FLAG_INVALID")
|
||||||
|
|
||||||
|
combined = json_text + "\n" + text
|
||||||
|
|
||||||
|
patterns = [
|
||||||
|
r"-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----",
|
||||||
|
r"(?i)(password|passwd|passphrase|authorization|api[_-]?key|access[_-]?token|refresh[_-]?token|private[_-]?key)\s*[:=]\s*\S+",
|
||||||
|
r"(?i)/root/\.ssh",
|
||||||
|
r"(?i)[SENSITIVE_PATH]",
|
||||||
|
r"(?i)rclone\.conf",
|
||||||
|
r"\b10(?:\.\d{1,3}){3}\b",
|
||||||
|
r"\b192\.168(?:\.\d{1,3}){2}\b",
|
||||||
|
r"\b172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2}\b",
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
if re.search(pattern, combined):
|
||||||
|
raise SystemExit("FORBIDDEN_PUBLIC_CONTENT")
|
||||||
|
|
||||||
|
print("PAYLOAD_VALIDATION=OK")
|
||||||
|
PY
|
||||||
|
|
||||||
|
GIT_UID="$(docker exec gitea id -u git)"
|
||||||
|
GIT_GID="$(docker exec gitea id -g git)"
|
||||||
|
|
||||||
|
mkdir -p \
|
||||||
|
"$PUBLIC_DIR" \
|
||||||
|
"$STAGE_DIR" \
|
||||||
|
"$WORK_ROOT"
|
||||||
|
|
||||||
|
chown "$GIT_UID:$GIT_GID" \
|
||||||
|
"$STAGE_DIR" \
|
||||||
|
"$WORK_ROOT"
|
||||||
|
|
||||||
|
install \
|
||||||
|
-o "$GIT_UID" \
|
||||||
|
-g "$GIT_GID" \
|
||||||
|
-m 0644 \
|
||||||
|
"$JSON_FILE" \
|
||||||
|
"$STAGE_DIR/latest.json"
|
||||||
|
|
||||||
|
install \
|
||||||
|
-o "$GIT_UID" \
|
||||||
|
-g "$GIT_GID" \
|
||||||
|
-m 0644 \
|
||||||
|
"$TXT_FILE" \
|
||||||
|
"$STAGE_DIR/latest.txt"
|
||||||
|
|
||||||
|
cat > "$STAGE_DIR/README.md" <<'DOC'
|
||||||
|
# Homelab assistant runtime
|
||||||
|
|
||||||
|
Sanitized command results for read-only assistant inspection.
|
||||||
|
|
||||||
|
Credentials, authentication data, private keys, private IP addresses and
|
||||||
|
unfiltered configuration files are prohibited.
|
||||||
|
DOC
|
||||||
|
|
||||||
|
chown "$GIT_UID:$GIT_GID" "$STAGE_DIR/README.md"
|
||||||
|
chmod 0644 "$STAGE_DIR/README.md"
|
||||||
|
|
||||||
|
PUBLISHED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
WORK_CONTAINER="/data/gitea/runtime-work/${COMMAND_ID}.$$"
|
||||||
|
|
||||||
|
COMMIT_SHA="$(
|
||||||
|
docker exec \
|
||||||
|
-u "${GIT_UID}:${GIT_GID}" \
|
||||||
|
-e COMMAND_ID="$COMMAND_ID" \
|
||||||
|
-e PUBLISHED_AT="$PUBLISHED_AT" \
|
||||||
|
-e WORK_CONTAINER="$WORK_CONTAINER" \
|
||||||
|
gitea sh -lc '
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
BARE="/data/git/repositories/homelab-admin/homelab-public-context.git"
|
||||||
|
STAGE="/data/gitea/runtime-stage"
|
||||||
|
WORK="$WORK_CONTAINER"
|
||||||
|
|
||||||
|
rm -rf "$WORK"
|
||||||
|
|
||||||
|
git clone --quiet "$BARE" "$WORK"
|
||||||
|
|
||||||
|
cd "$WORK"
|
||||||
|
|
||||||
|
git config user.name "homelab-runtime-publisher"
|
||||||
|
git config user.email "homelab-runtime@localhost"
|
||||||
|
|
||||||
|
if git show-ref \
|
||||||
|
--verify \
|
||||||
|
--quiet \
|
||||||
|
refs/remotes/origin/assistant-runtime
|
||||||
|
then
|
||||||
|
git checkout \
|
||||||
|
--quiet \
|
||||||
|
-B assistant-runtime \
|
||||||
|
origin/assistant-runtime
|
||||||
|
else
|
||||||
|
git checkout \
|
||||||
|
--quiet \
|
||||||
|
--orphan assistant-runtime
|
||||||
|
|
||||||
|
git rm -rf . >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p runtime/history
|
||||||
|
|
||||||
|
cp "$STAGE/latest.json" runtime/latest.json
|
||||||
|
cp "$STAGE/latest.txt" runtime/latest.txt
|
||||||
|
cp "$STAGE/latest.json" "runtime/history/${COMMAND_ID}.json"
|
||||||
|
cp "$STAGE/latest.txt" "runtime/history/${COMMAND_ID}.txt"
|
||||||
|
cp "$STAGE/README.md" README.md
|
||||||
|
|
||||||
|
git add \
|
||||||
|
README.md \
|
||||||
|
runtime/latest.json \
|
||||||
|
runtime/latest.txt \
|
||||||
|
"runtime/history/${COMMAND_ID}.json"
|
||||||
|
|
||||||
|
git add "runtime/history/${COMMAND_ID}.txt"
|
||||||
|
|
||||||
|
if ! git diff --cached --quiet
|
||||||
|
then
|
||||||
|
GIT_AUTHOR_DATE="$PUBLISHED_AT" \
|
||||||
|
GIT_COMMITTER_DATE="$PUBLISHED_AT" \
|
||||||
|
git commit \
|
||||||
|
--quiet \
|
||||||
|
-m "runtime: publish ${COMMAND_ID}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
COMMIT="$(git rev-parse HEAD)"
|
||||||
|
|
||||||
|
git --git-dir="$BARE" \
|
||||||
|
fetch \
|
||||||
|
--quiet \
|
||||||
|
"$WORK" \
|
||||||
|
refs/heads/assistant-runtime
|
||||||
|
|
||||||
|
OLD="$(
|
||||||
|
git --git-dir="$BARE" \
|
||||||
|
rev-parse refs/heads/assistant-runtime \
|
||||||
|
2>/dev/null ||
|
||||||
|
true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [ -n "$OLD" ]
|
||||||
|
then
|
||||||
|
git --git-dir="$BARE" \
|
||||||
|
update-ref \
|
||||||
|
refs/heads/assistant-runtime \
|
||||||
|
"$COMMIT" \
|
||||||
|
"$OLD"
|
||||||
|
else
|
||||||
|
git --git-dir="$BARE" \
|
||||||
|
update-ref \
|
||||||
|
refs/heads/assistant-runtime \
|
||||||
|
"$COMMIT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "$WORK"
|
||||||
|
|
||||||
|
printf "%s\n" "$COMMIT"
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
JSON_TMP="$(mktemp "${PUBLIC_DIR}/.latest.json.XXXXXX")"
|
||||||
|
TXT_TMP="$(mktemp "${PUBLIC_DIR}/.latest.txt.XXXXXX")"
|
||||||
|
INDEX_TMP="$(mktemp "${PUBLIC_DIR}/.index.json.XXXXXX")"
|
||||||
|
|
||||||
|
install -m 0644 "$JSON_FILE" "$JSON_TMP"
|
||||||
|
install -m 0644 "$TXT_FILE" "$TXT_TMP"
|
||||||
|
|
||||||
|
python3 - \
|
||||||
|
"$COMMAND_ID" \
|
||||||
|
"$PUBLISHED_AT" \
|
||||||
|
"$COMMIT_SHA" >"$INDEX_TMP" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
command_id, published_at, commit_sha = sys.argv[1:4]
|
||||||
|
|
||||||
|
print(json.dumps({
|
||||||
|
"schema_version": 1,
|
||||||
|
"channel": "homelab-runtime",
|
||||||
|
"branch": "assistant-runtime",
|
||||||
|
"latest_command_id": command_id,
|
||||||
|
"published_at_utc": published_at,
|
||||||
|
"commit_sha": commit_sha,
|
||||||
|
"latest_json_url": (
|
||||||
|
"https://git.gram1.ru/.well-known/"
|
||||||
|
"homelab-runtime/latest.json"
|
||||||
|
),
|
||||||
|
"latest_text_url": (
|
||||||
|
"https://git.gram1.ru/.well-known/"
|
||||||
|
"homelab-runtime/latest.txt"
|
||||||
|
)
|
||||||
|
}, ensure_ascii=False, indent=2))
|
||||||
|
PY
|
||||||
|
|
||||||
|
chmod 0644 "$INDEX_TMP"
|
||||||
|
|
||||||
|
mv -f "$JSON_TMP" "$PUBLIC_DIR/latest.json"
|
||||||
|
mv -f "$TXT_TMP" "$PUBLIC_DIR/latest.txt"
|
||||||
|
mv -f "$INDEX_TMP" "$PUBLIC_DIR/index.json"
|
||||||
|
|
||||||
|
curl -fsS \
|
||||||
|
--max-time 10 \
|
||||||
|
http://127.0.0.1:3002/.well-known/homelab-runtime/latest.json |
|
||||||
|
grep -F "$COMMAND_ID" >/dev/null
|
||||||
|
|
||||||
|
curl -fsS \
|
||||||
|
--max-time 10 \
|
||||||
|
"http://127.0.0.1:3002/homelab-admin/homelab-public-context/raw/branch/assistant-runtime/runtime/latest.json" |
|
||||||
|
grep -F "$COMMAND_ID" >/dev/null
|
||||||
|
|
||||||
|
echo 'RESULT=OK'
|
||||||
|
echo "COMMAND_ID=$COMMAND_ID"
|
||||||
|
echo "COMMIT_SHA=$COMMIT_SHA"
|
||||||
|
echo 'STATIC_PUBLICATION=OK'
|
||||||
|
echo 'RAW_BRANCH_PUBLICATION=OK'
|
||||||
|
|
||||||
|
OUTPUT_END
|
||||||
|
CHAT_OUTPUT_END
|
||||||
+8
-8
File diff suppressed because one or more lines are too long
+302
-68
@@ -1,88 +1,322 @@
|
|||||||
CHAT_OUTPUT_BEGIN
|
CHAT_OUTPUT_BEGIN
|
||||||
COMMAND_ID=BOOTSTRAP-FILE-DIAG-002
|
COMMAND_ID=RUNTIME-RECEIVER-FULL-023
|
||||||
STATUS=OK
|
STATUS=OK
|
||||||
RC=0
|
RC=0
|
||||||
HOST=pve01
|
HOST=pve01
|
||||||
MODE=read-only
|
MODE=read-only
|
||||||
COMPONENT=gitea-bootstrap
|
COMPONENT=gitea-runtime
|
||||||
REFERENCE_REGISTER_CHECK=OK
|
REFERENCE_REGISTER_CHECK=OK
|
||||||
REFERENCE_SHA256=f6f7749fda946015116cdbbd754c7e7f20356b4a6cf4df330a1b0b8fdf59cbb2
|
REFERENCE_SHA256=f6f7749fda946015116cdbbd754c7e7f20356b4a6cf4df330a1b0b8fdf59cbb2
|
||||||
ERROR_REGISTER_CHECK=OK
|
ERROR_REGISTER_CHECK=OK
|
||||||
ERROR_REGISTER_SHA256=ab8308549db90d235b591af0581580c3c7ba1b287b129cc6111c2eef0a0f9ecc
|
ERROR_REGISTER_SHA256=ab8308549db90d235b591af0581580c3c7ba1b287b129cc6111c2eef0a0f9ecc
|
||||||
COMMAND_SHA256=ff7fad42e98aa003067d7496afc43ad2c9ad5fe91eb84b8eb79aeb41674bb1be
|
COMMAND_SHA256=d17752c0235202b5d9f1822d70680ea57dd4578bde6bbc906c7569c0d0463bb0
|
||||||
DUPLICATE_FAILED_COMMAND_BLOCKED=false
|
DUPLICATE_FAILED_COMMAND_BLOCKED=false
|
||||||
EXECUTION_STARTED=true
|
EXECUTION_STARTED=true
|
||||||
CHANGES_MADE=false
|
CHANGES_MADE=false
|
||||||
SANITIZED=yes
|
SANITIZED=yes
|
||||||
SECRETS_INCLUDED=no
|
SECRETS_INCLUDED=no
|
||||||
PRIVATE_ADDRESSES_INCLUDED=no
|
PRIVATE_ADDRESSES_INCLUDED=no
|
||||||
RAW_EVIDENCE_SHA256=2c25590ae8d63b455ecfea329a8495fa57f04ba43113a348dace1265c58ce33e
|
RAW_EVIDENCE_SHA256=2c183b9ced4334e5ff12c1ba35dceb1f7f6ef113298d44bf3fb80ca07305b8b3
|
||||||
SANITIZED_OUTPUT_SHA256=2c25590ae8d63b455ecfea329a8495fa57f04ba43113a348dace1265c58ce33e
|
SANITIZED_OUTPUT_SHA256=cd4ccc98c3b9dff7bd1f5557b41fb60d073f889a31f3fd7d86900eecbe15dff4
|
||||||
OUTPUT_BEGIN
|
OUTPUT_BEGIN
|
||||||
{
|
#!/usr/bin/env bash
|
||||||
"schema_version": 2,
|
set -Eeuo pipefail
|
||||||
"cluster_id": "homelab",
|
umask 027
|
||||||
"status": "READY",
|
|
||||||
"generated_at_utc": "2026-07-21T07:02:17Z",
|
HOST_DATA='/opt/stacks/gitea/data'
|
||||||
"context": {
|
BARE_CONTAINER='/data/git/repositories/homelab-admin/homelab-public-context.git'
|
||||||
"full_context_command_id": "CONTEXT-SOURCES-FULL-002",
|
|
||||||
"full_context_commit": "e23caf794cc68ce5fa6f8922efa7f2a35c8aa6e0",
|
PUBLIC_DIR="${HOST_DATA}/gitea/public/.well-known/homelab-runtime"
|
||||||
"full_context_sha256": "621830f58b028a0116a42964d02bdca31c280d6ff358f518d0e8f24c2ba01366",
|
STAGE_DIR="${HOST_DATA}/gitea/runtime-stage"
|
||||||
"full_context_url": "https://git.gram1.ru/homelab-admin/homelab-public-context/raw/commit/e23caf794cc68ce5fa6f8922efa7f2a35c8aa6e0/runtime/history/CONTEXT-SOURCES-FULL-002.txt",
|
WORK_ROOT="${HOST_DATA}/gitea/runtime-work"
|
||||||
"context_index_command_id": "CONTEXT-INDEX-001",
|
|
||||||
"context_index_commit": "055521f14fbb0780bdb501c7d7be755350d45091",
|
LOCK='/run/lock/homelab-runtime-receive.lock'
|
||||||
"context_index_url": "https://git.gram1.ru/homelab-admin/homelab-public-context/raw/commit/055521f14fbb0780bdb501c7d7be755350d45091/runtime/history/CONTEXT-INDEX-001.json",
|
|
||||||
"reference_sha256": "f6f7749fda946015116cdbbd754c7e7f20356b4a6cf4df330a1b0b8fdf59cbb2",
|
[ "$#" -eq 3 ] || {
|
||||||
"error_register_sha256": "ab8308549db90d235b591af0581580c3c7ba1b287b129cc6111c2eef0a0f9ecc",
|
echo 'RESULT=FAIL'
|
||||||
"error_register_embedded_in_full_context": true
|
echo 'REASON=USAGE'
|
||||||
},
|
exit 64
|
||||||
"runtime": {
|
|
||||||
"history_url": "https://git.gram1.ru/homelab-admin/homelab-public-context/src/branch/assistant-runtime/runtime/history",
|
|
||||||
"latest_branch_url": "https://git.gram1.ru/homelab-admin/homelab-public-context/raw/branch/assistant-runtime/runtime/latest.json",
|
|
||||||
"result_confirmation": "immutable_history_by_command_id",
|
|
||||||
"latest_json_is_authoritative": false
|
|
||||||
},
|
|
||||||
"operating_contract": {
|
|
||||||
"load_bootstrap_before_commands": true,
|
|
||||||
"load_error_register_before_commands": true,
|
|
||||||
"check_previous_command_result": true,
|
|
||||||
"reject_known_failed_exact_repeats": true,
|
|
||||||
"one_command_per_step": true,
|
|
||||||
"manual_terminal_output_required": false,
|
|
||||||
"runner": "homelab-chat-run",
|
|
||||||
"confirmation_tokens": [
|
|
||||||
".",
|
|
||||||
",",
|
|
||||||
"\\"
|
|
||||||
],
|
|
||||||
"immediate_next_command": true,
|
|
||||||
"command_display": "collapsible_details",
|
|
||||||
"double_pre_command_check": true
|
|
||||||
},
|
|
||||||
"privacy": {
|
|
||||||
"sanitized": true,
|
|
||||||
"secrets_included": false,
|
|
||||||
"private_addresses_included": false
|
|
||||||
},
|
|
||||||
"entrypoints": {
|
|
||||||
"full_context_url": "https://git.gram1.ru/homelab-admin/homelab-public-context/raw/commit/e23caf794cc68ce5fa6f8922efa7f2a35c8aa6e0/runtime/history/CONTEXT-SOURCES-FULL-002.txt",
|
|
||||||
"errors_index_url": "https://git.gram1.ru/homelab-admin/homelab-public-context/raw/commit/a615069de3d32564fc4de29baff949bdaf811caf/runtime/history/ERRORS-INDEX-004.json",
|
|
||||||
"workflow_contract_url": "https://git.gram1.ru/homelab-admin/homelab-public-context/raw/commit/2f37a4eb600528d03f5c570af8c5aa0e463b1798/runtime/history/WORKFLOW-CONTRACT-003.json",
|
|
||||||
"double_check_rule_url": "https://git.gram1.ru/homelab-admin/homelab-public-context/raw/commit/d1061e47e7955dba9c73a2841d4eacf02ccc1bf5/runtime/policy/double-check-rule.json"
|
|
||||||
},
|
|
||||||
"freshness": {
|
|
||||||
"max_age_seconds": 86400,
|
|
||||||
"stale_requires_refresh": true
|
|
||||||
},
|
|
||||||
"coverage": {
|
|
||||||
"status": "PARTIAL",
|
|
||||||
"missing": [
|
|
||||||
"automatic_refresh",
|
|
||||||
"per_node_freshness",
|
|
||||||
"structured_service_inventory"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COMMAND_ID="$1"
|
||||||
|
JSON_FILE="$2"
|
||||||
|
TXT_FILE="$3"
|
||||||
|
|
||||||
|
case "$COMMAND_ID" in
|
||||||
|
''|*[!A-Za-z0-9._-]*)
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=INVALID_COMMAND_ID'
|
||||||
|
exit 65
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[ -f "$JSON_FILE" ] || {
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=JSON_FILE_MISSING'
|
||||||
|
exit 66
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -f "$TXT_FILE" ] || {
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=TXT_FILE_MISSING'
|
||||||
|
exit 67
|
||||||
|
}
|
||||||
|
|
||||||
|
exec 9>"$LOCK"
|
||||||
|
flock -w 60 9 || {
|
||||||
|
echo 'RESULT=FAIL'
|
||||||
|
echo 'REASON=RECEIVER_LOCK_TIMEOUT'
|
||||||
|
exit 68
|
||||||
|
}
|
||||||
|
|
||||||
|
python3 - "$JSON_FILE" "$TXT_FILE" "$COMMAND_ID" <<'PY'
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
json_path = Path(sys.argv[1])
|
||||||
|
text_path = Path(sys.argv[2])
|
||||||
|
expected_id = sys.argv[3]
|
||||||
|
|
||||||
|
if json_path.stat().st_size > 524_288:
|
||||||
|
raise SystemExit("JSON_TOO_LARGE")
|
||||||
|
|
||||||
|
if text_path.stat().st_size > 1_048_576:
|
||||||
|
raise SystemExit("TEXT_TOO_LARGE")
|
||||||
|
|
||||||
|
json_text = json_path.read_text(encoding="utf-8")
|
||||||
|
text = text_path.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
data = json.loads(json_text)
|
||||||
|
|
||||||
|
if data.get("command_id") != expected_id:
|
||||||
|
raise SystemExit("COMMAND_ID_MISMATCH")
|
||||||
|
|
||||||
|
if data.get("sanitized") is not True:
|
||||||
|
raise SystemExit("SANITIZED_FLAG_REQUIRED")
|
||||||
|
|
||||||
|
if data.get("secrets_included") is not False:
|
||||||
|
raise SystemExit("SECRETS_FLAG_INVALID")
|
||||||
|
|
||||||
|
if data.get("private_addresses_included") is not False:
|
||||||
|
raise SystemExit("PRIVATE_ADDRESS_FLAG_INVALID")
|
||||||
|
|
||||||
|
combined = json_text + "\n" + text
|
||||||
|
|
||||||
|
patterns = [
|
||||||
|
r"-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----",
|
||||||
|
r"(?i)(password|passwd|passphrase|authorization|api[_-]?key|access[_-]?token|refresh[_-]?token|private[_-]?key)\s*[:=]\s*\S+",
|
||||||
|
r"(?i)/root/\.ssh",
|
||||||
|
r"(?i)[SENSITIVE_PATH]",
|
||||||
|
r"(?i)rclone\.conf",
|
||||||
|
r"\b10(?:\.\d{1,3}){3}\b",
|
||||||
|
r"\b192\.168(?:\.\d{1,3}){2}\b",
|
||||||
|
r"\b172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2}\b",
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
if re.search(pattern, combined):
|
||||||
|
raise SystemExit("FORBIDDEN_PUBLIC_CONTENT")
|
||||||
|
|
||||||
|
print("PAYLOAD_VALIDATION=OK")
|
||||||
|
PY
|
||||||
|
|
||||||
|
GIT_UID="$(docker exec gitea id -u git)"
|
||||||
|
GIT_GID="$(docker exec gitea id -g git)"
|
||||||
|
|
||||||
|
mkdir -p \
|
||||||
|
"$PUBLIC_DIR" \
|
||||||
|
"$STAGE_DIR" \
|
||||||
|
"$WORK_ROOT"
|
||||||
|
|
||||||
|
chown "$GIT_UID:$GIT_GID" \
|
||||||
|
"$STAGE_DIR" \
|
||||||
|
"$WORK_ROOT"
|
||||||
|
|
||||||
|
install \
|
||||||
|
-o "$GIT_UID" \
|
||||||
|
-g "$GIT_GID" \
|
||||||
|
-m 0644 \
|
||||||
|
"$JSON_FILE" \
|
||||||
|
"$STAGE_DIR/latest.json"
|
||||||
|
|
||||||
|
install \
|
||||||
|
-o "$GIT_UID" \
|
||||||
|
-g "$GIT_GID" \
|
||||||
|
-m 0644 \
|
||||||
|
"$TXT_FILE" \
|
||||||
|
"$STAGE_DIR/latest.txt"
|
||||||
|
|
||||||
|
cat > "$STAGE_DIR/README.md" <<'DOC'
|
||||||
|
# Homelab assistant runtime
|
||||||
|
|
||||||
|
Sanitized command results for read-only assistant inspection.
|
||||||
|
|
||||||
|
Credentials, authentication data, private keys, private IP addresses and
|
||||||
|
unfiltered configuration files are prohibited.
|
||||||
|
DOC
|
||||||
|
|
||||||
|
chown "$GIT_UID:$GIT_GID" "$STAGE_DIR/README.md"
|
||||||
|
chmod 0644 "$STAGE_DIR/README.md"
|
||||||
|
|
||||||
|
PUBLISHED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
WORK_CONTAINER="/data/gitea/runtime-work/${COMMAND_ID}.$$"
|
||||||
|
|
||||||
|
COMMIT_SHA="$(
|
||||||
|
docker exec \
|
||||||
|
-u "${GIT_UID}:${GIT_GID}" \
|
||||||
|
-e COMMAND_ID="$COMMAND_ID" \
|
||||||
|
-e PUBLISHED_AT="$PUBLISHED_AT" \
|
||||||
|
-e WORK_CONTAINER="$WORK_CONTAINER" \
|
||||||
|
gitea sh -lc '
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
BARE="/data/git/repositories/homelab-admin/homelab-public-context.git"
|
||||||
|
STAGE="/data/gitea/runtime-stage"
|
||||||
|
WORK="$WORK_CONTAINER"
|
||||||
|
|
||||||
|
rm -rf "$WORK"
|
||||||
|
|
||||||
|
git clone --quiet "$BARE" "$WORK"
|
||||||
|
|
||||||
|
cd "$WORK"
|
||||||
|
|
||||||
|
git config user.name "homelab-runtime-publisher"
|
||||||
|
git config user.email "homelab-runtime@localhost"
|
||||||
|
|
||||||
|
if git show-ref \
|
||||||
|
--verify \
|
||||||
|
--quiet \
|
||||||
|
refs/remotes/origin/assistant-runtime
|
||||||
|
then
|
||||||
|
git checkout \
|
||||||
|
--quiet \
|
||||||
|
-B assistant-runtime \
|
||||||
|
origin/assistant-runtime
|
||||||
|
else
|
||||||
|
git checkout \
|
||||||
|
--quiet \
|
||||||
|
--orphan assistant-runtime
|
||||||
|
|
||||||
|
git rm -rf . >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p runtime/history
|
||||||
|
|
||||||
|
cp "$STAGE/latest.json" runtime/latest.json
|
||||||
|
cp "$STAGE/latest.txt" runtime/latest.txt
|
||||||
|
cp "$STAGE/latest.json" "runtime/history/${COMMAND_ID}.json"
|
||||||
|
cp "$STAGE/latest.txt" "runtime/history/${COMMAND_ID}.txt"
|
||||||
|
cp "$STAGE/README.md" README.md
|
||||||
|
|
||||||
|
git add \
|
||||||
|
README.md \
|
||||||
|
runtime/latest.json \
|
||||||
|
runtime/latest.txt \
|
||||||
|
"runtime/history/${COMMAND_ID}.json"
|
||||||
|
|
||||||
|
git add "runtime/history/${COMMAND_ID}.txt"
|
||||||
|
|
||||||
|
if ! git diff --cached --quiet
|
||||||
|
then
|
||||||
|
GIT_AUTHOR_DATE="$PUBLISHED_AT" \
|
||||||
|
GIT_COMMITTER_DATE="$PUBLISHED_AT" \
|
||||||
|
git commit \
|
||||||
|
--quiet \
|
||||||
|
-m "runtime: publish ${COMMAND_ID}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
COMMIT="$(git rev-parse HEAD)"
|
||||||
|
|
||||||
|
git --git-dir="$BARE" \
|
||||||
|
fetch \
|
||||||
|
--quiet \
|
||||||
|
"$WORK" \
|
||||||
|
refs/heads/assistant-runtime
|
||||||
|
|
||||||
|
OLD="$(
|
||||||
|
git --git-dir="$BARE" \
|
||||||
|
rev-parse refs/heads/assistant-runtime \
|
||||||
|
2>/dev/null ||
|
||||||
|
true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [ -n "$OLD" ]
|
||||||
|
then
|
||||||
|
git --git-dir="$BARE" \
|
||||||
|
update-ref \
|
||||||
|
refs/heads/assistant-runtime \
|
||||||
|
"$COMMIT" \
|
||||||
|
"$OLD"
|
||||||
|
else
|
||||||
|
git --git-dir="$BARE" \
|
||||||
|
update-ref \
|
||||||
|
refs/heads/assistant-runtime \
|
||||||
|
"$COMMIT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "$WORK"
|
||||||
|
|
||||||
|
printf "%s\n" "$COMMIT"
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
|
||||||
|
JSON_TMP="$(mktemp "${PUBLIC_DIR}/.latest.json.XXXXXX")"
|
||||||
|
TXT_TMP="$(mktemp "${PUBLIC_DIR}/.latest.txt.XXXXXX")"
|
||||||
|
INDEX_TMP="$(mktemp "${PUBLIC_DIR}/.index.json.XXXXXX")"
|
||||||
|
|
||||||
|
install -m 0644 "$JSON_FILE" "$JSON_TMP"
|
||||||
|
install -m 0644 "$TXT_FILE" "$TXT_TMP"
|
||||||
|
|
||||||
|
python3 - \
|
||||||
|
"$COMMAND_ID" \
|
||||||
|
"$PUBLISHED_AT" \
|
||||||
|
"$COMMIT_SHA" >"$INDEX_TMP" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
command_id, published_at, commit_sha = sys.argv[1:4]
|
||||||
|
|
||||||
|
print(json.dumps({
|
||||||
|
"schema_version": 1,
|
||||||
|
"channel": "homelab-runtime",
|
||||||
|
"branch": "assistant-runtime",
|
||||||
|
"latest_command_id": command_id,
|
||||||
|
"published_at_utc": published_at,
|
||||||
|
"commit_sha": commit_sha,
|
||||||
|
"latest_json_url": (
|
||||||
|
"https://git.gram1.ru/.well-known/"
|
||||||
|
"homelab-runtime/latest.json"
|
||||||
|
),
|
||||||
|
"latest_text_url": (
|
||||||
|
"https://git.gram1.ru/.well-known/"
|
||||||
|
"homelab-runtime/latest.txt"
|
||||||
|
)
|
||||||
|
}, ensure_ascii=False, indent=2))
|
||||||
|
PY
|
||||||
|
|
||||||
|
chmod 0644 "$INDEX_TMP"
|
||||||
|
|
||||||
|
mv -f "$JSON_TMP" "$PUBLIC_DIR/latest.json"
|
||||||
|
mv -f "$TXT_TMP" "$PUBLIC_DIR/latest.txt"
|
||||||
|
mv -f "$INDEX_TMP" "$PUBLIC_DIR/index.json"
|
||||||
|
|
||||||
|
curl -fsS \
|
||||||
|
--max-time 10 \
|
||||||
|
http://127.0.0.1:3002/.well-known/homelab-runtime/latest.json |
|
||||||
|
grep -F "$COMMAND_ID" >/dev/null
|
||||||
|
|
||||||
|
curl -fsS \
|
||||||
|
--max-time 10 \
|
||||||
|
"http://127.0.0.1:3002/homelab-admin/homelab-public-context/raw/branch/assistant-runtime/runtime/latest.json" |
|
||||||
|
grep -F "$COMMAND_ID" >/dev/null
|
||||||
|
|
||||||
|
echo 'RESULT=OK'
|
||||||
|
echo "COMMAND_ID=$COMMAND_ID"
|
||||||
|
echo "COMMIT_SHA=$COMMIT_SHA"
|
||||||
|
echo 'STATIC_PUBLICATION=OK'
|
||||||
|
echo 'RAW_BRANCH_PUBLICATION=OK'
|
||||||
|
|
||||||
OUTPUT_END
|
OUTPUT_END
|
||||||
CHAT_OUTPUT_END
|
CHAT_OUTPUT_END
|
||||||
|
|||||||
Reference in New Issue
Block a user