Skip to content

Commit 2020804

Browse files
committed
sweeping.
1 parent 163853f commit 2020804

3 files changed

Lines changed: 121 additions & 34 deletions

File tree

CLAUDE.md

Lines changed: 109 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,120 @@
33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

55
> Canonical agent prompt: `/usr/share/mios/ai/system.md` (deployed from `mios-bootstrap`).
6+
> Loading order: `/usr/share/mios/ai/system.md``/etc/mios/ai/system-prompt.md` (host override) → `~/.config/mios/system-prompt.md` (user override).
67
7-
## Loading order
8+
## What this repo is
89

9-
1. Load `/usr/share/mios/ai/system.md`.
10-
2. Apply `/etc/mios/ai/system-prompt.md` if present (host override).
11-
3. Apply `~/.config/mios/system-prompt.md` if present (user override).
10+
MiOS is an immutable, `bootc`-managed Fedora-derived workstation OS distributed as an OCI image. The repo root **is** the deployed system root: `usr/`, `etc/`, `srv/`, `var/`, `proc/`, `opt/` at the top level mirror their FHS-3.0 destinations. There is no `system_files/` indirection; `automation/08-system-files-overlay.sh` overlays them into the image.
1211

13-
## Claude Code deltas
12+
The published image is `ghcr.io/mios-dev/mios:latest` and is built `FROM ghcr.io/ublue-os/ucore-hci:stable-nvidia` (set via `MIOS_BASE_IMAGE`).
1413

15-
- **cwd:** `/` is the repo root and system root — do not treat it as dangerous.
14+
## Build commands
15+
16+
Linux orchestrator is `Justfile`; Windows orchestrator is `mios-build-local.ps1`. Do not invent a "cloud-ws.ps1" or four-stage pipeline — neither exists.
17+
18+
```bash
19+
just preflight # System prereq check (tools/preflight.sh)
20+
just build # Build OCI image -> localhost/mios:latest
21+
just lint # Re-run `bootc container lint` on the built image
22+
just rechunk # Optimize Day-2 deltas (rechunk into versioned tag)
23+
just raw # RAW disk image via BIB
24+
just iso # Anaconda installer ISO via BIB
25+
just qcow2 # Requires MIOS_USER_PASSWORD_HASH env (openssl passwd -6)
26+
just vhdx # Hyper-V VHDX (same env requirement)
27+
just wsl2 # WSL2 tarball
28+
just sbom # CycloneDX SBOM via syft
29+
just artifact # Refresh AI manifests, UKB, and Wiki docs
30+
just all-bootstrap # build + rechunk + log to bootstrap repo
31+
```
32+
33+
Windows: `.\preflight.ps1` then `.\mios-build-local.ps1` (rootful Podman machine, credential injection, BIB, GHCR push, cleanup).
34+
35+
The `Containerfile` already runs `bootc container lint` as its final RUN — `just build` is itself the lint gate.
36+
37+
## Phase-2 build pipeline (the `automation/` directory)
38+
39+
`Containerfile` triggers `automation/build.sh`, which iterates every `automation/[0-9][0-9]-*.sh` in lexicographic numeric order. **Sub-phase numbering encodes dependency order and must be preserved when adding new scripts.** Per-script failures are captured in `FAIL_LOG`/`WARN_LOG` (set +e wrapper around each invocation, `automation/build.sh:234-237`) — the orchestrator does not abort. Critical packages are post-validated via `rpm -q` against `packages-critical` from `PACKAGES.md`.
40+
41+
Skipped under the in-Containerfile build:
42+
- `08-system-files-overlay.sh` — runs pre-pipeline directly from `Containerfile`
43+
- `37-ollama-prep.sh` — CI-skipped
44+
45+
The full pipeline spans five phases owned by two repos:
46+
47+
| Phase | Owner | Description |
48+
|---|---|---|
49+
| 0 | `mios-bootstrap.git/install.sh` | Preflight + profile load + identity capture |
50+
| 1 | `mios-bootstrap.git/install.sh` | Total Root Merge of `mios.git` and `mios-bootstrap.git` to `/` |
51+
| 2 | `Containerfile` + `automation/build.sh` | Build (this repo) |
52+
| 3 | `mios.git/install.sh` + bootstrap profile staging | sysusers/tmpfiles + user create + per-user `~/.config/mios/{profile.toml,system-prompt.md}` |
53+
| 4 | `mios-bootstrap.git/install.sh` | Reboot prompt |
54+
55+
## Architectural Laws (non-negotiable, build/audit-fail on violation)
56+
57+
1. **USR-OVER-ETC** — static config in `/usr/lib/<component>.d/`; `/etc/` is admin-override only. Documented exceptions are upstream-contract surfaces (`/etc/yum.repos.d/`, `/etc/nvidia-container-toolkit/`).
58+
2. **NO-MKDIR-IN-VAR** — every `/var/` path declared via `usr/lib/tmpfiles.d/*.conf`. **Never write to `/var/` at build time.** bootc forbids it; lint will fail.
59+
3. **BOUND-IMAGES** — every Quadlet image symlinked into `/usr/lib/bootc/bound-images.d/`. Binder loop: `automation/08-system-files-overlay.sh:74-86`.
60+
4. **BOOTC-CONTAINER-LINT** — must be the final `RUN` of `Containerfile`. No `--squash-all` (strips OCI metadata bootc needs).
61+
5. **UNIFIED-AI-REDIRECTS** — all agents target `MIOS_AI_ENDPOINT` (`http://localhost:8080/v1`). Vendor-hardcoded URLs are forbidden. Endpoint served by `etc/containers/systemd/mios-ai.container`.
62+
6. **UNPRIVILEGED-QUADLETS** — every Quadlet declares `User=`, `Group=`, `Delegate=yes`. Documented root exceptions: `mios-ceph`, `mios-k3s` (file headers explain why).
63+
64+
## Package management
65+
66+
Single source of truth: `usr/share/mios/PACKAGES.md`. Every RPM lives in a fenced ` ```packages-<category>` block parsed by `automation/lib/packages.sh:get_packages` (regex `/^```packages-${category}$/,/^```$/`). **Never call `dnf install` on hard-coded names.** Use:
67+
68+
- `install_packages "<category>"` — best-effort, `--skip-unavailable`
69+
- `install_packages_strict "<category>"` — fails the script on any miss
70+
- `install_packages_optional "<category>"` — pure best-effort, never fails
71+
72+
Kernel rule: only add `kernel-modules-extra`, `kernel-devel`, `kernel-headers`, `kernel-tools`. Never upgrade `kernel`/`kernel-core` in-container — `automation/01-repos.sh` excludes them. dnf option spelling is `install_weak_deps=False` (underscore); `install_weakdeps` is silently ignored by dnf5.
73+
74+
## Containerfile shape
75+
76+
Single-stage main image with a `ctx` scratch context that bind-mounts read-only at `/ctx`. Mutating writes go to `/tmp/build`. The `Containerfile` pre-pipeline `RUN` installs `packages-base` (security stack) before `automation/build.sh` runs.
77+
78+
## Shell conventions
79+
80+
- `set -euo pipefail` at the top of every phase script.
81+
- Arithmetic: `VAR=$((VAR + 1))`. **`((VAR++))` is forbidden** — under `set -e` it exits 1 when the result is 0.
82+
- shellcheck-clean. SC2038 is fatal in CI (`.github/workflows/mios-ci.yml`).
83+
- File naming: `NN-name.sh` where NN encodes execution order.
84+
85+
## Kargs format
86+
87+
`usr/lib/bootc/kargs.d/*.toml` uses a flat top-level array; bootc rejects anything else:
88+
89+
```toml
90+
kargs = ["init_on_alloc=1", "lockdown=integrity"]
91+
```
92+
93+
No `[kargs]` section header, no `delete` sub-key. Files processed in lexicographic order; earlier entries cannot be removed by later files in the same image — use runtime `bootc kargs --delete` for removal.
94+
95+
Note: `lockdown=integrity` (not `confidentiality`). `init_on_alloc=1`, `init_on_free=1`, `page_alloc.shuffle=1` are **disabled** in MiOS due to NVIDIA/CUDA incompatibility.
96+
97+
## Service gating
98+
99+
- Bare-metal-only services: `ConditionVirtualization=no` drop-in.
100+
- WSL2-incompatible: `ConditionVirtualization=!wsl`.
101+
- Optional: `systemctl enable ... || true`.
102+
103+
Every boolean in `usr/share/mios/profile.toml` ships **`true`**; the system never disables a component via static config — Quadlet `Condition*` directives short-circuit incompatible units silently.
104+
105+
## Claude Code operating context
106+
107+
- **cwd:** `/` is both the repo root and the deployed system root — do not treat it as dangerous.
16108
- **Confirm before:** `git push`, `bootc upgrade`, `dnf install`, `systemctl`, `rm -rf`.
17-
- **Deliverables:** complete replacement files only — no diffs, no patches.
109+
- **Deliverables:** complete replacement files only — no diffs, no patches, no "paste this into X" fragments. Nothing in the repo gets removed without prior discussion.
18110
- **Memory:** `/var/lib/mios/ai/memory/`
19111
- **Scratch:** `/var/lib/mios/ai/scratch/`
20112
- **Tasks:** use the task tool for multi-step work; one in-progress at a time.
113+
114+
## Cross-references
115+
116+
- Architectural laws and API surface: `INDEX.md`
117+
- Filesystem and hardware layout: `ARCHITECTURE.md`
118+
- Engineering standards (this file's authoritative source for build rules): `ENGINEERING.md`
119+
- Build modes: `SELF-BUILD.md`
120+
- Deployment and Day-2 lifecycle: `DEPLOY.md`
121+
- Security posture and hardening kargs: `SECURITY.md`
122+
- Contribution conventions: `CONTRIBUTING.md`

automation/10-gnome.sh

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,16 @@ fc-cache -f /usr/share/fonts/geist 2>/dev/null || true
7070
# FAIL THE BUILD if cursors are missing — a square cursor is unacceptable.
7171
# ═════════════════════════════════════════════════════════════════════════════
7272
echo "[10-gnome] Installing Bibata-Modern-Classic cursor (MANDATORY)..."
73-
BIBATA_VER=""
74-
BIBATA_FALLBACK="2.0.7"
7573

76-
# Try GitHub API for latest release tag (strips leading 'v')
77-
# v0.2.0: Wrap in subshell + || true to prevent pipefail from killing the script if API is down
74+
# Resolve latest release from upstream. Project policy: every dependency
75+
# tracks :latest from its source, so no fallback pin — if api.github.com is
76+
# unreachable, fail loud rather than silently shipping a stale version.
7877
BIBATA_VER=$( (scurl -sL --connect-timeout 15 --max-time 30 \
7978
-H "Accept: application/vnd.github+json" "https://api.github.com/repos/ful1e5/Bibata_Cursor/releases/latest" \
8079
| grep -m1 '"tag_name"' | sed 's/.*"v\?\([^"]*\)".*/\1/') 2>/dev/null || true)
8180

82-
# Fallback if API fails (rate limit, network issue)
83-
if [ -z "$BIBATA_VER" ]; then
84-
BIBATA_VER="$BIBATA_FALLBACK"
85-
echo "[10-gnome] GitHub API unavailable — using fallback v${BIBATA_VER}"
86-
else
87-
echo "[10-gnome] Latest release: v${BIBATA_VER}"
88-
fi
81+
[[ -n "$BIBATA_VER" ]] || die "Bibata: api.github.com release-latest lookup returned empty"
82+
echo "[10-gnome] Latest release: v${BIBATA_VER}"
8983

9084
BIBATA_URL="https://github.com/ful1e5/Bibata_Cursor/releases/download/v${BIBATA_VER}/Bibata-Modern-Classic.tar.xz"
9185
BIBATA_DIR="/usr/share/icons/Bibata-Modern-Classic"

automation/37-aichat.sh

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,16 @@ install_packages "ai"
1111

1212
echo "[37-aichat] Installing AIChat and AIChat-NG binaries..."
1313

14-
# Fetch latest release tags
15-
# v0.2.0: Wrap in subshell + || true to prevent pipefail from killing the script if API is down
14+
# Resolve latest release tags from upstream. Project policy: every dependency
15+
# tracks :latest from its source, so no fallback pin — if api.github.com is
16+
# unreachable, fail loud rather than silently shipping a stale version.
1617
AICHAT_TAG=$( (scurl -s https://api.github.com/repos/sigoden/aichat/releases/latest | grep -Po '"tag_name": "\K.*?(?=")') 2>/dev/null || true)
1718
AICHAT_NG_TAG=$( (scurl -s https://api.github.com/repos/blob42/aichat-ng/releases/latest | grep -Po '"tag_name": "\K.*?(?=")') 2>/dev/null || true)
1819

19-
# Fallbacks if API fails
20-
if [[ -z "$AICHAT_TAG" ]]; then
21-
AICHAT_TAG="v0.25.0"
22-
echo "[37-aichat] AIChat API unavailable — using fallback ${AICHAT_TAG}"
23-
else
24-
echo "[37-aichat] Detected AIChat version: ${AICHAT_TAG}"
25-
fi
26-
27-
if [[ -z "$AICHAT_NG_TAG" ]]; then
28-
AICHAT_NG_TAG="v0.25.0"
29-
echo "[37-aichat] AIChat-NG API unavailable — using fallback ${AICHAT_NG_TAG}"
30-
else
31-
echo "[37-aichat] Detected AIChat-NG version: ${AICHAT_NG_TAG}"
32-
fi
20+
[[ -n "$AICHAT_TAG" ]] || die "AIChat: api.github.com release-latest lookup returned empty"
21+
[[ -n "$AICHAT_NG_TAG" ]] || die "AIChat-NG: api.github.com release-latest lookup returned empty"
22+
echo "[37-aichat] AIChat latest: ${AICHAT_TAG}"
23+
echo "[37-aichat] AIChat-NG latest: ${AICHAT_NG_TAG}"
3324

3425
# ── AIChat ────────────────────────────────────────────────────────────────────
3526
AICHAT_ARCH="aichat-${AICHAT_TAG}-x86_64-unknown-linux-musl.tar.gz"

0 commit comments

Comments
 (0)