AC Native OS — Initialization Analysis #
Internal technical analysis of the full boot path from EFI firmware to running JS piece.
Source files analyzed:
fedac/native/initramfs/init(81 lines)fedac/native/src/ac-native.c(~2300+ lines, focus onmain()and boot functions)fedac/native/src/audio.c(1497 lines)fedac/native/scripts/build-and-flash.sh(956 lines)fedac/native/ac-os(build orchestrator)fedac/native/Makefile
1. EFI Boot #
The system boots via UEFI from a GPT-partitioned drive with a single EFI System Partition (ESP), FAT32-formatted, labeled AC-NATIVE.
Partition layout (build-and-flash.sh:876-879):
label: gpt
type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, size=512M
Boot chain: The kernel (vmlinuz) is a standard Linux bzImage with the initramfs CPIO archive (LZ4-compressed) embedded directly via CONFIG_INITRAMFS_SOURCE. This means the kernel and initramfs are a single file.
Two boot layouts are supported:
- Direct EFI stub —
EFI/BOOT/BOOTX64.EFIis the kernel itself (default for USB flash). - Splash chainloader — If
fedac/native/bootloader/splash.efiexists, it is placed atBOOTX64.EFIand the kernel goes toEFI/BOOT/KERNEL.EFI. The splash EFI displays a logo while the kernel loads (build-and-flash.sh:900-908). - systemd-boot layout — Detected during install-to-disk: kernel at
EFI/Linux/vmlinuz-ac-nativewith separate loader configs.
The kernel version defaults to 6.14.2 (build-and-flash.sh:36). It is compiled with Intel WiFi firmware (iwlwifi-*) built in via CONFIG_EXTRA_FIRMWARE, i915 GPU drivers, HDA audio, and all necessary subsystems for the target ThinkPad-class hardware.
2. Init Script (/init — PID 1) #
The init script is a minimal POSIX shell script (#!/bin/sh) that runs as PID 1 inside the initramfs. It never uses systemd, udev, or any service manager.
Filesystem Mounts (lines 4-13) #
Mounts are performed in order with error suppression:
| Mount Point | Type | Notes |
|---|---|---|
/proc |
proc | |
/sys |
sysfs | |
/dev |
devtmpfs | Kernel-populated device nodes |
/dev/pts |
devpts | PTY support (ptmxmode=0666) |
/dev/shm |
tmpfs | Shared memory |
/tmp |
tmpfs | Temporary storage |
/run |
tmpfs | Runtime data |
/sys/kernel/tracing |
tracefs | Performance tracing |
/sys/kernel/debug |
debugfs | Debug interfaces |
Credential Restoration (lines 15-34) #
The build system bakes Claude Code credentials into the initramfs at /claude-creds.json and /claude-state.json. Because /tmp is mounted as tmpfs (hiding the initramfs's own /tmp), the init script copies these from the initramfs root:
/claude-creds.json->/tmp/.claude/.credentials.json/claude-state.json->/tmp/.claude.json- Seeds
/tmp/.claude/settings.jsonwith default permissions (all tools allowed,installMethod: "native", auto-updates off) - Copies
/device-claude.md->/tmp/ac/CLAUDE.md(working dir for Claude Code sessions)
Logging (line 37) #
An ilog() function writes to /tmp/ac-init.log (tmpfs). This log is later copied to USB by ac-native's try_mount_log() for post-mortem debugging.
zram Swap (line 42) #
Creates 1GB compressed RAM swap via zram:
modprobe zram && echo 1G > /sys/block/zram0/disksize && mkswap /dev/zram0 && swapon /dev/zram0
This effectively doubles usable memory for Firefox/GTK browser sessions.
Networking (line 45) #
Brings up loopback interface (ip link set lo up) for localhost connections (needed for Claude OAuth callback server).
Environment (lines 47-51) #
Sets PATH, SSL_CERT_FILE, CURL_CA_BUNDLE, SSL_CERT_DIR for curl/OpenSSL trust in the initramfs environment.
System Setup (lines 53-59) #
- Creates minimal
/etc/groupand/etc/passwd(root only) for seatd compatibility. - Sets all CPU cores to
performancegovernor.
Kernel Log Dump (lines 62-77) #
Before launching ac-native, the init script attempts to dump kernel boot logs to USB for debugging:
- Scans
/dev/sda1,/dev/sdb1,/dev/sdc1for block devices - Mounts the first available one at
/mnt - Reads
/dev/kmsg(non-blocking, 1-second read window) and writes to/mnt/dmesg.log - Copies
/tmp/ac-init.logto/mnt/init.log
Exec (line 80) #
exec /ac-native /piece.mjs
The init script replaces itself with the ac-native binary (PID 1 preserved). The default piece is /piece.mjs (baked into initramfs at build time, defaults to prompt.mjs).
3. ac-native Main Boot Sequence #
The main() function begins at line 1773 of ac-native.c. The binary records a monotonic clock timestamp at entry (boot_start) and reports total boot time at the end.
3.1 PID 1 Detection and Filesystem Setup (lines 1777-1802) #
Two code paths based on PID:
PID 1 (direct DRM boot — no cage):
- Calls
mount_minimal_fs()(line 217) which mounts proc, sysfs, devtmpfs, devpts, shm - Creates 1GB zram swap (again — safe if already done by init, skips silently)
- Brings up loopback
- Waits up to 1 second for
/dev/dri/card0or/dev/fb0to appear (line 246, 100 iterations x 10ms) - Sets all CPUs to performance governor
Non-PID-1 (under cage compositor):
- Filesystems already mounted by init/parent
- Opens
cage-child.logon USB or falls back to tmpfs
Both paths set PATH and SSL environment variables.
3.2 Signal Handling (lines 1804-1814) #
SIGINT,SIGTERM-> graceful shutdown (running = 0)SIGUSR1-> DRM master release (for browser handoff)SIGUSR2-> reboot requestSIGSEGV,SIGBUS,SIGABRT,SIGFPE-> crash report to/mnt/crash.jsonwith machine ID and timestamp, then re-raise
3.3 Display Initialization (lines 1834-1880) #
Two display backends, selected at runtime:
Wayland path (when WAYLAND_DISPLAY env var is set — running under cage):
- Calls
wayland_display_init()which connects to the cage compositor - Creates a minimal
ACDisplaystruct with width/height from Wayland surface - On failure: exits with code 1 (cage will restart or fall through to DRM)
DRM path (direct framebuffer — default for PID 1 boot):
- Calls
drm_init()which opens/dev/dri/card0, finds a connected CRTC, and sets up double-buffered dumb buffers - Fatal if no display found — powers off after 5 seconds if PID 1
Framebuffer creation (line 1869):
- Software framebuffer at 1/3 display resolution:
fb_create(display->width / 3, display->height / 3) - Default
pixel_scale = 3(3x nearest-neighbor upscale on present)
3.4 Graphics and Font Init (lines 1883-1888) #
graph_init()— software rendering context bound to framebufferfont_init()— loads built-in bitmap font- Creates separate cursor overlay framebuffer (prevents KidLisp effects from smearing cursor)
3.5 USB/Disk Mount and Log Setup (line 1894) #
try_mount_log() (defined at line 266):
- Waits up to 2 seconds for USB block devices (
/dev/sda1,/dev/sdb1) to appear (100 iterations x 20ms) - Two-pass mount strategy:
- Pass 0: Try removable devices first (USB install source)
- Pass 1: Fall back to internal ESP (for disk-installed boots)
- Scans:
sda1, sdb1, sdc1, sdd1, nvme0n1p1, nvme1n1p1 - Mounts as VFAT at
/mnt - Opens
/mnt/ac-native.login append mode - Dumps init log (
/tmp/ac-init.log) and cage stderr to/mnt/init.log - Logs all available block devices with removable status
3.6 Config Loading (line 1897) #
load_boot_visual_config() (defined at line 542):
Reads /mnt/config.json (USB/HD) or falls back to /default-config.json (baked in initramfs).
Extracts:
handle— setsAC_HANDLEenv var, generates time-of-day greeting as boot title (e.g., "good morning @jeffrey")colors— per-character RGB colors for the boot title text, from thehandle-colorsAPIwifi— boolean, defaults to true; when false, setswifi_disabled = 1claudeCreds— JSON object written to/tmp/.claude/.credentials.jsonclaudeState— JSON object written to/tmp/.claude.jsonvoice— "off" disables keystroke TTS
The config supports an AC_IDENTITY_BLOCK_V1 header line (used by ac-usb flash tool).
3.7 Audio Init (line 1900) #
audio_init() (defined at audio.c:616):
This is the most timing-sensitive subsystem. The function:
-
Allocates state —
ACAudiostruct with defaults: BPM 120, sample rate 192kHz (default target), reverb buffers, 10-second sample buffer at 48kHz, TTS ring buffer (5 seconds at output rate). -
Seeds default sample — Generates a 550ms synthesized one-shot (240Hz with wobble and exponential decay) so sample-mode is playable before first mic recording (audio.c:587-614).
-
Waits for sound card — Polls for
/dev/snd/pcmC{0,1,2}D0pfor up to 8 seconds (400 iterations x 20ms) at audio.c:665-670. This is the critical wait for HDA codec probe completion. -
Diagnostics — On timeout, distinguishes between:
- HDA controller present but codec not probed (
/dev/snd/controlC0exists but no PCM device) - No hardware at all
Dumps
/proc/asound/cardsand/dev/snd/contents to/mnt/ac-audio.log.
- HDA controller present but codec not probed (
-
ALSA device open — Tries 11 device names with 5 retry attempts (2-second sleep between retries, audio.c:722-743):
hw:0,0 hw:1,0 hw:0,1 hw:1,1 hw:0,2 hw:0,3 hw:1,2 hw:1,3 plughw:0,0 plughw:1,0 defaultTotal worst case: 5 attempts x 2s sleep = 10 additional seconds on top of the 8s card wait.
-
ALSA configuration (audio.c:755-801):
- Format: S16_LE interleaved, 2 channels
- Rate: Requests 192kHz (AUDIO_SAMPLE_RATE), accepts whatever the hardware provides via
set_rate_near - Period size:
AUDIO_PERIOD_SIZE(negotiated) - Buffer: 6 periods for underrun tolerance
- Logs actual vs. requested rate (common mismatch on some codecs)
-
Mixer setup (audio.c:809-875):
- Opens ALSA mixer for the detected card
- Unmutes every playback switch found
- Sets all playback volumes to maximum
- Enables all capture switches and sets capture volume to 90% of max
- Attempts HDA codec verb injection for ALC257 (Speaker node 0x14, HP Out node 0x21)
-
HDMI audio (audio.c:882-920) — Best-effort secondary output:
- Tries:
hdmi:0,0throughhdmi:1,1,plughw:0,3,plughw:0,7,plughw:0,8,plughw:1,3,plughw:1,7 - Opens non-blocking, 48kHz, 512-frame period
- Calculates downsample ratio from primary rate
- Tries:
-
Audio thread (audio.c:924) — Starts the real-time audio thread at SCHED_FIFO priority 50.
Audio is treated as optional — if no ALSA device is found after all retries, audio->pcm is NULL and the struct is returned with a status message. The rest of the system continues without sound.
3.8 TTS Init and Precache (lines 1901, 1928-1929) #
In DRM boot path only:
-
tts_init(audio)(tts.c:125) — Initializes Flite with two voices:cmu_us_slt(female, primary)cmu_us_kal(male, secondary)- Both set to 0.9x duration stretch (slightly faster speech)
- Starts a background TTS thread
-
tts_precache(tts)(tts.c:204) — Pre-renders all lowercase letters (a-z) and digits (0-9) as cached TTS waveforms. This eliminates latency on keystroke speech during typing.
3.9 Boot Beep (line 1927) #
audio_boot_beep(audio) (audio.c:1407-1413):
- Two-tone "doo-dah": E5 (660Hz, 120ms) then B5 (990Hz, 150ms) with 80ms gap
- Stereo-panned slightly left then right
3.10 Boot Animation — Startup Fade (line 1937) #
draw_startup_fade() (defined at line 1288):
300 frames at 60fps = 5-second animation.
- Immediate solid frame — White (daytime 7am-6pm LA time) or black (nighttime) to hide kernel console text.
- Version check — Compares
AC_GIT_HASH-AC_BUILD_TSagainst/mnt/booted-version. Marks as "FRESH" if different. Appends to/mnt/boot-history.log. - Machine ID — Reads
/mnt/.machine-idor generatesnotepat-XXXXXXXX(random hex from/dev/urandom). - TTS greeting (frame 10) — Speaks time-of-day greeting with handle name and build name: "good morning jeffrey. enjoy Los Angeles! fuzzy-meadow."
- Visual elements:
- Fade from solid color to time-of-day themed background (cream/warm white for day, deep blue/sunset purple for night)
- Centered title with per-character handle colors (animated pulse)
- Version panel (top-right): build name, git hash, build timestamp, "FRESH" badge
- "enjoy Los Angeles!" subtitle (appears after frame 130)
- Auth badges (bottom-left): pixel-art crab (Claude token present) and octocat (GitHub PAT present)
- Shrinking time bar at bottom
- "W: install to disk" hint (only when booting from removable USB)
- Input handling — Opens all
/dev/input/event*devices non-blocking:- First 60 frames (1 second): all key presses are drained/ignored
- After frame 60:
Wkey triggers install-to-disk flow, any other key skips animation
3.11 Input Init (line 1944) #
input_init() (input.c:315):
- Scans all
/dev/input/event*devices - Opens each with
O_RDONLY | O_NONBLOCK | O_CLOEXEC - Checks event bits: accepts devices with EV_KEY, EV_ABS, EV_REL, or EV_SW
- Detects tablet mode (EV_SW + SW_TABLET_MODE) with sysfs fallback for ThinkPad ACPI
- Identifies NuPhy keyboards by vendor ID — marks them for hidraw analog handling instead of evdev
- Calls
hidraw_scan()for analog keyboard support via HID raw protocol
3.12 Install-to-Disk Flow (lines 1967-1981) #
Triggered only when user pressed W during boot animation and confirmed with Y:
auto_install_to_hd() (line 713):
- Identifies source (removable USB with kernel at
EFI/BOOT/BOOTX64.EFI) - Finds target non-removable block device (NVMe or SATA)
- Creates GPT with EFI System Partition via
sfdisk - Formats FAT32, copies kernel and config.json
- Supports both direct EFI stub and systemd-boot layouts
- Displays animated progress during install
- On success: prompts reboot (with TTS "rebooting", shutdown sound, 600ms delay)
3.13 WiFi Init (line 1987) #
wifi_init() (wifi.c:653):
- Checks for
iwbinary existence - Logs diagnostic info: network interfaces, firmware files, kernel wireless messages from
/dev/kmsg, PCI device enumeration - Waits up to 3 seconds for wireless interface (30 attempts x 100ms)
- If found, stores interface name (e.g.,
wlan0) wifi_autoconnect()called immediately after — connects to saved/preset network
WiFi is skipped entirely if wifi_disabled is set from config.json.
3.14 Secondary HDMI Display (lines 1997-2000) #
drm_init_secondary() — attempts to find a second connected DRM output for HDMI mirroring/extension.
3.15 JS Runtime Init (line 2004) #
js_init() (js-bindings.c:1637):
- Allocates
ACRuntimestruct with pointers to all subsystems (graph, input, audio, wifi, tts) - Creates WebSocket and UDP client handles
- Initializes 3D camera
- Creates QuickJS-ng runtime and context
- Registers
FormandPaintingJS classes - Sets up module loader (
JS_SetModuleLoaderFunc) - Registers the full native API surface:
- Graphics chain API: box, line, circle, plot, write, scroll, blur, zoom, contrast, spin, qr, form, ink
- Top-level graphics: wipe, ink, line, box, circle, qr, plot, write, scroll, blur, zoom, contrast, spin
- console: log, warn, error
- performance: now()
- Evaluates init JS code (Button, Box classes)
- Evaluates browser API stubs for KidLisp compatibility
3.16 Config Application to Runtime (lines 2016-2077) #
After JS init, re-reads /mnt/config.json to populate runtime fields:
rt->handleandrt->piece(boot piece override)- Voice config ("off" disables keystroke TTS)
- Claude token ->
/claude-token - GitHub PAT ->
/github-pat - Piece aliases: "claude" and "cc" resolve to "terminal" with param "claude"
- Resolves piece path:
/pieces/{name}.mjs
3.17 Piece Load and Boot (lines 2080-2144) #
js_load_piece(rt, piece_path)— Evaluates the.mjsfile, extractsboot,paint,act,sim,leave,beatfunction references- Logs boot time:
"Booted in %.1fms"(line 2105) - Logs subsystem status: audio, backlight, input devices, NuPhy analog status, JS function availability
- Waits for TTS to finish greeting + 300ms drain + plays ready melody (C5-E5-G5 ascending triad) + 400ms ring-out
audio_prewarm()— plays near-silent 440Hz note (50ms, 0.001 volume) to fill ALSA buffers- Drains queued input events from boot animation
js_call_boot(rt)— Calls the piece'sboot()functionperf_init()— Initializes performance logger (30-second CSV chunks to/mnt/perf/)
3.18 DRM-to-Cage Transition (lines 2149-2280) #
After boot completes in DRM mode (compiled with USE_WAYLAND), the system attempts to transition to a Wayland session for browser popup support:
- Destroys audio (cage child will re-open ALSA)
- Releases DRM master
- Forks:
- Child: Sets env vars (
WLR_RENDERER=pixman,WLR_BACKENDS=drm), creates XDG runtime dir, startsseatd -g root, waits up to 3s for/run/seatd.sock, thenexeclp("cage", "cage", "-s", "--", "/ac-native", "/piece.mjs") - Parent: Waits for cage to exit, copies cage stderr to USB log
- Child: Sets env vars (
This means ac-native boots twice: once in DRM for fast startup, then re-execs under cage for full Wayland capabilities.
4. Timing #
Based on log data and code analysis, approximate timing from kernel handoff:
| Phase | Duration | Cumulative | Notes |
|---|---|---|---|
| Kernel + initramfs decompress | ~1-2s | ~1-2s | LZ4 decompression, driver probe |
| Init script (mounts, zram, dmesg dump) | ~1-2s | ~2-4s | dmesg dump takes ~1s (sleep 1) |
| Display wait | 0-1s | ~2-5s | Up to 1s for /dev/dri/card0 |
| USB mount + log setup | 0-2s | ~2-7s | Up to 2s waiting for USB block devs |
| Config load | <1ms | — | Simple file read + JSON parse |
| Audio init | 0-18s | ~2-25s | Highly variable — see Known Issues |
| TTS init + precache | ~0.5-1s | — | 36 Flite renders (a-z, 0-9) |
| Boot animation | 0-5s | — | Skippable with any keypress |
| Input init | <100ms | — | evdev + hidraw scan |
| WiFi init | 0-3s | — | Interface detection wait |
| JS init + piece load | ~100-200ms | — | QuickJS context + module eval |
| Total (typical, audio found quickly) | ~9s | From kernel to piece running |
The ac_log at line 2105 reports "Booted in %.1fms" measuring from main() entry to piece load completion. With well-behaved audio hardware (card appears quickly, first ALSA device opens on attempt 0), total time from kernel to playable piece is approximately 9 seconds.
5. Known Issues #
5.1 ALSA Race Condition (HDA Codec Probe Timing) #
The primary boot-time variability comes from audio initialization.
On fast NVMe boots, the HDA codec may not be fully probed when ac-native first tries to open ALSA. The code handles this with a two-layer retry:
-
Card wait loop (audio.c:665-670): Polls for
/dev/snd/pcmC{0,1,2}D0pfor 8 seconds. If timeout occurs and/dev/snd/controlC0exists, it means the HDA controller initialized but the codec hasn't finished probing — this is logged as"HDA ctrl ok, codec not probed". -
ALSA device open retries (audio.c:722-743): Even after a PCM device node appears,
snd_pcm_open()can fail if the codec isn't fully ready. The code retries 5 times with 2-second intervals across 11 different device names.
Worst case: 8s card wait + 5 retries x 2s = 18 seconds for audio init alone. On machines where audio hardware is ready quickly, this completes in under 1 second.
The diagnostic output is written to /mnt/ac-audio.log (separate from the main log) with full /proc/asound/cards and /dev/snd/ directory listings.
5.2 Missing Shared Libraries #
The build system uses dynamic linking by default (gcc, not musl-gcc). This means all shared libraries must be correctly bundled into the initramfs. The build script (build-and-flash.sh) handles this with:
- Per-binary
lddscanning for direct dependencies - A two-pass transitive dependency resolver (lines 740-769) that scans all ELF files in the initramfs and copies missing
.sofiles
Historical issues:
- Libraries missed by single-pass
ldd(transitive deps of deps) - Unversioned
.sosymlinks needed fordlopen()but not created - Mesa GPU stack requiring
libexpat,libffi,libxkbcommonthat aren't direct deps of ac-native
The build now generates an initramfs manifest (build/initramfs-manifest.txt, copied into the image as /manifest.txt) for build parity verification.
5.3 Firmware Requirements #
Required firmware blobs (bundled in initramfs at /lib/firmware/):
| Firmware | Purpose |
|---|---|
iwlwifi-9260-th-b0-jf-b0-*.ucode |
Intel WiFi 9260 |
iwlwifi-cc-a0-*.ucode |
Intel WiFi AX200 |
iwlwifi-QuZ-a0-hr-b0-*.ucode |
Intel WiFi AX201 |
iwlwifi-QuZ-a0-jf-b0-*.ucode |
Intel WiFi AX201 (variant) |
regulatory.db + .p7s |
Wireless regulatory database |
i915/* |
Intel GPU display firmware (all generations) |
intel/sof/*, intel/sof-ipc4/* |
Intel SOF audio firmware |
intel/sof-tplg/*, intel/sof-ipc4-tplg/* |
SOF audio topology |
The kernel config also embeds a subset of WiFi firmware via CONFIG_EXTRA_FIRMWARE for early availability before the initramfs mounts firmware. The build script decompresses .xz and .zst compressed firmware from the host system.
5.4 Wayland/Cage Transition Complexity #
The DRM-to-cage transition (Section 3.18) is inherently fragile:
- Audio must be destroyed and re-opened by the cage child
- DRM master must be released before cage can take it
- seatd must start successfully within 3 seconds
- The ac-native binary effectively boots twice (DRM then Wayland)
- If cage fails, the parent logs the error but the system stays on the DRM frame (no automatic fallback to DRM-only mode with continued operation)
5.5 Clock Accuracy #
Time-of-day greetings and themed backgrounds depend on get_la_hour() which computes LA time. Since the system has no RTC battery on some hardware and NTP hasn't run yet at boot, the kernel time may be epoch (1970). The code accepts this gracefully — it just shows the wrong greeting.
6. Build Pipeline #
Local builds (ac-os build) #
Orchestrated by fedac/native/ac-os:
-
Binary build (
build_binary()):- Runs
make CC=gcc(or musl-gcc if available with linux/input.h) - Passes
BUILD_TSenv var for timestamp embedding - Verifies the binary contains the current git hash via
stringscheck - Builds BPF trace tools if Makefile exists
- Runs
-
Initramfs build (
build_initramfs()):- Runs
build-and-flash.sh --skip-kernel --skip-binaryunder sudo - KidLisp bundling done as user (npx unavailable under sudo):
npx esbuildbundleskidlisp.mjsas IIFE - Copies fresh binary into initramfs
- Claude Code native binary: copies from
~/.local/share/claude/versions/(latest), bakes OAuth token from~/.claude/.credentials.json, bakes GitHub PAT fromgh auth token - Bakes credentials into initramfs root files
- Runs
-
Kernel build: Runs
build-and-flash.shfor kernel compilation with embedded initramfs
Makefile version stamps (Makefile:10-15) #
GIT_HASH := $(shell git rev-parse --short HEAD)
BUILD_TS := $(shell date -u '+%Y-%m-%dT%H:%M')
BUILD_NAME := $(shell scripts/build-name.sh --bump)
These are compiled into the binary via -DAC_GIT_HASH, -DAC_BUILD_TS, -DAC_BUILD_NAME and used for:
- Boot screen version panel
- TTS greeting (build name spoken aloud)
/mnt/booted-versioncomparison for "FRESH" detection/mnt/boot-history.logentries
Critical note (Makefile:123 comment): Make's dependency tracking doesn't detect changes to AC_BUILD_NAME or AC_GIT_HASH because they're not source file changes. The Makefile forces a rebuild of the main object file to prevent stale version strings.
Oven builds vs. local builds #
The primary differences:
| Aspect | Local (ac-os) |
Oven (CI/Docker) |
|---|---|---|
| Compiler | gcc (dynamic) or musl-gcc (static) | gcc (dynamic) |
| Claude binary | From ~/.local/share/claude/versions/ |
Downloaded from GCS (claude-code-releases) |
| Claude credentials | From ~/.claude/.credentials.json |
Baked by ac-usb flash injection |
| GitHub PAT | From gh auth token |
Baked by flash injection |
| KidLisp bundle | npx esbuild (user space) |
Not available under sudo |
| Firmware | Host /lib/firmware/ (all generations) |
Host /lib/firmware/ (Docker image) |
| i915 blobs | All from /lib/firmware/i915/ |
All from Docker image |
| SOF audio | All from host | All from Docker image |
| Manifest | Generated and embedded | Generated and embedded |
| Build name | scripts/build-name.sh --bump |
Same (increments per build) |
Both produce the same artifact structure: a single vmlinuz with embedded LZ4 initramfs CPIO. The build parity is verified via the initramfs manifest file.
Flash pipeline #
ac-os flash extends the build with USB write:
- Full rebuild (binary + initramfs + kernel)
- GPT partition table with 512MB ESP
- FAT32 format labeled
AC-NATIVE - Kernel copied to
EFI/BOOT/BOOTX64.EFI(or splash chainloader +KERNEL.EFI) config.jsonwith handle + colors fromhandle-colorsAPI- Credentials injected (Claude token, GitHub PAT, Claude settings)
ac-os upload always rebuilds first (kernel embeds git hash at compile time), then uploads to OTA release endpoint.