项目文件夹

文件
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

494 行
22 KiB
Bash
可执行文件

#!/usr/bin/env bash
# Cheap security policy checks for the elizaOS Live overlay.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
STRICT="${ELIZAOS_SECURITY_STRICT:-0}"
cd "${ROOT}"
failures=0
warnings=0
fail() {
echo "FAIL: $*" >&2
failures=$((failures + 1))
}
warn() {
echo "WARN: $*" >&2
warnings=$((warnings + 1))
}
strict_or_warn() {
if [ "${STRICT}" = "1" ]; then
fail "$@"
else
warn "$@"
fi
}
require_file() {
if [ ! -f "$1" ]; then
fail "missing required file: $1"
fi
}
require_grep() {
pattern="$1"
path="$2"
message="$3"
if ! grep -Eq "${pattern}" "${path}"; then
fail "${message}: ${path}"
fi
}
require_fixed() {
needle="$1"
path="$2"
message="$3"
if ! grep -Fq "${needle}" "${path}"; then
fail "${message}: ${path}"
fi
}
for path in \
tails/config/chroot_local-includes/usr/local/lib/elizaos/capability-runner \
tails/config/chroot_local-includes/etc/generate-sudoers.d/elizaos-capability-runner.toml \
tails/config/chroot_local-includes/usr/lib/python3/dist-packages/tps/configuration/features.py \
tails/config/chroot_local-includes/usr/local/lib/elizaos/persistence-maintenance \
tails/config/chroot_local-includes/usr/local/lib/elizaos/runtime-env \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-health-check \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-manager \
tails/config/chroot_local-includes/etc/systemd/system/elizaos-update-health-check.service \
tails/config/chroot_local-includes/etc/systemd/system/elizaos-update-verify.service \
tails/config/chroot_local-includes/etc/systemd/system/elizaos.service \
tails/config/chroot_local-includes/etc/systemd/user/elizaos-agent.service \
tails/config/chroot_local-includes/etc/systemd/user/elizaos-renderer.service \
tails/config/chroot_local-includes/etc/systemd/user/elizaos.service \
schemas/update-manifest.schema.json \
schemas/model-catalog.schema.json
do
require_file "${path}"
done
echo "==> capability broker boundary"
broker=tails/config/chroot_local-includes/usr/local/lib/elizaos/capability-runner
sudoers=tails/config/chroot_local-includes/etc/generate-sudoers.d/elizaos-capability-runner.toml
require_fixed 'executable = "/usr/local/lib/elizaos/capability-runner"' \
"${sudoers}" "elizaOS sudoers must target only capability-runner"
require_fixed 'allowed_user = "amnesia"' \
"${sudoers}" "elizaOS sudoers must be limited to the live user"
require_fixed 'args = ["root-status"]' \
"${sudoers}" "elizaOS sudoers must allow only the root-status smoke command"
if grep -Eq 'ALL|arbitrary_arguments|NOPASSWD: ALL|/bin/(ba)?sh|apt(-get)?|systemctl|service |nmcli|dd |mkfs|mount |umount ' "${sudoers}"; then
fail "elizaOS sudoers grants broad or mutating root authority: ${sudoers}"
fi
for command_name in status root-status open-persistent-storage privacy-mode; do
require_grep "^[[:space:]]*${command_name}\\)" "${broker}" \
"capability-runner missing command allowlist case ${command_name}"
done
if grep -Eq 'apt(-get)?|dpkg|systemctl|service |nmcli|iptables|nft |dd |mkfs|mount |umount |parted|sgdisk|curl |wget ' "${broker}"; then
fail "capability-runner exposes broad package, network, service, or disk mutation"
fi
require_fixed 'exec sudo -n "${self}" "$@"' "${broker}" \
"capability-runner must recurse through the exact sudo allowlist"
echo "==> broad sudoers inventory"
sudoers_review=docs/inherited-tails-sudoers-review.md
require_file "${sudoers_review}"
reviewed_inherited_sudoers=(
tails/config/chroot_local-includes/etc/generate-sudoers.d/tails-greeter-cryptsetup.toml
tails/config/chroot_local-includes/etc/generate-sudoers.d/tails-greeter-umount.toml
tails/config/chroot_local-includes/etc/generate-sudoers.d/tbb.toml
tails/config/chroot_local-includes/etc/generate-sudoers.d/tps.toml
tails/config/chroot_local-includes/etc/generate-sudoers.d/upgrade.toml
tails/config/chroot_local-includes/etc/generate-sudoers.d/whisperback.toml
)
is_reviewed_inherited_sudoers() {
local candidate="$1"
local reviewed
for reviewed in "${reviewed_inherited_sudoers[@]}"; do
if [ "${candidate}" = "${reviewed}" ]; then
return 0
fi
done
return 1
}
for reviewed in "${reviewed_inherited_sudoers[@]}"; do
require_file "${reviewed}"
require_fixed "$(basename "${reviewed}")" "${sudoers_review}" \
"inherited sudoers review missing entry"
done
while IFS= read -r file; do
[ -n "${file}" ] || continue
case "${file}" in
*elizaos*)
fail "unexpected broad elizaOS sudoers rule: ${file}"
;;
*)
if ! is_reviewed_inherited_sudoers "${file}"; then
strict_or_warn "unreviewed inherited Tails broad sudoers rule: ${file}"
fi
;;
esac
done < <(
rg -l 'executable = "ALL"|NOPASSWD: ALL|arbitrary_arguments]' \
tails/config/chroot_local-includes/etc/generate-sudoers.d || true
)
echo "==> runtime packaging boundary"
require_file docs/runtime-packaging.md
require_file scripts/prepare-elizaos-app-overlay.mjs
require_file scripts/validate-runtime-overlay.mjs
require_file tails/config/chroot_local-hooks/9100-install-elizaos
require_fixed 'not a post-boot injection path' docs/runtime-packaging.md \
"runtime packaging docs must reject post-boot injection"
require_fixed 'Resources/app/elizaos-live-overlay-manifest.json' docs/runtime-packaging.md \
"runtime packaging docs must require the live overlay manifest"
require_fixed 'Production replacement:' docs/runtime-packaging.md \
"runtime packaging docs must name the production replacement path"
require_fixed 'elizaos-live-overlay-manifest.json' scripts/prepare-elizaos-app-overlay.mjs \
"runtime prepare step must write an overlay manifest"
require_fixed 'manifest package inventory does not match staged node_modules' \
scripts/validate-runtime-overlay.mjs \
"runtime validator must compare package inventory"
require_grep '^src=/usr/share/elizaos/elizaos-app$' \
tails/config/chroot_local-hooks/9100-install-elizaos \
"runtime install hook must use the staged live-build app root"
require_grep '^dst=/opt/elizaos$' \
tails/config/chroot_local-hooks/9100-install-elizaos \
"runtime install hook must install to the immutable factory runtime root"
if grep -Eq 'curl |wget |git clone|npm install|bun install|pnpm install|yarn install' \
tails/config/chroot_local-hooks/9100-install-elizaos; then
fail "runtime install hook must not fetch or resolve dependencies during image build"
fi
echo "==> persistence boundary"
python3 - <<'PY'
from pathlib import Path
path = Path("tails/config/chroot_local-includes/usr/lib/python3/dist-packages/tps/configuration/features.py")
text = path.read_text()
required = {
'Binding("elizaos/eliza", "/home/amnesia/.eliza")',
'Binding("elizaos/elizaos", "/home/amnesia/.elizaos")',
'Binding("elizaos/config", "/home/amnesia/.config/elizaOS")',
'Binding("elizaos/config-legacy", "/home/amnesia/.config/elizaos")',
'Binding("elizaos/config-legacy-caps", "/home/amnesia/.config/elizaOS")',
'Binding("elizaos/cef-cache", "/home/amnesia/.cache/org.elizaos.app")',
'Binding("elizaos/cef-cache-legacy", "/home/amnesia/.cache/org.elizaos.app")',
'translatable_name = "elizaOS Data"',
}
missing = sorted(item for item in required if item not in text)
if missing:
raise SystemExit(f"{path}: missing elizaOS persistence policy: {missing}")
import re
start = text.index("class ElizaOSData")
end = text.index("class WelcomeScreen", start)
elizaos = text[start:end]
for name, target in re.findall(r'Binding\("([^"]+)", "([^"]+)"', elizaos):
if not name.startswith("elizaos/"):
continue
if target == "/home/amnesia" or target.startswith(("/etc", "/usr", "/var", "/root", "/opt")):
raise SystemExit(f"{path}: forbidden elizaOS persistence target: {name} -> {target}")
if "uses_symlinks=True" in elizaos:
raise SystemExit(f"{path}: elizaOS persistence must use bind mounts, not symlink persistence")
PY
persistence_helper=tails/config/chroot_local-includes/usr/local/lib/elizaos/persistence-maintenance
require_fixed 'run_dir=/run/elizaos' "${persistence_helper}" \
"persistence maintenance must use a root-owned runtime guard directory"
require_fixed 'flag="${run_dir}/persistence-maintenance"' "${persistence_helper}" \
"persistence maintenance must expose an activation/deactivation guard"
require_fixed 'user_persistence_flag="${runtime_dir}/elizaos-persistence-setup"' "${persistence_helper}" \
"persistence maintenance must also honor the user-session wizard guard"
require_fixed 'kill --kill-whom=all --signal=TERM' "${persistence_helper}" \
"persistence maintenance must quiesce user services before bind changes"
require_fixed 'kill --kill-whom=all --signal=KILL' "${persistence_helper}" \
"persistence maintenance must force-stop user services if graceful stop fails"
if grep -Eq 'pkill .* -u amnesia|rm -rf -- /home/amnesia($|/")|chown .* /home/amnesia' "${persistence_helper}"; then
fail "persistence-maintenance must not mutate the whole live-user home"
fi
require_fixed 'args = ["enter"]' \
tails/config/chroot_local-includes/etc/generate-sudoers.d/elizaos-persistence-maintenance.toml \
"live user may only enter the narrow elizaOS persistence-maintenance helper"
require_fixed 'args = ["leave"]' \
tails/config/chroot_local-includes/etc/generate-sudoers.d/elizaos-persistence-maintenance.toml \
"live user may only leave the narrow elizaOS persistence-maintenance helper"
cache_hook=tails/config/chroot_local-includes/usr/local/lib/persistent-storage/on-activated-hooks/ElizaOSData/10-clean-runtime-state
require_file "${cache_hook}"
require_fixed 'find -P "${state_dir}" -xdev' "${cache_hook}" \
"cache cleanup must not cross symlinks or filesystems"
if grep -Eq '/home/amnesia($|[[:space:]])|rm -rf -- /home/amnesia' "${cache_hook}"; then
fail "cache cleanup must stay within elizaOS-owned persistence paths"
fi
echo "==> service constraints"
root_unit=tails/config/chroot_local-includes/etc/systemd/system/elizaos.service
require_fixed 'ExecStart=/usr/local/lib/elizaos/elizaos-keeper' "${root_unit}" \
"root supervisor must run only the keeper"
require_fixed 'NoNewPrivileges=yes' "${root_unit}" \
"root supervisor must disable privilege escalation"
require_fixed 'PrivateTmp=yes' "${root_unit}" \
"root supervisor must use a private tmp"
require_fixed 'ProtectSystem=full' "${root_unit}" \
"root supervisor must protect the system tree"
if grep -Eq 'ExecStart=.*(/usr/local/bin/elizaos|/opt/elizaos|launcher)' "${root_unit}"; then
fail "root system service must not launch the app runtime directly"
fi
for unit in \
tails/config/chroot_local-includes/etc/systemd/user/elizaos-agent.service \
tails/config/chroot_local-includes/etc/systemd/user/elizaos-renderer.service \
tails/config/chroot_local-includes/etc/systemd/user/elizaos.service
do
require_fixed 'ConditionUser=1000' "${unit}" \
"user service must be pinned to the live user"
require_fixed 'ConditionPathExists=!/run/elizaos/persistence-maintenance' "${unit}" \
"user service must stay down while Persistent Storage setup is active"
require_fixed 'NoNewPrivileges=yes' "${unit}" \
"user service must disable privilege escalation"
if grep -q 'After=.*desktop.target' "${unit}"; then
fail "user service must not wait on the inherited Tails desktop target: ${unit}"
fi
done
for launcher in \
tails/config/chroot_local-includes/usr/local/bin/elizaos \
tails/config/chroot_local-includes/usr/local/lib/elizaos/start-elizaos-agent-user \
tails/config/chroot_local-includes/usr/local/lib/elizaos/start-elizaos-renderer-user
do
require_grep '127\.0\.0\.1|localhost|::1|normalize_loopback_bind' "${launcher}" \
"elizaOS runtime launchers must bind local services to loopback"
done
echo "==> update signing and recovery markers"
if [ -d tails/config/chroot_local-includes/usr/src/iuk ]; then
require_grep 'verify_signature|signature' \
tails/config/chroot_local-includes/usr/src/iuk/lib/Tails/IUK/Utils.pm \
"IUK stack must retain signature verification helpers"
require_grep 'valid signature|invalid signature|trusted key|untrusted key' \
tails/config/chroot_local-includes/usr/src/iuk/features/download_upgrade-description_file/Download_Upgrade-Description_File.feature \
"IUK tests must cover signature trust outcomes"
fi
if ! rg -q 'app/runtime update|model catalog|signed app bundle|signed model' docs/distribution-and-updates.md docs/security-model.md; then
fail "docs must define signed app/model update policy"
fi
if ! rg -q 'rollback|fail-closed|fail closed' docs/distribution-and-updates.md docs/security-model.md; then
fail "docs must define update rollback and fail-closed policy"
fi
update_manager=tails/config/chroot_local-includes/usr/local/lib/elizaos/update-manager
runtime_env=tails/config/chroot_local-includes/usr/local/lib/elizaos/runtime-env
update_unit=tails/config/chroot_local-includes/etc/systemd/system/elizaos-update-verify.service
require_fixed 'gpgv --keyring' "${update_manager}" \
"update manager must verify detached signed manifests"
require_fixed 'write_fallback_selector "missing-keyring"' "${update_manager}" \
"update manager must fail closed when no trusted keyring is present"
require_fixed 'ELIZAOS_RUNTIME_ROOT' "${runtime_env}" \
"runtime selector must export a runtime root for app wrappers"
require_fixed 'ExecStart=/usr/local/lib/elizaos/update-manager verify' "${update_unit}" \
"boot unit must verify staged updates before selecting a runtime"
require_fixed 'modelCatalog' schemas/update-manifest.schema.json \
"update manifest schema must cover a model catalog"
require_fixed 'filesComplete' schemas/update-manifest.schema.json \
"update manifest schema must require complete runtime file inventories"
require_fixed 'elizaos.modelCatalog' schemas/model-catalog.schema.json \
"model catalog schema must identify elizaOS model catalogs"
require_fixed 'contains unlisted file' "${update_manager}" \
"update manager must reject files outside the signed runtime inventory"
require_fixed 'runtime_store' "${update_manager}" \
"update manager must materialize verified runtimes into a root-owned store"
require_fixed 'os.O_NOFOLLOW' "${update_manager}" \
"update manager must materialize files without following source symlinks"
require_fixed 'tempfile.mkstemp' "${update_manager}" \
"update manager must materialize files through temporary files"
require_fixed 'verified runtime file changed while copying' "${update_manager}" \
"update manager must re-check manifest digests during materialization"
if grep -q 'ELIZAOS_ALLOW_RUNTIME_ENV_OVERRIDES' "${runtime_env}"; then
fail "runtime selector must not expose caller-controlled runtime override escape hatches"
fi
if grep -q 'ELIZAOS_BAKED_RUNTIME' "${runtime_env}"; then
fail "runtime selector must not honor caller-supplied baked runtime paths"
fi
require_fixed 'mark-${action} must run as root' "${update_manager}" \
"update promotion markers must be root-only"
require_fixed 'update-manager mark-good' \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-health-check \
"update health checker must promote healthy signed candidates"
require_fixed 'update-manager mark-bad' \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-health-check \
"update health checker must support rollback marking"
require_fixed 'ELIZAOS_UPDATE_HEALTH_MARK_BAD_ON_TIMEOUT' \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-health-check \
"update health checker must fail safe by default before rejecting candidates"
override_probe="$(
ELIZAOS_RUNTIME_ENV_FILE=/tmp/elizaos-fake-runtime.env \
ELIZAOS_BAKED_RUNTIME=/tmp/attacker \
ELIZAOS_RUNTIME_ROOT=/tmp/attacker \
ELIZAOS_BUN=/tmp/attacker/bin/bun \
"${runtime_env}" print
)"
if printf '%s\n' "${override_probe}" | grep -q /tmp/attacker; then
fail "runtime selector honored caller-supplied runtime override paths"
fi
tmp_mark="$(mktemp -d)"
trap 'rm -rf "${tmp_mark}"' EXIT
mkdir -p "${tmp_mark}/run" "${tmp_mark}/state"
cat > "${tmp_mark}/run/runtime-selection.json" <<'JSON'
{"trust":"signed","manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","channel":"stable","sequence":1}
JSON
set +e
ELIZAOS_RUN_DIR="${tmp_mark}/run" \
ELIZAOS_UPDATE_STATE_DIR="${tmp_mark}/state" \
"${update_manager}" mark-good >/dev/null 2>"${tmp_mark}/mark.err"
mark_code=$?
set -e
if [ "${mark_code}" -ne 77 ]; then
fail "non-root mark-good must exit 77, got ${mark_code}"
fi
if find "${tmp_mark}/state" -type f | grep -q .; then
fail "non-root mark-good wrote update state"
fi
rm -rf "${tmp_mark}"
if ! find tails/config/chroot_local-includes/usr/share/keyrings \
tails/config/chroot_local-includes/etc/elizaos \
-type f \( -name 'elizaos-update.gpg' -o -name 'update-keyring.gpg' \) 2>/dev/null | grep -q .
then
strict_or_warn "production elizaOS update keyring is not baked yet"
fi
if command -v gpg >/dev/null 2>&1 && command -v gpgv >/dev/null 2>&1; then
tmp="$(mktemp -d)"
trap 'rm -rf "${tmp}"' EXIT
export GNUPGHOME="${tmp}/gnupg"
mkdir -m 700 "${GNUPGHOME}"
gpg --batch --quiet --pinentry-mode loopback --passphrase '' \
--quick-gen-key 'elizaOS smoke <smoke@elizaos.invalid>' ed25519 sign 1d
gpg --batch --quiet --export 'smoke@elizaos.invalid' > "${tmp}/elizaos-update.gpg"
runtime="${tmp}/staged/channels/stable/runtime"
mkdir -p "${runtime}/bin" "${runtime}/Resources/app/eliza-dist/node_modules"
printf '#!/bin/sh\nexit 0\n' > "${runtime}/bin/bun"
printf '#!/bin/sh\nexit 0\n' > "${runtime}/bin/launcher"
printf 'console.log("elizaOS smoke")\n' > "${runtime}/Resources/app/eliza-dist/entry.js"
chmod 755 "${runtime}/bin/bun" "${runtime}/bin/launcher"
bun_sha="$(sha256sum "${runtime}/bin/bun" | awk '{print $1}')"
launcher_sha="$(sha256sum "${runtime}/bin/launcher" | awk '{print $1}')"
entry_sha="$(sha256sum "${runtime}/Resources/app/eliza-dist/entry.js" | awk '{print $1}')"
cat > "${tmp}/staged/channels/stable/manifest.json" <<EOF
{
"schemaVersion": 1,
"kind": "elizaos.updateManifest",
"manifestVersion": "smoke",
"channel": "stable",
"sequence": 1,
"publishedAt": "2026-05-17T00:00:00Z",
"runtime": {
"id": "smoke",
"version": "0.0.1",
"bundlePath": "runtime",
"filesComplete": true,
"entrypoints": {
"bun": "bin/bun",
"launcher": "bin/launcher",
"agent": "Resources/app/eliza-dist/entry.js",
"nodeModules": "Resources/app/eliza-dist/node_modules"
},
"files": [
{ "path": "bin/bun", "sha256": "${bun_sha}" },
{ "path": "bin/launcher", "sha256": "${launcher_sha}" },
{ "path": "Resources/app/eliza-dist/entry.js", "sha256": "${entry_sha}" }
]
}
}
EOF
gpg --batch --quiet --yes --pinentry-mode loopback --passphrase '' \
--detach-sign --output "${tmp}/staged/channels/stable/manifest.json.sig" \
"${tmp}/staged/channels/stable/manifest.json"
ELIZAOS_UPDATE_KEYRING="${tmp}/elizaos-update.gpg" \
ELIZAOS_STAGED_UPDATE_DIR="${tmp}/staged" \
ELIZAOS_RUN_DIR="${tmp}/run-good" \
ELIZAOS_RUNTIME_STORE="${tmp}/store" \
ELIZAOS_UPDATE_STATE_DIR="${tmp}/state" \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-manager verify
require_fixed "ELIZAOS_RUNTIME_TRUST=signed" "${tmp}/run-good/runtime.env" \
"signed smoke update must be selected"
if grep -q "${tmp}/staged" "${tmp}/run-good/runtime.env"; then
fail "signed runtime selector must point at the materialized store, not staged user storage"
fi
require_fixed "${tmp}/store" "${tmp}/run-good/runtime.env" \
"signed runtime selector must point at the materialized store"
materialized_entry="$(
sed -n "s/^ELIZAOS_AGENT_ENTRY=//p" "${tmp}/run-good/runtime.env" | tr -d "'"
)"
printf 'materialized tamper\n' >> "${materialized_entry}"
ELIZAOS_UPDATE_KEYRING="${tmp}/elizaos-update.gpg" \
ELIZAOS_STAGED_UPDATE_DIR="${tmp}/staged" \
ELIZAOS_RUN_DIR="${tmp}/run-materialized-tamper" \
ELIZAOS_RUNTIME_STORE="${tmp}/store" \
ELIZAOS_UPDATE_STATE_DIR="${tmp}/state" \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-manager verify
require_fixed "ELIZAOS_RUNTIME_TRUST='baked'" "${tmp}/run-materialized-tamper/runtime.env" \
"tampered materialized runtime must fall back to baked runtime"
rm -rf "${tmp}/store"
printf 'extra\n' > "${runtime}/extra.js"
ELIZAOS_UPDATE_KEYRING="${tmp}/elizaos-update.gpg" \
ELIZAOS_STAGED_UPDATE_DIR="${tmp}/staged" \
ELIZAOS_RUN_DIR="${tmp}/run-unlisted" \
ELIZAOS_RUNTIME_STORE="${tmp}/store-unlisted" \
ELIZAOS_UPDATE_STATE_DIR="${tmp}/state-unlisted" \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-manager verify
require_fixed "ELIZAOS_RUNTIME_TRUST='baked'" "${tmp}/run-unlisted/runtime.env" \
"runtime with unlisted files must fall back to baked runtime"
rm -f "${runtime}/extra.js"
printf 'tampered\n' >> "${runtime}/Resources/app/eliza-dist/entry.js"
ELIZAOS_UPDATE_KEYRING="${tmp}/elizaos-update.gpg" \
ELIZAOS_STAGED_UPDATE_DIR="${tmp}/staged" \
ELIZAOS_RUN_DIR="${tmp}/run-badhash" \
ELIZAOS_RUNTIME_STORE="${tmp}/store-badhash" \
ELIZAOS_UPDATE_STATE_DIR="${tmp}/state-badhash" \
tails/config/chroot_local-includes/usr/local/lib/elizaos/update-manager verify
require_fixed "ELIZAOS_RUNTIME_TRUST='baked'" "${tmp}/run-badhash/runtime.env" \
"tampered signed runtime must fall back to baked runtime"
else
strict_or_warn "gpg/gpgv unavailable; signed updater smoke was skipped"
fi
if ! rg -q 'gpgv --keyring|ELIZAOS_RELEASE_KEYRING|ELIZAOS_REQUIRE_ISO_SIGNATURE' scripts/usb-write.sh; then
strict_or_warn "USB writer verifies checksum but not a release signature"
fi
echo "==> SBOM and provenance markers"
if ! rg -q 'SBOM|provenance|license bundle' docs/distribution-and-updates.md docs/security-model.md docs/production-readiness.md; then
fail "docs must define SBOM/provenance release gates"
fi
require_file scripts/generate-release-evidence.mjs
if ! find . -maxdepth 4 -type f \( -iname '*sbom*' -o -iname '*provenance*' -o -iname '*attestation*' \) | grep -q .; then
strict_or_warn "production SBOM/provenance artifacts are not generated in this distro"
fi
if [ "${failures}" -ne 0 ]; then
echo "security smoke failed: ${failures} failure(s), ${warnings} warning(s)" >&2
exit 1
fi
echo "security smoke passed: ${warnings} warning(s)"