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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion bootloader/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions console/system.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ start = ["logd", "console"]
# reads the kernel's records with `logread`, which is `Rights::LOG |
# Rights::WAIT` on a `SysCap` duplicate, and serves `log` to the console.
[programs.logd]
service = true
syscap = ["logread"]
serves = ["log"]

Expand Down
1 change: 1 addition & 0 deletions diag/system.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ start = ["logd", "toybox"]
# `Rights::LOG | Rights::WAIT` on a `SysCap` duplicate, and init hands it every
# program's output beside that.
[programs.logd]
service = true
syscap = ["logread"]

[programs.toybox]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
status: open
kind: tooling
opened: 2026-09-26
---

# A lane's tap socket path is past `SUN_LEN` on the dev host

`lan_mdns_answer` reds on the macOS dev host, wide and alone:

```
connect to QEMU's /private/var/folders/gr/mr4_fg4n34jb417sx1g5cgxc0000gp/T/toyos-tmp-89085-0/tests-0/lane-3/tap-out-0.sock: path must be shorter than SUN_LEN
```

That path is 104 bytes, and macOS's `sun_path` holds 104 including the NUL.
`tests/common/segment.rs`'s `Tap::in_lane` puts both sockets in
`lane::dir()`, which since `toyos-tmpdir` is
`$TMPDIR/toyos-tmp-<pid>-<n>/tests-<n>/lane-<i>/`, and the dev host's
`$TMPDIR` resolves to 57 bytes (`/private/var/folders/…/T/`) before any of
that. A five-digit pid is enough to cross the limit. Seen on
`wt/toyos-layout` after it merged `origin/main` at `e48604c0`; nothing on that
branch touches the lane or the tap.

## Exit condition

A tap socket's path fits `sun_path` on every host the suite runs on, and
`lan_mdns_answer` is green on the dev host.

This file was deleted.

This file was deleted.

33 changes: 33 additions & 0 deletions issues/filesystem/a-directory-on-data-survives-no-reboot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
status: open
kind: defect
opened: 2026-09-26
---

# A directory on DATA survives no reboot, and takes a child whose parent was never made

The DATA adapter answers every `create_dir` with `NotSupported`
(`kernel/src/bcachefs_adapter.rs`), so the VFS keeps the directory itself in
`created_dirs` (`kernel/src/vfs.rs`). Two things follow:

- **A child is taken whose parent was never made.** `create_dir("/home/toy/Apps")`
succeeds with no `/home/toy`, so `std::fs::create_dir_all` makes the leaf and
nothing above it. init makes every level in turn for this reason (`make_dir`
in `userland/init/src/main.rs`).
- **It is memory.** Nothing reaches the volume, so an empty directory is gone
at the next boot; init remakes the session home and each service's `/state`
every boot.

Seen building `layout_fresh_boot`: `read_dir /home/toy: entity not found` from
a boot whose init had just made `/home/toy/Apps`.

## Owner

The storage track, `issues/filesystem/storage-is-layers-and-a-role-is-a-filesystem.md`:
real bcachefs under DATA carries directories.

## What would close it

DATA stores directories, `create_dir` refuses a missing parent, and
`created_dirs` is gone or kept only for a mount that has no directories and
says so.
8 changes: 4 additions & 4 deletions issues/filesystem/a-user-is-a-home-tree-and-a-login-row.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ opened: 2026-09-05
A stage of `issues/filesystem/storage-is-layers-and-a-role-is-a-filesystem.md`;
where a program's own data goes depends on it.

A user is two things: `/home/<user>`, a directory on DATA holding Documents,
Downloads, `.config/<app>` and `.local/<app>`; and a row in the manifest from
A user is two things: `/home/<user>`, a directory on DATA laid out as
`issues/filesystem/where-everything-lives.md` rules; and a row in the manifest from
which init builds a login session's namespace, the way it builds every system
program's from `system.toml`. Nothing else names a user: no numeric id the
kernel checks, no password file, no ambient "current user" a process can ask
for. A session holds its home tree because init moved that directory's handle
into it, and a program launched inside the session holds what the session's
launcher row grants it. Until this lands there is one implicit user and a
package writes under `/apps/<name>/` only.
launcher row grants it. Until this lands there is one user, `toy`, whose home
init makes at boot.

Stage 3 of `issues/isolation/every-program-sees-only-the-files-it-was-given.md`, and blocked on its first two stages: a user is isolated only
once a session's view is all it can name. The mount protocol and real bcachefs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ down, where `set_current_dir(&home)` is at least a stated policy.

## Reproduction

Spawn `/system/bin/shell` with `Command::current_dir("/home/root")` and the
Spawn `/system/bin/shell` with `Command::current_dir("/home/toy")` and the
arguments `-c`, `pwd`. It answers `/`.

Read from the source rather than measured: `pkg_install_gbae`'s
Expand Down
25 changes: 8 additions & 17 deletions issues/filesystem/storage-is-layers-and-a-role-is-a-filesystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ volume, handed to whichever server recognises its superblock.
|---|---|---|---|
| ESP | FAT32, firmware's rule | `/boot` | kernel only |
| ROOT | bcachefs image the build writes | `/system` | no; versioned per release |
| DATA | bcachefs, formatted on first boot | `/apps`, `/home` | yes |
| DATA | bcachefs, formatted on first boot | `/apps`, `/config`, `/home`, `/state` | yes |
| LOG | FAT32 while a Mac has to read the dev stick | `/log` | yes |

`/tmp` has no backing. The dev loop keeps the ESP and ROOT on the stick and
Expand Down Expand Up @@ -90,22 +90,13 @@ NTFS by the same shape if wanted.

## Paths

No drive letters, no `/usr`, `/var`, `/opt`, `/dev`, `/proc` or `/sys`:
devices and processes are capabilities and syscalls here, not files. Each
process sees the directories its parent gave it
(`issues/isolation/every-program-sees-only-the-files-it-was-given.md`).

- `/boot` — bootloader, kernel, kernel arguments.
- `/system` — the OS image, read-only, versioned: today's `bin`, `lib`, `share`
and the manifest.
- `/apps/<name>` — each installed program in its own directory with its own
binaries, data and manifest row; doom moves here with its WAD.
- `/home/<user>` — Documents, Downloads, `.config/<app>` for settings,
`.local/<app>` for saves and caches.
- `/log`, `/tmp` — as today.
- `/media/<label>` — foreign and unassigned volumes; a Windows disk is
`/media/windows`: one more directory capability, served by the server that
recognises the volume.
The tree, its names and how a program finds them are
`issues/filesystem/where-everything-lives.md`. What this track owes it is the
mount structure: a mount point is exactly one top-level name
(`kernel/src/vfs.rs`, `ROOT_ENTRIES` and the array indexed by it), so
`/media/<label>` for a foreign volume — a Windows disk is `/media/windows` — is
a nested mount the structure cannot represent. The mount protocol owes that,
and `/media` is an empty directory until then.

Users are a track of their own,
`issues/filesystem/a-user-is-a-home-tree-and-a-login-row.md`: a `/home/<user>`
Expand Down
122 changes: 122 additions & 0 deletions issues/filesystem/where-everything-lives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
status: open
kind: track
opened: 2026-09-26
---

# Where everything lives, and how a program finds it

The owner ruled this layout on 2026-09-26. This file is where it is recorded:
`issues/filesystem/storage-is-layers-and-a-role-is-a-filesystem.md` says what
backs each name, and
`issues/filesystem/a-user-is-a-home-tree-and-a-login-row.md` and
`issues/isolation/every-program-sees-only-the-files-it-was-given.md` say who
may reach it. Every line below is the owner's ruling.

## The tree

```
/system read-only, signed: bin, lib, etc, and
share/{fonts, icons, themes, sounds, backgrounds, licenses, doc}
/config machine settings the owner changes; a file here overrides /system/etc
/state/<program> a system service's private persistent data
/apps/<name> installed packages; no layout is required inside one
/home/<user> Desktop Documents Downloads Music Pictures Videos Fonts Apps/<name>/
/boot bootloader, kernel, kernel arguments; the updater's alone
/log /media logs; foreign volumes, one /media/<label> each
/tmp private per program
```

`/system` is signed because the image is:
`issues/boot-media/the-machine-updates-itself-without-ubuntu.md` signs it, and
until that lands nothing verifies it.

## The rules

- **No drive letters, no `/usr`, `/var`, `/opt`, `/dev`, `/proc` or `/sys`**:
devices and processes are capabilities and syscalls here, not files.
- **No dotfile and no hidden folder anywhere in the layout.** A third-party
program that hard-codes `~/.something` may still create one, but only inside
its own app folder: the owner accepted that as the honest limit.
- **English names, never translated.**
- **A path means the same file in every view.** `/tmp` is the one exception:
it is private per program. A view hides names and never renames one.
- **A program learns a location from an environment variable init sets**
(`HOME` and those like it). A location grants nothing, because the view
decides what a program can reach. On ToyOS `std::env::home_dir()` is `$HOME`
or `None`, with no fallback, and `temp_dir()` is `/tmp`. A program init did
not start carries the `HOME` its caller named or init answered for it, never
its parent's own.
- **`/home/<user>/Apps/<name>` is one app's private data for that user**, and
it is that app's `HOME`. Inside it ToyOS answers config, data, cache and
state as the visible `Config`, `Data`, `Cache` and `State`.
- **Fonts**: the system's are in `/system/share/fonts`, a user's in
`/home/<user>/Fonts`. sans-serif and system-ui are Open Sans, monospace is
JetBrains Mono, and serif is empty until a licence-clean one is chosen.
- **The time zone is machine-wide in `/config`.** The keyboard layout and the
language are machine defaults in `/config`, and per-user overrides come with
the users track.
- **The dev image's user is `toy`.** There is no `root` user and no
`/home/root`.
- An app finds its own files next to its binary (`current_exe()`).

## Stages

1. **Everything that needs no isolation.** `/config` and `/state` are DATA
names; init makes `/home/toy`, its eight folders, and each service's
`/state/<name>`, and it sets `HOME` from each row (`service = true` in
`system.toml`, which a row that serves a port must say); a program no row
names carries the `HOME` init answers for it, never its spawner's own;
the kernel makes no home, and lists a directory it carries; the keyboard
layout is `/config/keyboard-layout`; sshd keeps its identity and key list
in `/state/sshd`; the shell's history is in `Apps/shell/State`; std's
`home_dir` reads `$HOME`. **Exit**: `layout_fresh_boot` is green.
2. **An app's `HOME` is its folder.** init launches an app from `/apps` (and
each desktop app in the image) with `HOME=/home/<user>/Apps/<name>`, makes
the folder and its `Config Data Cache State`, and puts nothing else of the
home in that app's view. It needs
`issues/isolation/every-program-sees-only-the-files-it-was-given.md` stage 2.
**Exit**: a launched app's `home_dir()` is its folder, it cannot name
another app's folder, and the shell's history is still
`/home/<user>/Apps/shell/State/history` (`OWN_FOLDER` in `userland/shell`).
3. **Users.** The users track creates `/home/<user>` and its folders from a
login row, and `toy` stops being a constant in `toyos-manifest`.
**Exit**: init names no user.
4. **The time zone.** One file in `/config` names the machine's zone, one
program writes it, and local time is read through it rather than recovered
by subtracting `SYS_CLOCK_REALTIME` from `SYS_CLOCK_EPOCH`. **Exit**: a
guest test sets the zone and reads its local time back, and nothing
recovers the zone by subtraction.
5. **The language.** One file in `/config` names the machine's default
language, one program writes it, and init sets it on every program it
starts. **Exit**: a guest test sets the language and a program init starts
reads it back.
6. **Fonts ship as files.** JetBrains Mono and Open Sans ship as TTFs under
`/system/share/fonts` from `assets/fonts/`, the console's raster stays its
own asset, and each OFL text ships once, under `/system/share/licenses`.
**Exit**: an image carries both families there and their licence texts
under `/system/share/licenses`, and none under `/system/share/fonts`.
7. **The upstream arms**, none opened until the owner opens it:
- fontdb and fontique scan `/system/share/fonts` and `$HOME/Fonts` from the
start, and fontique maps the families above. **Exit**: both prepared
branches carry both folders and the family table.
- dirs-sys answers `home_dir` from std, and the six user folders and the
four `Config Data Cache State` directories under `$HOME` by their names
here. **Exit**: `dirs` and `directories` build for ToyOS and answer this
layout.
- home answers from std. **Exit**: `home` builds for ToyOS.
- tempfile takes the unix create-then-unlink path once std's
`create_new` and unlink-while-open are measured on ToyOS. **Exit**:
`tempfile` makes files on ToyOS.
- sys-locale reads the language init sets from `/config`. **Exit**:
`sys-locale` answers on ToyOS.
- rustls-native-certs reads a CA bundle under `/system/etc`, once something
needs native roots. **Exit**: the first native-roots consumer runs.

## Not moved

`/log/lease.txt` stays on `/log`. It is not netd's lease: it is the
`--exit-with-lease` bench report the metal loop reads off the stick's FAT log
volume, as it reads `/log/metal-*.bin`, and the DATA volume is not readable
there. It goes with
`issues/diagnostics/the-lanleasecase-boot-is-a-third-t14-flash-for-one-exit-code.md`.
4 changes: 2 additions & 2 deletions issues/isolation/sshd-authorized-keys-unprotected.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ opened: 2026-08-06

# Sshd's keys are as protected as any other file, which is not at all

`/home/root/.ssh/host_ed25519` is the machine's SSH private key and
`/home/root/.ssh/authorized_keys` is the list of who may log in. There is no
`/state/sshd/host_ed25519` is the machine's SSH private key and
`/state/sshd/authorized_keys` is the list of who may log in. There is no
user model and no file permissions, so **any process on the machine can read
the first and rewrite the second** — the second being the one that matters:
appending a line to it is a remote login, and nothing stops a process doing it.
Expand Down
2 changes: 1 addition & 1 deletion kernel/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading