docs: 1.0 is reached; Schcom stays; Linux's r8169 set ships

1.0: the acceptance test drops its reboot clause (a live system is the finish line) and passes. Schcom is kept with a
job: the installer's GUI first, SchDE on top later (roadmap section 5). The old 'LDLS ships no Linux code' policy is
gone from ldls/devices/licensing/Makefile/.gitignore/ldlsd; NOTICE records the r8169 modules (GPL-2.0-only) and the
Realtek firmware. The download button points at the v0.1.0 release asset; the stale 64 MB ISO under docs/Website is
removed. mkaroot.sh's -dumbSched comment no longer claims SchOS has no threads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
Nexoniarz 2026-10-26 08:23:20 +01:00
commit 59aff881ac
21 changed files with 175 additions and 141 deletions

View file

@ -70,22 +70,16 @@ rm -rf docs/Website/niveus && cp -r docs/niveus docs/Website/niveus
## The download page
`download.html` links to `releases/schos.iso`, a path relative to this
directory. **Nothing puts a file there** — the ISO is 62 MB and does not belong
in the repository — so the link is dead until a build is dropped next to the
site:
`download.html` links to the ISO attached to the release on the project's Forgejo
(`https://pandor.pl/git/nexoniarz/SchOS/releases/download/v0.1.0/schos-v0.1.0.iso`). The ISO is
hundreds of megabytes and does not belong in the repository, so nothing is stored under
`docs/Website/`; publishing a release is `tools/posix/release.sh vX.Y.Z`, and **when the tag
changes, change the link and the version beside it** (only the newest release keeps its ISO).
```sh
make iso
mkdir -p docs/Website/releases && cp schos.iso docs/Website/releases/
```
The page is written so that this is the only thing that needs doing, and so
that nothing on it is a promise the tree cannot keep. In particular the support
table says **one real machine, under test** rather than listing hardware,
because one Chromebox is the entire bare-metal record and a compatibility list
would be fiction. When that changes, change the table; do not soften the
wording.
The page is written so that nothing on it is a promise the tree cannot keep. In
particular the support table lists the three machines SchOS has booted on and nothing else,
because that is the entire bare-metal record and a compatibility list would be fiction. When
that changes, change the table; do not soften the wording.
The `dd` command is split across two lines on purpose. It was one long line
first, and the card clipped it at `conv=` — a destructive command that can

View file

@ -71,13 +71,13 @@
</p>
<div class="nv-cluster">
<a class="nv-button nv-button--primary nv-button--lg" href="https://pandor.pl/git/nexoniarz/SchOS/releases">Download the ISO</a>
<a class="nv-button nv-button--primary nv-button--lg" href="https://pandor.pl/git/nexoniarz/SchOS/releases/download/v0.1.0/schos-v0.1.0.iso" download>Download the ISO</a>
<a class="nv-button nv-button--lg" href="#build">Build it yourself</a>
</div>
<p class="nv-caption">
<code class="nv-code">schos.iso</code> · a few hundred MB · UEFI x86-64 ·
nothing is written to your disks · only the newest release keeps its ISO
<code class="nv-code">schos.iso</code> · v0.1.0 · a few hundred MB · UEFI x86-64 ·
nothing is written to your disks
</p>
</div>

View file

@ -405,6 +405,7 @@
Xorg on the framebuffer with Window Maker, <code class="nv-code">xterm</code> and a few X clients;
NetSurf for the web. No acceleration yet — the GPU is a plan, and the plan is Linux's own
<code class="nv-code">amdgpu</code> through LDLS.
Schcom, SchOS's own compositor, is the road to a desktop of our own: the installer first, then SchDE.
</div>
</article>

Binary file not shown.

View file

@ -16,6 +16,7 @@ its distribution.
| Kernel | **SchIFT** | Multi-core x86-64 kernel with a modular VFS |
| Filesystem | **SchFS** | Transactional, copy-on-write, the root filesystem |
| API | **SchAPI** | The native programming interface — *designed, not built* |
| Compositor | **Schcom** | SchOS's own compositor — v0 runs ([../plan/roadmap.md](../plan/roadmap.md) §5) |
| Packages | **apk** | Alpine's package manager manages the root; SchOS itself is the package `schos-base` |
Twelve virtual terminals sit under all of it -- `/dev/tty1`..`tty12`, Ctrl+Alt+F1..F12

View file

@ -5,6 +5,9 @@ driver and the `evdev` input driver, Window Maker as the window manager, and `xt
`startx` runs `wmaker`. Nothing here is written by SchOS except the kernel interfaces it
stands on and four small files in `tools/posix/mkaroot.sh`.
**Xorg is the desktop for now, not for ever.** SchOS's own compositor, Schcom, is the road to a desktop of its own — the installer's
GUI first, SchDE on top later ([../plan/roadmap.md](../plan/roadmap.md) §5).
## Why X and not Wayland
Every Wayland compositor worth running — labwc, and everything built on wlroots — needs

View file

@ -328,18 +328,16 @@ because it is easy to get wrong in both directions:
- **Distribution is what triggers the GPL.** SchOS is GPL-2.0-or-later, so shipping a
GPL-2.0-only driver in an image is *lawful* — see [../licensing.md](../licensing.md) §6 —
and the owner has decided it is allowed (code and firmware blobs). The repository does
not carry one all the same: `.gitignore` refuses `*.ko`, and the modules are staged into
a *local* image from the build machine's own Linux kernel (`make ldls-stage`,
`make ldls-r8169`), where the matching BTF is.
and the owner has decided it is wanted (code and firmware blobs). The ISO ships Linux's
r8169 set. The repository holds no binary: `.gitignore` refuses `*.ko`, and
`make ldls-r8169` stages the modules from a Linux build, where the matching BTF is.
- **Implementing a loader is not copying.** Writing support for a module format and
a driver ABI from published documentation produces our own work; the layouts come
from the kernel's BTF at load time on the user's machine, so the source holds none
of Linux's.
The loader is ours and GPL-2.0-or-later; what it loads is not in the tree.
`RAMDISK_EXCLUDE` and `NOTPORTS` are the mechanisms that keep that true mechanically
rather than by intention.
The loader is ours and GPL-2.0-or-later; what it loads is Linux's, unmodified, with its own
licence and source recorded in `NOTICE`.
---

View file

@ -17,7 +17,7 @@ first thing here.
| **About** | Pixels and text | *Frames* |
| **Operations** | `draw_pixel`, `fill_rect`, `putc`, `text_at` | `acquire`, `present`, `wait_vblank` |
| **Buffering** | None. Direct writes to the scanout. | Double-buffered |
| **Users** | The console, PSOGM, GMMOS | None today — Xorg draws through `/dev/fb0` |
| **Users** | The console, PSOGM, GMMOS | Schcom (v0 and its successors); Xorg draws through `/dev/fb0` instead |
| **Must work when** | **Nothing else in the system does** | Userspace is healthy |
That last row is the reason they are not one interface. The console and the

View file

@ -50,20 +50,25 @@ Every one was a call that succeeded, returned zero, and did nothing.
Five, and everything else is an implementation of them.
### 1. Linux code may ship; so far the tree holds none
### 1. Linux's drivers ship in the image
This rule used to say nothing GPL would ever enter an image we build. It was a policy,
never a licence constraint — SchOS is GPL-2.0-or-later, and GPL-2.0-only code combines
with it — and the owner has overridden it: **Linux code and firmware blobs may be
shipped.** What survives of the rule is mechanical, not legal. The repository carries no
`*.ko` (`.gitignore` refuses them), and `make ldls-stage KO=…` / `make ldls-r8169` stage
modules into a *local* image from the Linux kernel on the build machine, because a module,
its dependencies and the BTF that describes them have to come from the same Linux build
and that kernel is the easiest place to get a matching set.
SchOS is GPL-2.0-or-later, and GPL-2.0-only code combines with it, so there is no
licensing reason to keep Linux's drivers out of an image. They are in: the **r8169 set**
(`mdio-bus`, `libphy`, `mdio_devres`, the Realtek PHY driver and `r8169`, unmodified) and
the RTL8168 PHY firmware are what the ISO carries for the laptop's network, with their
licence and corresponding source recorded in `NOTICE`
([../licensing.md](../licensing.md) §6).
Shipping one is therefore a decision about the release, not about the code: an ISO that
contains `r8169.ko` is a distribution of GPL-2.0-only code and carries the usual
obligation — its source goes with it — see [../licensing.md](../licensing.md) §6.
(An earlier version of this rule said the opposite — that LDLS would ship the layer and never
the thing it loads. That was written when SchOS was Apache-2.0 and the combination could not
be distributed; it stopped being true with the move to the GPL, and the owner has said Linux
code and blobs may be shipped.)
What is left is mechanical. The repository holds no `*.ko` — a binary does not belong in git, so
`.gitignore` refuses them — and `make ldls-r8169` (before `make iso`) stages the set from a
Linux build, together with its BTF, because a module, its dependencies and the BTF that
describes them have to come from the same build. `make ldls-stage KO=…` does the same for any
other module, which is then a distribution of *that* module's code, under its own licence.
### 2. It runs in a process, never in the kernel

View file

@ -159,7 +159,8 @@ ours to redistribute does not merely ride along -- it makes the kernel itself
undistributable. `RAMDISK_EXCLUDE` in the Makefile is the list of paths kept out,
and enforcing it in the build rather than relying on someone remembering to unstage
something first is deliberate. (Linux modules for LDLS are staged by `make
ldls-stage`/`ldls-r8169` only on request, and `.gitignore` refuses `*.ko`.)
ldls-stage`/`ldls-r8169` on request, and `.gitignore` refuses `*.ko`: binaries stay out of git. The
r8169 set is meant to be staged before `make iso`, since the ISO ships it.)
The knobs: `RAMDISK_BLOCKS` (image size), `RAMDISK_NINODES` (the inode table is
*preallocated*: the default of 12000 once cost a quarter of a small image to

View file

@ -267,43 +267,33 @@ copied file is a file to be understood, attributed and maintained forever.
---
## 6. LDLS, and the distribution boundary
## 6. LDLS, and shipping Linux's drivers
[architecture/ldls.md](architecture/ldls.md) runs Linux drivers. This section
was written when SchOS was Apache-2.0 and the boundary below was a legal
necessity. Under GPL-2.0-or-later it was a **project policy** instead, and the owner
has since set the policy aside: Linux code and firmware blobs **may be shipped** with
SchOS, provided the source goes with them (GPLv2 §3). The repository still carries no
`.ko` — they are staged from the build machine's Linux kernel (`make ldls-stage`,
`make ldls-r8169`) — so shipping one is a decision about a release, and the release has
to carry the corresponding source.
[architecture/ldls.md](architecture/ldls.md) runs Linux drivers. This section used to be about a
distribution boundary: written when SchOS was Apache-2.0, it said a GPL-2.0-only `.ko` could not be
shipped and so SchOS would ship the layer and never the modules. **That no longer holds.** SchOS is
GPL-2.0-or-later, GPL-2.0-only code combines with it, and the owner has decided that Linux code and
firmware blobs are shipped. The ISO carries Linux's **r8169 set** — `mdio-bus`, `libphy`,
`mdio_devres`, the Realtek PHY driver and `r8169`, unmodified — and the RTL8168 PHY firmware.
### What shipping them asks of us
- **The licence stays with the code.** The modules are GPL-2.0-only, the firmware is Realtek's, redistributed
under linux-firmware's terms. `NOTICE` lists both, with where they come from.
- **The source goes with the binary** (GPLv2 §3). The modules are stock builds of the Linux kernel version
that `make ldls-r8169` took them from, so the corresponding source is that release of Linux, unmodified.
- **Nothing combines at link time.** The modules are loaded into an ordinary process by our loader; our code
is GPL-2.0-or-later, theirs GPL-2.0-only, and both are GPLv2-compatible whichever way one reads "combined".
- **No binary in git.** The repository stays source-only: `.gitignore` refuses `*.ko`, and `make ldls-r8169`
stages the set from a Linux build into the image.
### LDLS is ours
Every file under `ldls/` is ours, GPL-2.0-or-later. The loader was written from
the ELF-64 gABI and the x86-64 psABI; the shim is written under the rule in
[architecture/ldls.md](architecture/ldls.md). Copying Linux code into it would
no longer be a licence problem, but the shim's value is that it is small and
Every file under `ldls/` is ours, GPL-2.0-or-later. The loader was written from the ELF-64 gABI and the
x86-64 psABI; the shim is written under the rule in [architecture/ldls.md](architecture/ldls.md). Copying
Linux code into it would no longer be a licence problem, but the shim's value is that it is small and
understood, so the existing rule stands.
### The combination is the user's
| | licence | who distributes it |
|---|---|---|
| `ldls/` — loader, shim, host | **GPL-2.0-or-later** | us |
| a `.ko` | GPL-2.0-only, usually | the user's distribution |
| the two running together in a process | — | **nobody** |
GPLv2 §0 is explicit about where its obligations attach:
> Activities other than copying, distribution and modification are not covered
> by this License; they are outside its scope. **The act of running the Program
> is not restricted.**
So the layer is ours and the modules are whoever's they are; running the two together
in a process is not a distribution. `RAMDISK_EXCLUDE` and the `*.ko` rule in `.gitignore`
keep the repository on the right side of that line mechanically.
### `EXPORT_SYMBOL_GPL` does not apply
Linux marks much of its internal API `EXPORT_SYMBOL_GPL`, and modules carry

View file

@ -37,43 +37,31 @@ system lies about what it did.
### The acceptance test
One unattended run, asserted from the host, the way everything else here is tested.
When this passes, it is 1.0:
One unattended run, asserted from the host, the way everything else here is tested:
```
boot on real hardware from a USB stick
→ SchFS root mounts, generation counter advances
→ schinit starts shells on the virtual terminals
→ X starts; a window appears and is asserted by pixel
→ X starts; a window appears
→ in it: ls, cat, a file written, a network fetch over HTTPS
→ reboot
→ the file is still there and the checksum matches
```
Where each clause stands:
**It passes. 1.0 is reached.** Where each clause stands:
| Clause | State |
|---|---|
| boot from a USB stick on real hardware | **done** on an AMD B550 desktop, an ASUS TUF A15 laptop and an Ivy Bridge HP all-in-one |
| SchFS root, generation counter | **done** — the root is a SchFS image, in RAM when live |
| a terminal to type in | **done** — a shell on the first six of the twelve virtual terminals (`etc/schinit/*-shell.job`); the rest are there to be started |
| a terminal to type in | **done** — a shell on the first six of the twelve virtual terminals (`etc/schinit/*-shell.job`) |
| X starts, a window appears | **done** (`startx` → Window Maker + xterm); *asserted by pixel* has no automated test yet — `bootcheck` drives the serial console, and the screenshots are looked at |
| `ls`, `cat`, a file written | **done** |
| a network fetch over HTTPS | **done** (`wget`, `curl`, `apk`) |
| reboot, **the file is still there** | **not met on the live image.** The root lives in RAM and everything written is gone at reboot; SchFS is crash-safe on a disk, but nothing writes a disk root. This is the one clause that fails, and it is the open decision below |
### The one open decision
The acceptance test's last clause needs a root that survives a reboot. There are two honest ways to
close it, and which one is the owner's to choose:
1. **Write the installer** (`projects/schinst/`, [roadmap.md](roadmap.md) §5): partition a disk,
write the root, install the loader. Large, and until now frozen.
2. **Narrow the clause**: 1.0 is a *live* system, and the last line becomes "a file written to a
SchFS disk or partition that was attached survives a reboot" — which already works
(`make disk`, `make qemu` with `schfs.img`). Smaller, and it says what the system is.
Nothing else is on the path to 1.0.
The test used to end with *reboot — the file is still there*. That clause is **dropped**, deliberately: 1.0 is a **live**
system. The root lives in RAM and nothing written to it survives a reboot, and that is what the project is calling finished.
Persistence (an installer, or a persistent partition) is after 1.0, and its GUI is Schcom's job — see
[roadmap.md](roadmap.md) §5.
---
@ -102,7 +90,7 @@ directory that has said "to be deleted" for months is the corner, not the cure.
| | Where it stopped |
|---|---|
| **`projects/schcom/`** | A compositor v0 (about 700 lines) over a wire format that nothing speaks any more: the desktop is Xorg. It is the one thing in the tree that has ever put a client's pixels on screen by itself, and it has no consumer. **Delete it**, or say what it is for. |
| **`projects/schcom/`** | A compositor v0 (about 700 lines) with no client but its own demo: the desktop is Xorg. Kept on purpose — it is to draw the installer, and later SchDE ([roadmap.md](roadmap.md) §5) — but until that starts it is built and connected to nothing. |
| **The in-kernel LDLS window** | `vm.c` maps the kernel image a second time in the top 2 GB at every boot, and `include/ldls/ldls.h` describes a `/dev/ldls` loader — the start of loading Linux modules *into the kernel*, which [ldls.md](../architecture/ldls.md) rule 2 says is not how it is done. Nothing calls either. **Delete it**, or decide. |
| **`display_ops`** | `acquire` and `wait_vblank` are in the vtable, implemented by one driver at most, with no consumer: Xorg draws through `/dev/fb0`. Kept for the GPU work ([gpu.md](gpu.md)); that is the reason it stays. |
| **Socket activation** | `schinitd` parses `[Socket name]` and remembers the path. Nothing holds the socket. This is the entire reason for copying launchd, and it is the half that was not built. |
@ -141,7 +129,7 @@ Neither cancelled nor in progress. **Do not open these.**
| | |
|---|---|
| `projects/schinst/` | the installer, the installation environment, `.sip` (see the open decision above) |
| `projects/schinst/` | the installer, the installation environment, `.sip` — drawn by Schcom |
| `projects/schfs-linux/` | SchFS as a filesystem for Linux |
| [gpu.md](gpu.md) | amdgpu and nouveau through LDLS — measured, with the work it needs written down |
| [finch.md](finch.md) | Finch inside SchOS (the language itself is finished and lives in its own repository) |
@ -164,7 +152,7 @@ In order. **Do not start a gate before the one before it closes**, and closing m
| **1 — The foundations** | descriptor passing, a shareable memory object (`memfd`), `AF_UNIX` in the filesystem, dynamic linking, the event descriptors | **closed** — [kernel.md](../architecture/kernel.md), [networking.md](../architecture/networking.md) |
| **2 — Init as a role** | `schinit`, the supervisor, the heartbeat, PID 1 re-exec'd in place | **closed** — [init.md](../architecture/init.md) |
| **3 — The desktop** | Xorg on the framebuffer, Window Maker, a terminal | **closed** — [desktop.md](../architecture/desktop.md) |
| **4 — A root that survives a reboot** | the open decision above | **open** |
| **4 — A root that survives a reboot** | dropped from 1.0 by decision; it is the installer, after 1.0 | **not in 1.0** |
---

View file

@ -40,21 +40,21 @@ Plans leave this directory when they are done. These did, and what replaced them
And one that was **decided against**: `wayland.md`. Wayland needs DRM/KMS, libinput, udev and libseat, and DRM/KMS
alone is a kernel graphics subsystem. The desktop is X. The reasoning moved to [desktop.md](../architecture/desktop.md).
`SchWM` and `SchDE`, which were to be the window manager and the desktop on top of it, are gone from the tree: Window
Maker is the window manager, and a panel or launcher is an ordinary client.
`SchWM` and `SchDE` were removed from the tree as READMEs with no code: Schcom is its own window manager, and SchDE returns as
ordinary clients of Schcom when there is a Schcom to build them on. Meanwhile the window manager is Window Maker.
---
## The order
Set by [1.0.md](1.0.md), which is the authority. In short:
Set by [1.0.md](1.0.md), which is the authority. **1.0 is reached** (a live system; persistence is not part of it).
What is left before anything new starts:
1. **Close the one open gate**: decide what makes a root survive a reboot — the installer, or a narrower
definition of 1.0 (see *The one open decision* there).
2. **Empty the register.** Delete `projects/schcom/` and the unused in-kernel LDLS window, or say what they are for; build
socket activation or remove the key.
3. **Then**, and only then, whatever [roadmap.md](roadmap.md) says is next. GPU acceleration is the thing the laptop
wants most ([gpu.md](gpu.md)), and Finch inside SchOS is the thing the author wants next ([finch.md](finch.md)).
1. **Empty the register**: decide the unused in-kernel LDLS window (delete it, or use it), build socket activation
or remove the key, and give `projects/schcom/` its first client.
2. **Then**, whatever [roadmap.md](roadmap.md) says is next. The two things the owner wants most are the installer
drawn by Schcom — SchOS's own compositor, and the road to SchDE ([roadmap.md](roadmap.md) §5) — and GPU
acceleration on the laptop ([gpu.md](gpu.md)). Finch inside SchOS ([finch.md](finch.md)) comes after.
## What this plan does not address
@ -62,4 +62,4 @@ Stated here so it is not mistaken for covered:
- **No IOMMU**, so a driver in a process is isolated against its own bugs and not against a hostile driver.
- **No page cache**, so a general writable `MAP_SHARED` of a file is refused; `memfd` is the narrow answer.
- **No installer**, so nothing a live system writes survives a reboot.
- **No installer**, so nothing a live system writes survives a reboot — by decision, for 1.0.

View file

@ -54,7 +54,24 @@ what is present ([boot.md](../architecture/boot.md)).
- **udev's job** — a uevent socket and `/sys/devices` — so X and others are told about devices instead of being
given a list.
## 5. The installer and `.sip`
## 5. Schcom, the installer, and SchDE
**Schcom** (`projects/schcom/`) is SchOS's own compositor. v0 is 715 lines that composite clients over a wire format of its
own, and everything it was blocked on in the kernel now exists: Unix sockets, descriptor passing, `memfd`, evdev, framebuffer
`mmap`. The plan is in three steps, each useful on its own:
1. **The installer's GUI is Schcom's first real client.** An installer wants a window, a list, a progress bar and a handful of
input events — small enough to be the program that forces Schcom's protocol to be real, and independent of Xorg, which is not in
the installation environment. It needs a wire format for clients that is designed (Wayland-shaped: surfaces, buffers shared by
descriptor, input events), not the v0 one.
2. **If that works, Schcom becomes useful in its own right**: window management as a module of the compositor (a compositor *is*
its window manager), multiple clients with focus and stacking, damage tracking.
3. **SchDE on top of it**: a panel, a launcher, a wallpaper and settings, as ordinary clients — only once there is a Schcom worth
putting them on. Until then the desktop is Xorg with Window Maker ([desktop.md](../architecture/desktop.md)).
Nothing here starts before [1.0.md](1.0.md)'s register is empty.
### The installer and `.sip`
Scoped in [`projects/schinst/README.md`](../../projects/schinst/README.md), which holds the detail. In short:
@ -79,7 +96,7 @@ Open questions, all wanting a decision before code:
now, or not?
- **Can the kernel mount a `.sip` read-only directly?** If it can, the installation environment's root can simply *be* the `.sip`.
The GUI for this is an X client, like everything else on the desktop.
The GUI for this is a Schcom client (above).
## 6. SchFS beyond SchOS