Skip to content

Commit 7b56947

Browse files
committed
fix: address build-log + drift findings (k3s path, MOTD, env resolver, AI manifests)
Cross-referenced the latest local build log (C:\Users\Administrator\AppData\Local\MiOS\logs\mios-install-20260503-160327.log) against the architectural-drift findings and fixed every confirmed issue: Build log: - 05-enable-external-repos.sh:121 -- quote the CrowdSec config_file URL so the unquoted '&' stops splitting '-o "${REPO_DIR}/crowdsec.repo"' off into its own command (was producing 'line 121: -o: command not found' + a 404 in every build); bump dist=40 -> dist=44. Documentation vs implementation drift: - /etc/profile.d/mios-env.sh -- the canonical login-shell environment resolver INDEX.md sec 4 documents but that did not exist on disk. Walks the five-layer overlay and exports MIOS_AI_*, MIOS_USER, etc. - /usr/bin/mios-env -- CLI wrapper that prints the resolved surface (--json, --explain, --unset, NAME-only). INDEX.md referenced it but there was no implementation; this fills the gap and Law-5 callers can now $(mios-env MIOS_AI_ENDPOINT) to discover the endpoint. - /usr/share/mios/ai/v1/{models,mcp}.json -- OpenAI-API-shaped manifests INDEX.md sec 2 declares as the source for /v1/models and /v1/mcp; previously dangling references with no payload. - usr/bin/mios-env, usr/share/mios/ai/v1/** allow-listed in .gitignore (the prior 'usr/share/mios/ai/' rule needed to be a glob to allow the v1/ subtree to slip through). Critical runtime faults: - k3s.service ExecStart /usr/local/bin/k3s -> /usr/bin/k3s. Binary is installed at /usr/bin/k3s by 13-ceph-k3s.sh; the unit was hard- coded to /usr/local/bin which is /var/usrlocal/bin on bootc/FCOS layouts and never contains the binary. - usr/lib/tmpfiles.d/mios.conf -- drop the 'L /var/usrlocal/bin/crictl - - - - /usr/local/bin/k3s' line. The target resolved to a non-existent path (/var/usrlocal/bin/k3s) and 13-ceph-k3s.sh already creates /usr/bin/crictl -> k3s in the immutable image surface. - usr/libexec/mios/motd -- Law-5 drift fix. Was probing /etc/mios/ai/config.json (does not exist) and hardcoding qwen2.5-coder:7b. Now sources /etc/profile.d/mios-env.sh on demand and reads the resolved MIOS_AI_ENDPOINT / MIOS_AI_MODEL. Operational hygiene: - 36-tools.sh -- drop aichat / aichat-ng from the chmod-loop TOOLS list. They are installed by 37-aichat.sh (later in pipeline order), so this loop printed a WARN every build. Add mios-env in their place. - 37-selinux.sh -- write usr/share/selinux/packages/mios/booleans.conf with container_use_devices=on so the existing selinux-init service applies the boolean at first boot. semanage is inoperative inside the OCI build, so 35-gpu-passthrough.sh's build-time setsebool was a no-op and GPU containers got 'Permission Denied' until manual intervention. Booleans now apply at runtime.
1 parent d90d81a commit 7b56947

11 files changed

Lines changed: 412 additions & 19 deletions

File tree

.gitignore

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ etc/fapolicyd/*
106106
!/etc/fapolicyd/fapolicyd.rules
107107
!/etc/wsl.conf
108108

109+
# /etc/profile.d/ -- login-shell environment resolver (INDEX.md sec 4).
110+
# mios-env.sh walks the five-layer env overlay and exports MIOS_AI_* +
111+
# identity vars system-wide. Bash sources /etc/profile.d/*.sh from
112+
# /etc/profile, so the file must live under /etc to be picked up.
113+
!/etc/profile.d/
114+
!/etc/profile.d/mios-env.sh
115+
109116
# /etc/mios/ -- KB config (system-prompts, kb.conf.toml, eval-criteria.json) tracked here.
110117
# Runtime secrets and per-host overrides are NOT tracked (handled by mios-bootstrap.git).
111118
!/etc/mios/
@@ -143,9 +150,10 @@ usr/*
143150
!/usr/libexec/
144151
!/usr/share/
145152

146-
# usr/bin -- only the mios binary
153+
# usr/bin -- only MiOS-owned CLIs
147154
usr/bin/*
148155
!/usr/bin/mios
156+
!/usr/bin/mios-env
149157

150158
# usr/share -- mios/ build files + doc/mios/ KB docs
151159
usr/share/*
@@ -155,8 +163,14 @@ usr/share/doc/*
155163
!/usr/share/doc/mios/**
156164
!/usr/share/mios/
157165
!/usr/share/mios/**
158-
# AI files, knowledge, logs, user data belong in mios-bootstrap.git
159-
usr/share/mios/ai/
166+
# AI runtime state (memory, knowledge graphs, scratch) belongs in
167+
# mios-bootstrap.git -- it is per-host and seeded into /var at first
168+
# boot. The OpenAI-API surface manifests in usr/share/mios/ai/v1/
169+
# (models.json + mcp.json) ARE shipped from MiOS because INDEX.md sec 2
170+
# declares them as the canonical /v1/* manifest source.
171+
usr/share/mios/ai/*
172+
!/usr/share/mios/ai/v1/
173+
!/usr/share/mios/ai/v1/**
160174
usr/share/mios/knowledge/
161175
usr/share/mios/memory/
162176
usr/share/mios/user-preferences.md

automation/05-enable-external-repos.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,13 @@ fi
118118
# crowdsec ships its own RPM repo; not in Fedora or RPM Fusion.
119119
if [[ ! -f "${REPO_DIR}/crowdsec.repo" ]]; then
120120
log "enabling CrowdSec repo"
121-
scurl -fsSL https://packagecloud.io/crowdsec/crowdsec/config_file.repo?os=fedora&dist=40&source=script \
121+
# URL must be quoted -- the unquoted '&' is parsed as a job-control
122+
# background, which silently splits '-o "${REPO_DIR}/crowdsec.repo"'
123+
# onto its own command line and yields 'line N: -o: command not found'.
124+
# The 'dist' query parameter pins the packagecloud distro release;
125+
# crowdsec ships a single fedora repo across releases (the value is
126+
# only used for substituting $releasever in baseurl).
127+
scurl -fsSL "https://packagecloud.io/crowdsec/crowdsec/config_file.repo?os=fedora&dist=44&source=script" \
122128
-o "${REPO_DIR}/crowdsec.repo"
123129
else
124130
log "CrowdSec repo already present -- skipping"

automation/36-tools.sh

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,21 @@ echo "[36-tools] Configuring 'MiOS' CLI tools..."
1111
# might have lost the executable bit during git/Windows transfer.
1212

1313
TOOLS=(
14-
mios
15-
mios-update
16-
mios-rebuild
17-
mios-build
18-
mios-backup
19-
mios-deploy
20-
mios-status
21-
mios-vfio-toggle
22-
mios-vfio-check
14+
mios
15+
mios-env
16+
mios-update
17+
mios-rebuild
18+
mios-build
19+
mios-backup
20+
mios-deploy
21+
mios-status
22+
mios-vfio-toggle
23+
mios-vfio-check
2324
iommu-groups
24-
aichat
25-
aichat-ng
2625
)
26+
# Note: aichat / aichat-ng are installed by 37-aichat.sh (which fetches
27+
# the upstream binaries); attempting to chmod them here printed a WARN
28+
# every build because the binaries do not exist yet at this stage.
2729

2830
for tool in "${TOOLS[@]}"; do
2931
if [ -f "/usr/bin/$tool" ]; then

automation/37-selinux.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,20 @@ allow xdm_t cache_home_t:file { create write read open getattr setattr };'
171171
echo "[37-selinux] ${SELINUX_OK} policies staged in /usr/share/selinux/packages/mios/, ${SELINUX_FAIL} skipped"
172172
fi
173173

174+
# ─── Persistent SELinux booleans applied at first boot ─────────────────────
175+
# usr/libexec/mios/selinux-init reads booleans.conf next to the staged .pp
176+
# modules and applies each entry via 'setsebool -P'. semanage is typically
177+
# inoperative inside an OCI build (no running policy / read-only contexts),
178+
# so booleans MUST be applied at runtime; this file is the manifest the
179+
# runtime service consults.
180+
#
181+
# container_use_devices=on -- required by 35-gpu-passthrough.sh / GPU
182+
# container CDI flow (NVIDIA, ROCm, Intel xe). Without it, the first
183+
# GPU container start is denied by SELinux on enforcing hosts.
184+
mkdir -p /usr/share/selinux/packages/mios
185+
cat > /usr/share/selinux/packages/mios/booleans.conf <<'EOBOOL'
186+
container_use_devices=on
187+
EOBOOL
188+
echo "[37-selinux] booleans.conf staged for runtime selinux-init"
189+
174190
echo "[37-selinux] SELinux configuration complete."

etc/profile.d/mios-env.sh

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# /etc/profile.d/mios-env.sh -- 'MiOS' login-shell environment resolver.
2+
#
3+
# Sourced from /etc/profile by every interactive login shell. Walks the
4+
# documented five-layer overlay (INDEX.md sec 4) and exports the resolved
5+
# MIOS_* variables so every agent and CLI sees the same values regardless
6+
# of which shell or terminal launched them.
7+
#
8+
# Resolution order (later layers override earlier values):
9+
# 1. /usr/share/mios/env.defaults vendor defaults (lowest)
10+
# 2. /etc/mios/env.d/*.env admin/distro drop-ins (alphabetical)
11+
# 3. /etc/mios/install.env host bootstrap-written identity
12+
# 4. ~/.env.mios legacy per-user (deprecated)
13+
# 5. ~/.config/mios/env per-user override (highest)
14+
#
15+
# Architectural Law 5 (UNIFIED-AI-REDIRECTS): MIOS_AI_ENDPOINT,
16+
# MIOS_AI_MODEL, and MIOS_AI_KEY are exported here so every OpenAI-API
17+
# client on the system resolves the same canonical surface at
18+
# http://localhost:8080/v1.
19+
#
20+
# Safe to source in non-bash POSIX shells; uses only POSIX builtins.
21+
22+
# Bail out early on non-interactive non-login shells -- this file should
23+
# not perturb cron jobs or systemd-spawned children.
24+
case "$-" in
25+
*i*) ;;
26+
*)
27+
# Still export AI endpoint for non-interactive scripts; just skip
28+
# the chatty layer-walk and per-user reads.
29+
if [ -r /usr/share/mios/env.defaults ]; then
30+
# shellcheck disable=SC1091
31+
. /usr/share/mios/env.defaults
32+
export MIOS_AI_ENDPOINT MIOS_AI_MODEL MIOS_AI_KEY
33+
fi
34+
return 0 2>/dev/null || exit 0
35+
;;
36+
esac
37+
38+
_mios_source_if_readable() {
39+
[ -r "$1" ] || return 0
40+
# shellcheck disable=SC1090
41+
. "$1"
42+
}
43+
44+
# Layer 1: vendor defaults (always present on a 'MiOS' system).
45+
_mios_source_if_readable /usr/share/mios/env.defaults
46+
47+
# Layer 2: admin / distro drop-ins (alphabetical, env-style).
48+
if [ -d /etc/mios/env.d ]; then
49+
for _f in /etc/mios/env.d/*.env; do
50+
_mios_source_if_readable "$_f"
51+
done
52+
unset _f
53+
fi
54+
55+
# Layer 3: host install.env -- bootstrap writes identity here.
56+
_mios_source_if_readable /etc/mios/install.env
57+
58+
# Layer 4: legacy per-user env file (deprecated; kept for migration).
59+
_mios_source_if_readable "${HOME}/.env.mios"
60+
61+
# Layer 5: canonical per-user env override.
62+
_mios_source_if_readable "${HOME}/.config/mios/env"
63+
64+
# Export the OpenAI-API surface required by every Law-5-compliant agent.
65+
export MIOS_AI_ENDPOINT="${MIOS_AI_ENDPOINT:-http://localhost:8080/v1}"
66+
export MIOS_AI_MODEL="${MIOS_AI_MODEL:-qwen2.5-coder:7b}"
67+
export MIOS_AI_EMBED_MODEL="${MIOS_AI_EMBED_MODEL:-nomic-embed-text}"
68+
export MIOS_AI_KEY="${MIOS_AI_KEY:-}"
69+
70+
# Identity surface (consumed by 'mios' CLI, ai-bootstrap, postcheck).
71+
export MIOS_USER="${MIOS_USER:-${MIOS_DEFAULT_USER:-mios}}"
72+
export MIOS_HOSTNAME="${MIOS_HOSTNAME:-${MIOS_DEFAULT_HOST:-mios}}"
73+
export MIOS_VERSION="${MIOS_VERSION:-}"
74+
75+
# Runtime path surface (used by tools/lib/userenv.sh and the 'mios' CLI).
76+
export MIOS_SHARE_DIR="${MIOS_SHARE_DIR:-/usr/share/mios}"
77+
export MIOS_AI_DIR="${MIOS_AI_DIR:-/usr/share/mios/ai}"
78+
export MIOS_AI_SCRATCH_DIR="${MIOS_AI_SCRATCH_DIR:-/var/lib/mios/ai/scratch}"
79+
export MIOS_AI_MEMORY_DIR="${MIOS_AI_MEMORY_DIR:-/var/lib/mios/ai/memory}"
80+
81+
unset -f _mios_source_if_readable

usr/bin/mios-env

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#!/usr/bin/env bash
2+
# /usr/bin/mios-env -- print the resolved MIOS_* environment surface.
3+
#
4+
# Documented in INDEX.md sec 4 as the canonical CLI for inspecting the
5+
# layered environment overlay (/usr/share/mios/env.defaults ->
6+
# /etc/mios/env.d/*.env -> /etc/mios/install.env -> ~/.env.mios ->
7+
# ~/.config/mios/env). Wraps /etc/profile.d/mios-env.sh + the
8+
# tools/lib/userenv.sh TOML resolver and reports the consolidated values
9+
# the running session would actually see.
10+
#
11+
# Usage:
12+
# mios-env # all MIOS_* vars, sorted (KEY=VALUE)
13+
# mios-env <NAME> # one var, value only (suitable for $(...) capture)
14+
# mios-env --json # machine-readable JSON
15+
# mios-env --unset # POSIX 'unset' lines (for re-evaluating in a parent shell)
16+
# mios-env --explain # show which layer supplied each value
17+
# mios-env --help
18+
#
19+
# Architectural Law 5: MIOS_AI_ENDPOINT / MIOS_AI_MODEL / MIOS_AI_KEY
20+
# resolved here are the canonical values for every OpenAI-API-shaped
21+
# client on the system.
22+
set -euo pipefail
23+
24+
# Canonical layer paths (mirrors mios-env.sh).
25+
LAYERS=(
26+
"/usr/share/mios/env.defaults"
27+
)
28+
# /etc/mios/env.d/*.env (alphabetical)
29+
if [[ -d /etc/mios/env.d ]]; then
30+
while IFS= read -r -d '' f; do
31+
LAYERS+=("$f")
32+
done < <(find /etc/mios/env.d -maxdepth 1 -name '*.env' -print0 2>/dev/null | sort -z)
33+
fi
34+
LAYERS+=(
35+
"/etc/mios/install.env"
36+
"${HOME}/.env.mios"
37+
"${HOME}/.config/mios/env"
38+
)
39+
40+
_load_layers() {
41+
local layer
42+
for layer in "${LAYERS[@]}"; do
43+
[[ -r "$layer" ]] || continue
44+
# shellcheck disable=SC1090
45+
. "$layer"
46+
done
47+
}
48+
49+
# Also fold in the TOML resolver (tools/lib/userenv.sh) which deep-merges
50+
# /usr/share/mios/mios.toml -> /etc/mios/mios.toml -> ~/.config/mios/mios.toml.
51+
# That library exports a richer set of typed MIOS_* slots derived from the
52+
# unified mios.toml schema.
53+
_load_toml_layers() {
54+
local userenv_lib
55+
for userenv_lib in /usr/lib/mios/userenv.sh /usr/share/mios/tools/lib/userenv.sh; do
56+
if [[ -r "$userenv_lib" ]]; then
57+
# shellcheck disable=SC1090
58+
. "$userenv_lib"
59+
return 0
60+
fi
61+
done
62+
}
63+
64+
_print_kv() {
65+
# Print all currently-set MIOS_* variables sorted by name.
66+
while IFS= read -r line; do
67+
printf '%s\n' "$line"
68+
done < <(compgen -A variable | grep -E '^MIOS_' | sort | while read -r v; do
69+
printf '%s=%q\n' "$v" "${!v-}"
70+
done)
71+
}
72+
73+
_print_json() {
74+
printf '{\n'
75+
local first=1
76+
while IFS= read -r v; do
77+
local val="${!v-}"
78+
# JSON-escape the value: backslash, double-quote, control chars.
79+
val=${val//\\/\\\\}
80+
val=${val//\"/\\\"}
81+
val=${val//$'\n'/\\n}
82+
val=${val//$'\t'/\\t}
83+
if [[ $first -eq 1 ]]; then first=0; else printf ',\n'; fi
84+
printf ' "%s": "%s"' "$v" "$val"
85+
done < <(compgen -A variable | grep -E '^MIOS_' | sort)
86+
printf '\n}\n'
87+
}
88+
89+
_print_explain() {
90+
# For each MIOS_* variable, identify the highest layer that set it.
91+
declare -A SOURCE_OF
92+
local layer
93+
for layer in "${LAYERS[@]}"; do
94+
[[ -r "$layer" ]] || continue
95+
# Parse KEY=... lines from this layer (env-file format).
96+
while IFS= read -r key; do
97+
[[ -n "$key" ]] && SOURCE_OF["$key"]="$layer"
98+
done < <(grep -oE '^[[:space:]]*MIOS_[A-Z0-9_]+(?==)' "$layer" 2>/dev/null \
99+
| sed -E 's/^[[:space:]]*//; s/=$//' || true)
100+
done
101+
102+
while IFS= read -r v; do
103+
printf '%-32s = %-30s [%s]\n' \
104+
"$v" "${!v-}" "${SOURCE_OF[$v]:-runtime/default}"
105+
done < <(compgen -A variable | grep -E '^MIOS_' | sort)
106+
}
107+
108+
_print_unset() {
109+
# Emit 'unset' lines for every MIOS_* var. Useful for callers that
110+
# want to clear and re-source the resolver in their own shell.
111+
while IFS= read -r v; do
112+
printf 'unset %s\n' "$v"
113+
done < <(compgen -A variable | grep -E '^MIOS_' | sort)
114+
}
115+
116+
_help() {
117+
sed -n '2,/^set -euo/p' "$0" | sed -n '/^# /p' | sed 's/^# //'
118+
}
119+
120+
case "${1:-}" in
121+
-h|--help)
122+
_help
123+
exit 0
124+
;;
125+
--json)
126+
_load_layers
127+
_load_toml_layers 2>/dev/null || true
128+
_print_json
129+
;;
130+
--explain)
131+
_load_layers
132+
_load_toml_layers 2>/dev/null || true
133+
_print_explain
134+
;;
135+
--unset)
136+
_load_layers
137+
_load_toml_layers 2>/dev/null || true
138+
_print_unset
139+
;;
140+
"")
141+
_load_layers
142+
_load_toml_layers 2>/dev/null || true
143+
_print_kv
144+
;;
145+
MIOS_*)
146+
_load_layers
147+
_load_toml_layers 2>/dev/null || true
148+
var="$1"
149+
# Only echo the value (no name=); suitable for $(mios-env MIOS_AI_ENDPOINT).
150+
printf '%s\n' "${!var-}"
151+
;;
152+
*)
153+
printf 'mios-env: unknown argument %q (try --help)\n' "$1" >&2
154+
exit 2
155+
;;
156+
esac

usr/lib/systemd/system/k3s.service

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ Restart=always
2222
RestartSec=5s
2323
ExecStartPre=-/sbin/modprobe br_netfilter
2424
ExecStartPre=-/sbin/modprobe overlay
25-
ExecStart=/usr/local/bin/k3s server --disable traefik --write-kubeconfig-mode 644
25+
# Binary lives at /usr/bin/k3s (immutable image surface, installed by
26+
# automation/13-ceph-k3s.sh). /usr/local/bin -> /var/usrlocal/bin on
27+
# bootc/FCOS layouts and is mutable per-host; /usr/bin keeps the unit
28+
# pinned to the image's authoritative copy.
29+
ExecStart=/usr/bin/k3s server --disable traefik --write-kubeconfig-mode 644
2630

2731
[Install]
2832
WantedBy=multi-user.target

usr/lib/tmpfiles.d/mios.conf

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ d /var/home/mios 0755 mios mios - -
1616
d /var/lib/AccountsService 0775 root root - -
1717
d /var/lib/AccountsService/icons 0775 root root - -
1818
d /var/lib/AccountsService/users 0700 root root - -
19-
L /var/usrlocal/bin/crictl - - - - /usr/local/bin/k3s
19+
20+
# /usr/bin/crictl -> k3s symlink is created by automation/13-ceph-k3s.sh
21+
# at image-build time (immutable surface). The previous L line here
22+
# pointed /var/usrlocal/bin/crictl at /usr/local/bin/k3s, which (because
23+
# /usr/local is itself a symlink to /var/usrlocal on bootc/FCOS layouts)
24+
# resolved to /var/usrlocal/bin/k3s -- a path that does not exist.
2025

2126
# Seed initial journal if missing
2227
C /var/lib/mios/memory/journal/v1.jsonl - - - - /usr/share/mios/memory/v1.jsonl

0 commit comments

Comments
 (0)