Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,120 @@ Not yet implemented (planned):
- Factory reset (backup, wipe, restore)
- Flash modes (disk clone, network, HTTP/HTTPS)

## Startup Flow

The diagram traces every phase from PID 1 start to `switch_root`. The `release-image`
feature flag determines the error-handling branch at each fatal failure point.

```mermaid
flowchart TD
START([PID 1 starts]) --> MOUNT_ESS["mount_essential_filesystems\n/dev · /proc · /sys · /run"]

MOUNT_ESS -->|OK| LOGGER["KmsgLogger::init()"]
MOUNT_ESS -->|Fail| EARLY_ERR{Image type?}
EARLY_ERR -->|release| HALT1(["🔴 eprintln loop — halt"])
EARLY_ERR -->|debug| ESHELL(["🐚 emergency sh — respawn"])

LOGGER -->|OK| CONFIG["Config::load()\n/proc/cmdline · os-release"]
LOGGER -->|Fail| FEB

CONFIG -->|OK| RDEV["detect_root_device()"]
CONFIG -->|Fail| FEB

RDEV -->|OK| LAYOUT["PartitionLayout::new()\ncreate_omnect_symlinks()"]
RDEV -->|Fail| FEB

LAYOUT -->|OK| CORE["mount_core_partitions()\nrootfs + boot + fsck"]
LAYOUT -->|Fail| FEB

CORE --> BENV["open_boot_env()"]

BENV --> CLASSIFY{"classify_boot_env"}
CLASSIFY -->|"OK → Available"| APPLY["apply_boot_env_decision()\ncore_result × env decision\npersist_fsck_results — always"]
CLASSIFY -->|"Fail + release → Degraded"| APPLY
CLASSIFY -->|"Fail + debug → Abort"| FEB

APPLY -->|FsckRequiresReboot| FEB
APPLY -->|Fatal| FEB
APPLY -->|"OK\nDegraded: ods.degraded_boot=true"| FBDETECT["compute_first_boot()\nset_update_pending()"]

FBDETECT --> ISETUP["init_setup::run()\nresize-data preflight\nif feature = resize-data"]
ISETUP -->|FsckRequiresReboot| FEB
ISETUP -->|"ResizeData error\nContinueDegraded — warn"| BMODE["BootMode::detect() → Normal"]
ISETUP -->|"Fatal (non-resize)"| FEB
ISETUP -->|OK| BMODE

BMODE -->|Fatal| FEB

BMODE --> MREM["mount_remaining_partitions()\ndata · factory · cert + fsck\npersist_fsck_results — always"]
MREM -->|FsckRequiresReboot| FEB
MREM -->|Fatal| FEB
MREM -->|OK| OVL["setup_raw_rootfs_mount()\nsetup_etc_overlay()\nsetup_data_overlay()"]
Comment thread
JanZachmann marked this conversation as resolved.

OVL -->|OK| LINKS["create_fs_links()\ncreate_ods_runtime_files()"]
OVL -->|Fail| FEB

LINKS -->|OK| FBM["write_first_boot_marker()\nif first_boot ∧ resize_ok ∧ env_available\nbest-effort — warn on fail"]
LINKS -->|Fail| FEB

FBM --> SR["switch_root → systemd"]
SR -->|OK| SUCCESS(["✅ systemd running"])
SR -->|Fail| FEB

FEB{"Error handler\nRecoveryClass?"}
FEB -->|"RebootToApply (e.g. FsckRequiresReboot)"| REBOOT(["🔁 Reboot"])
FEB -->|"Fatal + update_pending"| REBOOT
FEB -->|"Fatal + no update + release"| HALT2(["🔴 kmsg loop — halt forever"])
FEB -->|"Fatal + no update + debug"| DSHELL(["🐚 debug bash/sh — respawn"])

classDef success fill:#2d6a2d,color:#fff,stroke:#1a3d1a
classDef reboot fill:#1a4d7a,color:#fff,stroke:#0d2d4d
classDef halt fill:#7a1a1a,color:#fff,stroke:#4d0d0d
classDef shell fill:#7a4a1a,color:#fff,stroke:#4d2d0d

class SUCCESS success
class REBOOT reboot
class HALT1,HALT2 halt
class ESHELL,DSHELL shell
```

**Terminal states**

| Symbol | Outcome | Trigger |
|--------|---------|---------|
| ✅ | `switch_root` — systemd takes over | Normal completion |
| 🔁 | Reboot | `FsckRequiresReboot` (unconditional); or any fatal error while `omnect_validate_update` is set — triggers bootloader OTA rollback |
| 🔴 | Halt (kmsg loop, infinite) | Fatal error · release image · no OTA in flight |
| 🐚 | Debug shell (bash → sh fallback, respawning) | Fatal error · debug image · no OTA in flight |

**Notes on error handling**

All errors from `run_init()` reach `handle_fatal_error` in `main.rs`, which dispatches on
`RecoveryClass`:
- `RebootToApply` (e.g. `FsckRequiresReboot`) → always Reboot, regardless of image type
- `Fatal` + `omnect_validate_update` set → Reboot (bootloader OTA rollback)
- `Fatal` + no OTA in flight + release → Halt (kmsg loop)
- `Fatal` + no OTA in flight + debug → debug shell

`FsckRequiresReboot` edges in the diagram flow through this handler.

**Notes on overlay, fs-link, and ODS setup (`OVL` / `LINKS` blocks)**

These steps (`setup_raw_rootfs_mount`, `setup_etc_overlay`, `setup_data_overlay`,
`create_fs_links`, `create_ods_runtime_files`) abort the boot on any failure: the error
reaches `handle_fatal_error`, which halts the device on a release image, drops to a debug
shell on a debug image, or reboots when an OTA update is in flight (`update_pending`).
No dedicated design spec covers this region.

**Notes on `apply_boot_env_decision`**

`mount_core_partitions` result is captured rather than propagated immediately so that
fsck diagnostics can be persisted to the bootloader environment before any reboot.
`apply_boot_env_decision` enforces the invariant that `FsckRequiresReboot` always wins
over a concurrent `DegradedBoot` — the two failure modes can co-occur when GRUB's
boot partition is unmountable. `persist_fsck_results` runs on every mount path,
including degraded boot.

## Building

```bash
Expand Down
5 changes: 5 additions & 0 deletions docs/superpowers/specs/2026-05-04-resize-data-design.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# resize-data: Data Partition Auto-Resize

**Status:** Partially superseded — the "resize failure is fatal" claim (§ Error Handling,
line ~130) was reclassified to `ContinueDegraded` (non-fatal) in
`2026-05-27-fsck-and-resize-design.md` and `2026-06-22-first-boot-retry-on-resize-failure.md`.
The resize mechanism itself remains valid. Retained for design history.

## Problem

When an omnect OS image is flashed to a disk larger than the image itself, the
Expand Down
6 changes: 5 additions & 1 deletion docs/superpowers/specs/2026-05-12-first-boot-mode-design.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Design: BootMode::FirstBoot and resize-data relocation

**Date:** 2026-05-12
**Status:** Draft
**Status:** Superseded by `2026-05-13-preflight-design.md`
**Branch:** TBD (follows feat/resize-data)

> **Note:** This design was superseded before implementation. `BootMode::FirstBoot` and
> `mode::first_boot::run` were never shipped. Resize-data was implemented as a preflight
> step instead — see `2026-05-13-preflight-design.md`. Retained for design history.

---

## 1. Motivation
Expand Down
7 changes: 5 additions & 2 deletions docs/superpowers/specs/2026-05-13-preflight-design.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Preflight: Separate One-Time Prep from Mode Dispatch

**Date:** 2026-05-13
**Status:** Draft
**Status:** Partially superseded — the "resize failure is fatal" claim (verification table,
line ~191) was reclassified to `ContinueDegraded` (non-fatal) in
`2026-05-27-fsck-and-resize-design.md` and `2026-06-22-first-boot-retry-on-resize-failure.md`.
The preflight separation design itself remains valid.
**Supersedes:** `2026-05-12-first-boot-mode-design.md`

---
Expand Down Expand Up @@ -188,7 +191,7 @@ branch in `BootMode::detect()`.
| Resize skipped when data partition missing | ✓ (filesystem layer) | ✓ (filesystem layer unchanged) |
| Resize sets guard after success | ✓ | ✓ |
| Resize runs after core mount, before data mount | ✓ | ✓ |
| Resize failure is fatal | ✓ | ✓ |
| Resize failure is fatal | ✓ | ✓ | ← **superseded**: reclassified to `ContinueDegraded` in `2026-05-27-fsck-and-resize-design.md` |
| `normal::run` mounts remaining + overlays + ODS + switch_root | ✓ | ✓ |
| Degraded boot when bootloader unavailable | ✓ | ✓ |

Expand Down