- Restore 29 recipe symlinks (libdrm, qtbase, dbus, sddm, pipewire, etc.) - Restore 33 patches (KDE, libdrm, mesa, pipewire, sddm, wireplumber) - Restore 20+ local/scripts (audit, lint, test, build helpers) - Restore src/cook/scheduler.rs, status.rs, gnu-config/ - Restore scripts/patch-inclusion-gate.sh, run_mini1.sh, validate-collision-log.sh - Recover TLC source from HEAD (was overwritten by 0.2.3 checkout) - Recover 11 local/docs plans from HEAD (were overwritten) - Recover qt6-wayland-smoke symlink from HEAD - Fix MOTD: remove garbled ASCII art, use clean text - Update version: 0.2.0 -> 0.2.4 in os-release, motd, config - Reduce filesystem_size: 1536 -> 512 MiB - Add ABSOLUTE RULE to AGENTS.md: never delete/ignore packages - Reduce pcid scheme log verbosity: info -> debug
20 KiB
Red Bear OS /scheme/ Namespace Population Plan
Version: 1.0 (2026-06-12)
Status: Draft — pending review
Canonical: local/docs/SCHEME-NAMESPACE-POPULATION-PLAN.md
Blocks: Writable rootfs on live ISO, redoxfs disk discovery, ls /scheme/ in shell
Cross-references: Linux kobject/uevent, Fuchsia Zircon/Component Manager, seL4 CSpace, Plan 9 per-process namespace, Genode capability routing, MINIX 3 driver model
1. Problem Statement
ls /scheme/ hangs or returns empty in Red Bear OS. Three root causes:
- initnsmgr
getdentsdepends on daemons registering — but boot ordering means some schemes haven't registered yet whenredoxfscallsfs::read_dir("/scheme")to find disk devices. - No aggregator for block devices —
redoxfsmust enumerate alldisk.*schemes individually, but/scheme/disk.livemay not exist yet when the rootfs mount runs at priority 50. - driver-block
getdentsreturnsEOPNOTSUPP— individual disk schemes use legacy text-based listing, not propergetdents.
The result: redoxfs can't discover disks, rootfs fails to mount read-write, and /scheme/
listing is incomplete.
2. Design Principles (Informed by Cross-Reference)
2.1 Microkernel Principle (seL4, Red Bear OS)
The kernel tracks scheme IDs (integers), not names. All name→ID mapping happens in userspace
(initnsmgr). This is correct per the user's explicit correction:
"Kernel does not have to track id-name mapping! Kernel only knows about IDs. It's a microkernel and stuff like this must be done in userspace"
Implication: We never modify the kernel to "export" scheme names. The namespace is purely
a userspace construct managed by initnsmgr.
2.2 Aggregator Pattern (Linux devtmpfs + Fuchsia devcoordinator)
Linux populates /dev via two mechanisms:
- devtmpfs — kernel auto-creates basic
/dev/null,/dev/sda1etc. at boot - udev — userspace daemon receives uevents via netlink, applies rules, creates additional nodes
Fuchsia uses devcoordinator (now driver-index + device-finder):
- Drivers register devices with the driver manager
- devcoordinator exposes them via
devfs(listable, browsable) - Component Manager routes specific devices to components via capability declarations
Red Bear OS should follow the aggregator pattern: userspace daemons that discover, enumerate, and expose device categories through listable scheme namespaces.
2.3 Bootstrap Ordering (Plan 9, Fuchsia)
Plan 9 bootstraps namespace incrementally:
- Kernel boots with
#device drivers (kernel-resident, like Red Bear'sGlobalSchemes) boot(8)script binds drivers into the namespaceinit(8)builds the per-process namespace from/lib/namespace
Fuchsia bootstraps similarly:
- Zircon boots, creates root job + resource handles
- component_manager starts, receives boot info (device handles from ZBI)
- driver_index enumerates drivers, binds them to devices
- devfs provides the listable namespace
Red Bear OS boot sequence (current):
bootstrap → initnsmgr (initial schemes: 10 kernel globals + "proc" + "initfs")
→ init starts service targets
→ 10_lived.service (priority 10): registers "disk.live"
→ 40_drivers.target: pcid, graphics, etc.
→ 45_diskd.service (NEW): scans disk.* schemes, registers "diskd"
→ 50_rootfs.service: redoxfs uses diskd to find root device
2.4 Separation of Discovery and Access (Genode, seL4)
Genode separates:
- Platform session — device discovery (what hardware exists)
- I/O session — device access (read/write/mmio)
seL4 separates:
- Device Untyped caps — raw hardware access
- Platform description — structured description of what devices exist
In Red Bear OS terms: diskd provides discovery (listing), but actual block I/O goes through
the original disk.live/disk.sata0 schemes directly. diskd returns OpenResult::OtherScheme
so the kernel hands the caller a raw fd to the underlying scheme — zero overhead.
3. Current Architecture
3.1 Kernel Global Schemes (10)
Registered by bootstrap in exec.rs → initnsmgr::run():
| Scheme | GlobalSchemes Variant | Kernel Source |
|---|---|---|
| debug | Debug | scheme/debug.rs |
| event | Event | scheme/event.rs |
| memory | Memory | scheme/memory.rs |
| pipe | Pipe | scheme/pipe.rs |
| serio | Serio | scheme/serio.rs |
| irq | Irq | scheme/irq.rs |
| time | Time | scheme/time.rs |
| sys | Sys | scheme/sys/mod.rs |
| proc | Proc | scheme/proc/mod.rs |
| acpi | Acpi | scheme/acpi.rs |
| dtb | Dtb | scheme/dtb.rs |
These are registered in the KernelSchemes enum (kernel/src/scheme/mod.rs:438) and
exposed to initnsmgr during bootstrap.
3.2 initnsmgr Namespace Manager
Located at local/sources/base/bootstrap/src/initnsmgr.rs.
Key structures:
struct Namespace {
schemes: HashMap<String, Arc<FdGuard>>, // name → fd
}
open("")→Handle::List(directory listing handle)getdents(Handle::List)→ iteratesschemesHashMap, returnsDirEntryfor each name- Daemons register via
NsDup::IssueRegister+ sendfd mechanism - Bootstrap passes initial set: kernel globals + "proc" + "initfs"
3.3 Userspace Scheme Registration
Daemons register via:
Socket::create()→ creates scheme socketNsDup::IssueRegister→ tells initnsmgr the scheme namesendfd→ sends the scheme socket fd to initnsmgr- initnsmgr stores in
schemes: HashMap<String, Arc<FdGuard>>
3.4 Current Userspace Schemes (at boot)
| Scheme | Daemon | Priority | Source |
|---|---|---|---|
| initfs | bootstrap | 0 | bootstrap exec.rs |
| proc | kernel | 0 | GlobalSchemes |
| disk.live | lived | 10 | init.initfs.d/10_lived.service |
| disk.sata0 | ahcid | 40 | pcid-spawner |
| disk.virtio0 | virtio-blkd | 40 | pcid-spawner |
| display | vesad | 20 | init.initfs.d/20_vesad.service |
| drm | redox-drm | 30 | init.initfs.d/30_graphics.service |
| net | e1000d / virtio-netd | 40 | pcid-spawner |
| orbital | orbital | rootfs | (legacy, not used in redbear-full) |
3.5 The Root Cause Chain
redoxfs mount (priority 50)
→ fs::read_dir("/scheme") → initnsmgr getdents
→ iterates schemes HashMap → finds "disk.live" (registered at priority 10)
→ is_scheme_category("disk") → true
→ Fd::open("/scheme/disk.live") → reads text listing
→ finds block device → opens /scheme/disk.live/0 → reads UUID
→ UUID matches → mounts as rootfs
The bug: redoxfs retries 20×200ms = 4 seconds. If disk discovery takes longer than
4 seconds (e.g., AHCI probe on real hardware), rootfs mount fails → read-only fallback.
The fix: diskd aggregator + longer timeout + event-driven notification.
4. Solution Architecture
4.1 Component Overview
┌─────────────────────────────────────────────────────────┐
│ /scheme/ namespace │
│ (initnsmgr) │
│ │
│ Kernel globals: │
│ debug, event, memory, pipe, serio, irq, │
│ time, sys, proc, acpi, dtb │
│ │
│ Boot schemes (initfs): │
│ initfs, disk.live, display │
│ │
│ Aggregators: │
│ diskd ← /scheme/diskd lists ALL block devices │
│ │
│ Hardware daemons (post-drivers.target): │
│ disk.sata0..7 (ahcid) │
│ disk.virtio0..7 (virtio-blkd) │
│ disk.nvme0..7 (nvmed) │
│ disk.usb0..7 (usbscsid) │
│ disk.ide0..3 (ideid) │
│ net (e1000d, virtio-netd, ixgbed, rt8169d) │
│ drm (redox-drm) │
│ │
│ System daemons (post-rootfs): │
│ audio (audiod) │
│ firmware (firmware-loader) │
│ input (evdevd) │
│ udev (udev-shim) │
│ ... │
└─────────────────────────────────────────────────────────┘
4.2 diskd — Disk Aggregator Daemon (IMPLEMENTED)
Location: local/recipes/system/diskd/
Scheme name: diskd
Binary: /usr/bin/diskd
Status: Code complete, cargo check/clippy/fmt clean
How it works:
- At boot (priority 45), diskd starts
- Probes
/scheme/disk.live,/scheme/disk.sata0..7,/scheme/disk.virtio0..7, etc. - For each found scheme, reads its text listing to discover devices and partitions
- Registers scheme
diskdwith initnsmgr getdentsondiskd:returns realDirEntrywithDirentKind::BlockDevopen("0")oropen("0p1")opens the underlying scheme and returnsOtherScheme(zero-copy — caller talks directly to the block device)
Why this solves the root cause:
redoxfscurrently must enumerate ALL/scheme/disk.*individually — 50+Fd::opencalls- With
diskd,redoxfsdoes ONEread_dir("/scheme/diskd")to get all block devices - diskd already did the probing and enumeration
- Even if AHCI hasn't registered yet, diskd's retry logic handles late registration
redoxfstimeout only needs to wait fordiskdto be ready, not all individual schemes
4.3 Changes Required to Existing Components
4.3.1 redoxfs — Use diskd for disk discovery
File: local/sources/redoxfs/src/bin/mount.rs (function filesystem_by_uuid)
Current behavior:
// Line 224: fs::read_dir("/scheme") → filter is_scheme_category("disk")
// For each disk.* scheme: open, read listing, find block devices, check UUID
// Retry 20×200ms = 4 seconds total
New behavior (two-path approach):
fn filesystem_by_uuid(uuid: &[u8; 16]) -> Option<File> {
// Path A: Try diskd aggregator first (fast, single enumeration)
if let Some(f) = try_diskd_uuid(uuid) {
return Some(f);
}
// Path B: Fall back to legacy per-scheme enumeration
// (for backwards compat and environments without diskd)
try_legacy_uuid_search(uuid)
}
fn try_diskd_uuid(uuid: &[u8; 16]) -> Option<File> {
// Wait for diskd scheme to appear
for _ in 0..50 { // 50 × 200ms = 10 seconds
if let Ok(dir) = fs::read_dir("/scheme/diskd") {
for entry in dir {
let entry = entry.ok()?;
let name = entry.file_name().to_string_lossy().into_owned();
// Open the block device via diskd (which proxies to underlying scheme)
let path = format!("/scheme/diskd/{name}");
if let Ok(mut f) = File::open(&path) {
if check_uuid(&mut f, uuid) {
return Some(f);
}
}
}
}
thread::sleep(Duration::from_millis(200));
}
None
}
4.3.2 init.initfs.d — Add diskd service
New file: local/sources/base/init.initfs.d/45_diskd.service
[[service]]
name = "diskd"
command = "/usr/bin/diskd"
priority = 45
requires = ["lived"]
This ensures diskd starts after lived (which provides disk.live at priority 10) and before rootfs mount (priority 50).
4.3.3 config/redbear-mini.toml — Add diskd package
Add diskd to the [packages] section so it's included in the image.
4.4 /scheme/ Namespace Completeness Matrix
After all changes, /scheme/ will expose:
| Category | Scheme Name | Provider | getdents | Notes |
|---|---|---|---|---|
| Kernel globals | ||||
| Debug | debug |
kernel GlobalSchemes | ✅ real DirEntry | kernel/src/scheme/debug.rs |
| Event | event |
kernel GlobalSchemes | ✅ real DirEntry | kernel/src/scheme/event.rs |
| Memory | memory |
kernel GlobalSchemes | EOPNOTSUPP | No sub-entries expected |
| Pipe | pipe |
kernel GlobalSchemes | EOPNOTSUPP | Anonymous, no listing |
| Serio | serio |
kernel GlobalSchemes | ✅ real DirEntry | kernel/src/scheme/serio.rs |
| IRQ | irq |
kernel GlobalSchemes | ✅ real DirEntry | cpu-XX entries |
| Time | time |
kernel GlobalSchemes | ✅ real DirEntry | CLOCK_* entries |
| Sys | sys |
kernel GlobalSchemes | ✅ real DirEntry | scheme:/scp/ sub-entries |
| Proc | proc |
kernel GlobalSchemes | ✅ real DirEntry | pid entries |
| ACPI | acpi |
kernel GlobalSchemes | ✅ real DirEntry | rxsdt, kstop |
| DTB | dtb |
kernel GlobalSchemes | EOPNOTSUPP | Single blob |
| Bootstrap | ||||
| InitFS | initfs |
bootstrap | ✅ real DirEntry | initramfs contents |
| Storage | ||||
| Live disk | disk.live |
lived | ✅ text listing | virtio/ahci backend |
| SATA disk | disk.sata0..7 |
ahcid | ✅ text listing | per-disk scheme |
| VirtIO disk | disk.virtio0..7 |
virtio-blkd | ✅ text listing | per-disk scheme |
| NVMe disk | disk.nvme0..7 |
nvmed | ✅ text listing | per-disk scheme |
| USB disk | disk.usb0..7 |
usbscsid | ✅ text listing | per-disk scheme |
| IDE disk | disk.ide0..3 |
ideid | ✅ text listing | per-disk scheme |
| Aggregators | ||||
| Disk aggregator | diskd |
diskd | ✅ real DirEntry BlockDev | THIS PLAN |
| Display | ||||
| Framebuffer | display |
vesad | EOPNOTSUPP | Legacy text listing |
| DRM/KMS | drm |
redox-drm | ✅ real DirEntry | card0, card0-*, connectors |
| Network | ||||
| Ethernet | net |
e1000d/virtio-netd | ✅ real DirEntry | interface entries |
| Input | ||||
| Input events | input |
evdevd | ✅ real DirEntry | event0, event1, ... |
| Audio | ||||
| Audio | audio |
audiod | ✅ text listing | Audio streams |
| System | ||||
| Firmware | firmware |
firmware-loader | ✅ real DirEntry | GPU/device blobs |
| Udev | udev |
udev-shim | ✅ real DirEntry | Linux-compatible device nodes |
4.5 initnsmgr getdents — Already Correct
The initnsmgr getdents implementation at line 402-439 of initnsmgr.rs iterates
schemes: HashMap<String, Arc<FdGuard>> and emits a DirEntry for each registered scheme.
This is already correct — it will list any scheme that has been registered, including diskd.
The /scheme/ listing issue was NOT a getdents bug — it was a timing issue:
- Daemons hadn't registered yet when
fs::read_dir("/scheme")was called - The fix is proper boot ordering (diskd at priority 45) and the diskd aggregator
5. Future Enhancements (Beyond Current Scope)
5.1 Event-Driven Discovery (uevent Equivalent)
Currently diskd probes statically at startup. For hotplug (USB drives, PCIe hot-add):
- pcid sends a
uevent-like notification when a new PCI device appears - diskd listens for these notifications and re-scans
- Alternative: inotify-like watch on
/scheme/(would need kernel support)
This mirrors Linux's uevent netlink broadcast → udev listener pattern.
5.2 devfs-Style Aggregation
A future devfsd could provide Linux-compatible /dev paths:
/scheme/devfs/sda → /scheme/diskd/0
/scheme/devfs/sda1 → /scheme/diskd/0p1
/scheme/devfs/null → /scheme/debug (write sink)
/scheme/devfs/zero → /scheme/memory (zero-filled read)
/scheme/devfs/random → /scheme/entropy
/scheme/devfs/tty0 → /scheme/display.0
/scheme/devfs/input/event0 → /scheme/input/event0
This would be the Fuchsia devcoordinator equivalent — a unified, Linux-compatible
device namespace. The udev-shim already provides parts of this.
5.3 Per-Process Namespace (Plan 9 Style)
Plan 9's bind and mount allow per-process namespace customization. Red Bear OS's
setrens syscall provides a basic version (switch namespace fd). Future enhancement:
- Per-container namespaces (for
containand future container runtime) - Namespace inheritance rules (like Fuchsia's
.cmlcapability routing) chroot-like namespace restriction for sandboxed applications
5.4 Capability-Based Access (seL4 Style)
seL4 uses CSpace (capability spaces) for device access. Each process has a CSpace that contains only the capabilities it should have access to. Red Bear OS could evolve toward this model:
initnsmgrtracks which schemes each process can accessopen("/scheme/net")checks the caller's capability setsetrensevolves from "switch namespace" to "restrict to capability subset"
This would require kernel changes (per-process scheme allowlists), which is beyond current scope but worth keeping in mind for security hardening.
6. Implementation Plan
Phase 1 — Immediate Fix (This Session)
| Step | Action | Files | Status |
|---|---|---|---|
| 1 | diskd daemon implementation | local/recipes/system/diskd/ |
✅ Done |
| 2 | Add diskd init service | local/sources/base/init.initfs.d/45_diskd.service |
Pending |
| 3 | Add diskd to config | config/redbear-mini.toml |
Pending |
| 4 | Modify redoxfs to use diskd | local/sources/redoxfs/src/bin/mount.rs |
Pending |
| 5 | Commit uncommitted changes | driver-manager, config | Pending |
| 6 | Remove pcid debug logging | local/sources/base/drivers/pcid/src/cfg_access/fallback.rs |
Pending |
| 7 | Make C++ header fix durable | mk/prefix.mk |
Pending |
| 8 | Build and test ISO | ./local/scripts/build-redbear.sh redbear-mini |
Pending |
| 9 | Boot test in QEMU | scripts/run_mini1.sh |
Pending |
Phase 2 — Hotplug Support (Future)
| Step | Action | Dependencies |
|---|---|---|
| 1 | pcid uevent notification | pcid-spawner enhancement |
| 2 | diskd dynamic re-scan | uevent listener |
| 3 | devfsd Linux-compatible /dev | udev-shim + diskd integration |
Phase 3 — Namespace Security (Future)
| Step | Action | Dependencies |
|---|---|---|
| 1 | Per-process scheme allowlist | kernel scheme access control |
| 2 | Container namespace isolation | contain enhancement |
| 3 | Capability routing | initnsmgr capability model |
7. Cross-Reference Summary
| System | Mechanism | Red Bear Equivalent | Status |
|---|---|---|---|
| Linux | kobject/uevent → udev → /dev | pcid → diskd → /scheme/diskd | Phase 1 |
| Fuchsia | devcoordinator → devfs | initnsmgr → diskd | Phase 1 |
| seL4 | CSpace capabilities | setrens (basic) | Phase 3 |
| Plan 9 | bind/mount per-process | setrens (basic) | Phase 3 |
| Genode | Platform session | redox-driver-sys | Existing |
| MINIX 3 | driver announce → devfs | daemon register → initnsmgr | Existing |
8. Risk Assessment
| Risk | Mitigation |
|---|---|
| diskd probe takes too long on real hardware | Increase retry count (50×200ms = 10s), add event-driven re-scan |
| diskd crashes and disk namespace disappears | init service auto-restart (restart = true in service file) |
| redoxfs legacy path broken by diskd changes | Two-path approach: try diskd first, fall back to legacy |
| Boot ordering regression (diskd starts before lived) | Explicit requires = ["lived"] in service file |
| diskd returns stale device list after hotplug | Phase 2: event-driven re-scan; Phase 1: manual re-trigger via signal |
9. Acceptance Criteria
ls /scheme/in shell shows all registered schemes (no hang, no empty)ls /scheme/diskd/shows all block devices discovered by diskdredoxfsmounts rootfs read-write via diskd path/tmpis writable by non-root users- Boot completes to login prompt with zero warnings
- QEMU boot test passes:
scripts/run_mini1.shreaches login prompt ./local/scripts/build-redbear.sh redbear-miniproduces working ISO