boot/bootstrap.sh
back to source
#!/bin/sh
# NumericalOS bootstrap - POSIX floor.
# Resolves architecture, selects and verifies an init artifact, execs it.
# Fail-closed: every abnormal path halts with a named reason.
set -eu
NUMOS_BASE="${NUMOS_BASE:-https://numericalos.com}"
NUMOS_LIB="${NUMOS_LIB:-$(dirname "$0")/lib}"
. "$NUMOS_LIB/arch_table.sh"
numos_die() {
echo "numos: HALT: $*" >&2
exit 1
}
numos_uname_m() {
if [ -n "${NUMOS_FAKE_UNAME_M:-}" ]; then
echo "$NUMOS_FAKE_UNAME_M"
else
uname -m
fi
}
numos_detect_arch() {
raw="$(numos_uname_m)"
canonical="$(numos_canonical_arch "$raw")" ||
numos_die "unsupported architecture: $raw"
echo "$canonical"
}
numos_select_artifact() {
arch="$1"
if numos_arch_has_static "$arch"; then
echo "static numinit-$arch"
else
echo "fallback busybox-static-$arch"
fi
}
numos_sha256() {
[ -f "$1" ] || numos_die "file not found: $1"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | cut -d' ' -f1
elif command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$1" | cut -d' ' -f1
else
numos_die "no sha256 implementation available"
fi
}
numos_verify_sha256() {
file="$1"
want="$2"
got="$(numos_sha256 "$file")"
[ "$got" = "$want" ] ||
numos_die "hash mismatch for $file: expected $want got $got"
}
# The C record carries the sha256 of every other line. Recompute and compare.
# The body goes through a real temp file rather than /dev/stdin, because
# numos_sha256 requires a regular file and /dev/stdin is not one everywhere.
#
# The whole remainder of the C line is the hash, not just its first
# whitespace-delimited field: taking `cut -f2` would let `C <valid-hash>
# ANYTHING` verify here while numos.state.verify() rejects it, and the tail
# is covered by no hash, no length check and no signature. Same definition
# on both sides, and it must be exactly 64 lowercase hex characters --
# numos/state.py::verify applies the identical rule.
numos_verify_state() {
file="$1"
[ -f "$file" ] || numos_die "state not found: $file"
c_count="$(grep -c '^C ' "$file")" || c_count=0
[ "$c_count" = "1" ] ||
numos_die "state has $c_count C records, expected exactly 1: $file"
c_line="$(grep '^C ' "$file" | head -n 1)"
want="${c_line#C }"
[ "${#want}" = "64" ] ||
numos_die "state C record is not a 64-character sha256: $file"
case "$want" in
*[!0-9a-f]*)
numos_die "state C record is not lowercase hex: $file" ;;
esac
tmp="${TMPDIR:-/tmp}/numos-verify.$$"
trap 'rm -f "$tmp"' EXIT
grep -v '^C ' "$file" > "$tmp"
got="$(numos_sha256 "$tmp")"
trap - EXIT
rm -f "$tmp"
[ "$got" = "$want" ] ||
numos_die "state hash mismatch for $file: expected $want got $got"
}
numos_main() {
arch="$(numos_detect_arch)"
echo "numos: arch $arch"
echo "numos: artifact $(numos_select_artifact "$arch")"
if [ -n "${NUMOS_STATE:-}" ]; then
numos_verify_state "$NUMOS_STATE"
echo "numos: state verified"
fi
}
if [ "${NUMOS_SOURCE_ONLY:-0}" != "1" ]; then
numos_main "$@"
fi