console,file,proc and the utility syscall profile from its own manifest. QEMU screenshot, cropped, not retouched.Why this was missing
EuroOS is a kernel written from scratch, with its own filesystem, network stack, X server and desktop. For a year every program that ran on it was a program we built into the image: the system apps, the test programs, and the Linux-ABI bridge that runs glibc software up to Chromium. That is fine for a research kernel and useless for an operating system. An operating system is a place where other people's software runs.
Two things stood in the way. The launcher's list of applications was a constant in the kernel source. And eupkg, our signed package manager, verified every package against exactly one key: ours. There was no way for anyone else to ship anything, and no way for a user to decide whom to trust.
The package
An app is a .eupkg file: a plain zip with three members. MANIFEST.toml describes the app, the binary is the ELF the developer compiled, and signature.ed25519 is a 64-byte Ed25519 signature over the manifest. The manifest carries the SHA-256 of the binary, so the signature covers the binary too. No new format was invented; the same package type EuroOS already used for its own libraries grew a few fields.
[package] name = "hello-x11" version = "1.0.0" binary = "hello" kind = "app" title = "Hello (window)" caps = "console,file,proc" profile = "utility" display = "x11" [build] binary_sha256 = "4f1134d2f32925dec85ab8e6a7c2a4eebfa7a35aa44a81fe34e4f7ebf724990c"
The four new fields are the policy. caps lists the EuroGuard capabilities the app gets: console for stdout, file for the user's files, net for the network, proc for process information. What is not listed is refused by the kernel, syscall by syscall. profile picks the syscall profile: utility (no new processes, no tracing, no mounts, no identity change), renderer (also no inbound sockets, no filesystem writes, a file scope limited to system paths), strict (read, write, exit) or full. display says whether the app is a command-line program or opens an X11 window on the in-kernel X server. title is what the launcher shows.
Trust: the owner decides
A package verifies against the EuroOS developer key or against any key the owner of the machine has enrolled. Enrolling is one command, app trust /usb/me.pub me, and only the owner can do it. The kernel keeps the enrolled keys in /etc/euroapps/trusted.keys. A package signed with a key nobody enrolled is refused with the reason in the log. A package whose binary was changed after signing is refused too: the hash in the manifest no longer matches.
The check does not stop at install time. At every launch the kernel re-reads the stored binary, hashes it again and compares it with the registry before a single instruction runs. A blob that was altered on disk after installation does not start. Then the manifest's capabilities and profile are applied to the new process, from the kernel, from the outside. The app cannot raise them.
This is what the dev build's self-test proves on every boot, with a package signed by an example key the kernel does not trust by default:
[app] REFUSED /tmp/hello-cli.eupkg: BadSignature [app] developer key 'example-dev' enrolled (69fa04981383cfb8…) [app] installed hello-cli v1.0.0 (app, caps 'console,proc', profile utility, display none) [app] REFUSED /tmp/hello-cli-tampered.eupkg: BadZip [app] HELLO from an installed EuroApp (pid 1) [app] built by a third-party developer, signed with their own key, installed by eupkg [app] EuroApps SDK: unknown-key-refused=true · key-enrolled=true · signed-package-installed=true · tampered-refused=true · listed-as-app=true · runs-with-manifest-caps+profile(exit 7)=true → OK
Hello in forty lines
There is no EuroOS library to link against. An app is a C program against glibc, with -lX11 if it wants a window. The policy is applied from outside the process, so the program does not know or care that it is sandboxed.
# any C program against glibc; for a window add -lX11 gcc -O2 -o hello hello.c -lX11 # your own key, once python3 sdk/euroapp.py keygen mykeys/me # mykeys/me.key (private), mykeys/me.pub python3 sdk/euroapp.py build --name hello --version 1.0.0 --binary hello \ --key mykeys/me.key --title "Hello" --caps console,file,proc --profile utility \ --display x11 --out hello-1.0.0.eupkg python3 sdk/euroapp.py verify hello-1.0.0.eupkg --pub mykeys/me.pub
euroos:/ $ app trust /usb/me.pub me euroos:/ $ app install /usb/hello-1.0.0.eupkg euroos:/ $ app run hello-x11 Hello (window): launched in a window (task 27; caps 'console,file,proc', profile utility)
A windowed app also appears in the launcher under its title and opens in a framed desktop window. The two examples, hello-cli and hello-x11, a build script and the developer guide are in sdk/ in the repository.
What the first window found
The self-test passed on the second attempt. The window took six. Each attempt ended with a bounded syscall trace that named the next defect, and none of them was in the app.
- A process without the
filecapability could not load libc. The dynamic loader's opens of/libwere refused as file access. Read-only access to the system libraries is now exempt fromfile; that capability governs the user's data, not the system's. - The X11 display connection counted as network. Xlib connects over an AF_UNIX socket, which the kernel filed under
net. A local client socket is inter-process communication, not the network: it needs no capability now. Serving a socket (bind, listen, accept) still does. - An infinite
poll()returned 0 and xcb hung up. To hand the CPU back under emulation, the kernel returned 0 frompoll(fd, 1, -1)after 64 tries. GTK and Chromium poll with finite timeouts and never noticed. Modern xcb treats a 0 from an infinite poll as a dead connection and printed "X connection to :0 broken". The early return is now-EINTR, the one spurious result POSIX allows, which glib, libevent and xcb all retry. - The in-kernel X server had no core text. Every program so far drew text client-side (cairo, Pango) and uploaded pixels.
XDrawStringuses PolyText8, which the server silently consumed, along with ClearArea and the window's background pixel. They are implemented now, with the kernel's 8x8 bitmap font as the one server font and real metrics in the QueryFont reply.
None of this is glamorous. All of it is what happens when software you did not write meets a kernel for the first time, and it is the reason the SDK exists: every outside program is a test we could not have written ourselves.
What is not there yet
Stated plainly
- Apps are Linux-ABI programs running through the compatibility bridge. Native EuroOS apps speak EuroGuard capabilities and EuroIPC directly; the bridge is how outside software gets on, not what EuroOS is.
- One hosted window at a time. The whole binary is loaded into RAM, not demand-paged. No dependencies between packages, no icons yet.
- The library set is what EuroOS already serves: glibc, X11, cairo, Pango, GTK3 and their dependencies. Static-link anything else.
- Core X text has one fixed 8x8 font. Toolkits that draw client-side are unaffected.
- No app store. The next step is a signed package index over the same channel the system updates come from, so
eupkg install nameworks without a USB stick. - This is in the source tree and ships with the next image. Everything above was measured in QEMU; hardware runs wait for the lab.
Try it
The developer guide, docs/APP-DEVELOPER-GUIDE.md, has the full flow: build, key, package, install, what the capabilities and profiles mean, and what the X server offers a core-X program. If you build something, tell us. The first app that is not ours goes on this page.