J94's picture
Initial Space upload
3436bdd verified
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
MANIFEST_PATH="${1:-}"
POLICY_PATH="$ROOT/policy/exec_allowlist_v0.json"
if [ -z "$MANIFEST_PATH" ]; then
echo "usage: ./runtime/execute_manifest.sh <manifest.json>" >&2
exit 1
fi
if [ ! -f "$MANIFEST_PATH" ]; then
echo "manifest not found: $MANIFEST_PATH" >&2
exit 1
fi
MANIFEST_ABS="$(cd "$(dirname "$MANIFEST_PATH")" && pwd)/$(basename "$MANIFEST_PATH")"
INPUT_SHA256="$(shasum -a 256 "$MANIFEST_ABS" | awk '{print $1}')"
DOC_KIND="$(jq -r 'if .version == "work_manifest_v0" then "work_manifest" elif has("task_id") and has("operations") then "utir" else "unknown" end' "$MANIFEST_ABS")"
if [ "$DOC_KIND" = "unknown" ]; then
echo "unsupported manifest packet: $MANIFEST_ABS" >&2
exit 1
fi
DOC_ID="$(jq -r 'if has("manifest_id") then .manifest_id else .task_id end' "$MANIFEST_ABS")"
DOC_GOAL="$(jq -r 'if has("goal") then .goal else .description end' "$MANIFEST_ABS")"
DOC_LANE="$(jq -r 'if has("lane") then .lane else "execution" end' "$MANIFEST_ABS")"
RUN_ID="$(date -u +"%Y%m%dT%H%M%SZ")-$DOC_ID"
OUT_DIR="$ROOT/runs/runtime/$RUN_ID"
RECEIPT_PATH="$OUT_DIR/receipt.json"
SUMMARY_PATH="$OUT_DIR/summary.json"
GRAPH_PATH="$OUT_DIR/graph_state.json"
EFFECTS_NDJSON="$OUT_DIR/effects.ndjson"
mkdir -p "$OUT_DIR"
: > "$EFFECTS_NDJSON"
if [ "$DOC_KIND" = "work_manifest" ]; then
EXECUTION_GATE="$(jq -r '.execution_gate' "$MANIFEST_ABS")"
if [ "$EXECUTION_GATE" != "true" ]; then
echo "execution gate is closed in manifest: $MANIFEST_ABS" >&2
exit 1
fi
fi
append_effect() {
printf '%s\n' "$1" >> "$EFFECTS_NDJSON"
}
file_sha_or_null() {
local file="$1"
if [ -s "$file" ]; then
shasum -a 256 "$file" | awk '{print $1}'
else
echo ""
fi
}
is_command_allowed() {
local cmd="$1"
local first_word
first_word="$(printf '%s' "$cmd" | awk '{print $1}')"
if [ -z "$first_word" ]; then
return 1
fi
if jq -e --arg cmd "$first_word" '.allowed_commands | index($cmd)' "$POLICY_PATH" >/dev/null; then
return 0
fi
return 1
}
has_blocked_pattern() {
local cmd="$1"
while IFS= read -r pattern; do
[ -z "$pattern" ] && continue
case "$cmd" in
*"$pattern"*) return 0 ;;
esac
done < <(jq -r '.blocked_patterns[]' "$POLICY_PATH")
return 1
}
is_path_allowed() {
local rel="$1"
while IFS= read -r prefix; do
[ -z "$prefix" ] && continue
case "$rel" in
"$prefix"*) return 0 ;;
esac
done < <(jq -r '.sandbox_prefixes[]' "$POLICY_PATH")
return 1
}
ACTION_COUNT="$(jq '.actions | length' "$MANIFEST_ABS")"
if [ "$DOC_KIND" = "utir" ]; then
ACTION_COUNT="$(jq '.operations | length' "$MANIFEST_ABS")"
fi
normalize_type() {
local raw="$1"
case "$DOC_KIND:$raw" in
work_manifest:write_file) echo "write_file" ;;
work_manifest:exec) echo "exec" ;;
utir:fs.write) echo "write_file" ;;
utir:shell) echo "exec" ;;
*) echo "unsupported" ;;
esac
}
if [ "$ACTION_COUNT" -eq 0 ]; then
echo "no actions to execute: $MANIFEST_ABS" >&2
exit 1
fi
for idx in $(seq 0 $((ACTION_COUNT - 1))); do
ACTION="$(jq -c "if \"$DOC_KIND\" == \"work_manifest\" then .actions[$idx] else .operations[$idx] end" "$MANIFEST_ABS")"
RAW_TYPE="$(printf '%s' "$ACTION" | jq -r '.type')"
TYPE="$(normalize_type "$RAW_TYPE")"
case "$TYPE" in
write_file)
REL_PATH="$(printf '%s' "$ACTION" | jq -r '.path')"
if ! is_path_allowed "$REL_PATH"; then
append_effect "$(jq -n \
--arg path "$REL_PATH" \
'{kind:"blocked",op:("write_file:" + $path),reason:"path blocked by local policy"}')"
continue
fi
CONTENT="$(printf '%s' "$ACTION" | jq -r '.content')"
TARGET_PATH="$ROOT/$REL_PATH"
mkdir -p "$(dirname "$TARGET_PATH")"
printf '%s' "$CONTENT" > "$TARGET_PATH"
BYTES="$(wc -c < "$TARGET_PATH" | tr -d ' ')"
SHA="$(shasum -a 256 "$TARGET_PATH" | awk '{print $1}')"
append_effect "$(jq -n \
--arg path "$REL_PATH" \
--arg sha256 "$SHA" \
--argjson bytes "$BYTES" \
'{kind:"write_file",path:$path,bytes:$bytes,sha256:$sha256,ok:true,error:null}')"
;;
exec)
CMD="$(printf '%s' "$ACTION" | jq -r 'if has("cmd") then .cmd else .command end')"
if has_blocked_pattern "$CMD"; then
append_effect "$(jq -n \
--arg cmd "$CMD" \
'{kind:"blocked",op:("exec:" + $cmd),reason:"command blocked by local policy pattern"}')"
elif ! is_command_allowed "$CMD"; then
append_effect "$(jq -n \
--arg cmd "$CMD" \
'{kind:"blocked",op:("exec:" + $cmd),reason:"command not present in local allowlist"}')"
else
STDOUT_FILE="$OUT_DIR/exec-$idx.stdout"
STDERR_FILE="$OUT_DIR/exec-$idx.stderr"
set +e
sh -lc "$CMD" >"$STDOUT_FILE" 2>"$STDERR_FILE"
STATUS="$?"
set -e
STDOUT_SHA="$(file_sha_or_null "$STDOUT_FILE")"
STDERR_SHA="$(file_sha_or_null "$STDERR_FILE")"
append_effect "$(jq -n \
--arg cmd "$CMD" \
--argjson ok "$([ "$STATUS" -eq 0 ] && echo true || echo false)" \
--argjson status "$STATUS" \
--arg stdout_sha256 "$STDOUT_SHA" \
--arg stderr_sha256 "$STDERR_SHA" \
'{
kind:"exec",
cmd:$cmd,
ok:$ok,
status:$status,
stdout_sha256:(if $stdout_sha256 == "" then null else $stdout_sha256 end),
stderr_sha256:(if $stderr_sha256 == "" then null else $stderr_sha256 end),
error:(if $ok then null else "command failed" end)
}')"
fi
;;
*)
append_effect "$(jq -n \
--arg op "$RAW_TYPE" \
'{kind:"blocked",op:$op,reason:"unsupported action type for local runtime"}')"
;;
esac
done
EFFECTS_JSON="$(jq -s '.' "$EFFECTS_NDJSON")"
jq -n \
--arg version "receipt_v1" \
--argjson deterministic false \
--arg input_sha256 "$INPUT_SHA256" \
--argjson effects "$EFFECTS_JSON" \
'{
version:$version,
deterministic:$deterministic,
input_sha256:$input_sha256,
generated_at_ms:null,
effects:$effects
}' > "$RECEIPT_PATH"
jq -n \
--arg manifest "$MANIFEST_ABS" \
--arg receipt "$RECEIPT_PATH" \
--arg graph "$GRAPH_PATH" \
--arg run_id "$RUN_ID" \
--arg doc_kind "$DOC_KIND" \
--argjson action_count "$ACTION_COUNT" \
'{
run_id:$run_id,
manifest:$manifest,
document_kind:$doc_kind,
receipt:$receipt,
graph_state:$graph,
action_count:$action_count
}' > "$SUMMARY_PATH"
jq -n \
--arg run_id "$RUN_ID" \
--arg manifest_id "$DOC_ID" \
--arg goal "$DOC_GOAL" \
--arg lane "$DOC_LANE" \
--arg receipt "$RECEIPT_PATH" \
--argjson execution_allowed true \
--arg doc_kind "$DOC_KIND" \
--argjson effects "$EFFECTS_JSON" \
'{
run_id:$run_id,
manifest_id:$manifest_id,
document_kind:$doc_kind,
goal:$goal,
lane:$lane,
execution_gate:{allowed:$execution_allowed},
receipt:$receipt,
effects:$effects
}' > "$GRAPH_PATH"
printf '%s\n' "$RECEIPT_PATH"
printf '%s\n' "$SUMMARY_PATH"