Library / SDK
hexagonal-sun/moss-kernel avatar
hexagonal-sun/moss-kernel

moss runs an Alpine userspace on an async Rust kernel, and admits it is experimental

Rust Linux-compatible kernel

1,962 stars91 forksRustMIT

At a glance

What is it?
moss is an MIT licensed Unix-like kernel written in Rust and AArch64 assembly that runs a dynamically linked Arch Linux AArch64 userspace including bash and coreutils, with an async core built on Rust futures. It implements 109 Linux syscalls, has no networking stack yet, and lists production hardening as a non-goal.
Who is it for?
Adopt moss if you are studying asynchronous kernel design or need a Linux-ABI playground for driver and syscall work, since the async core and the host-runnable `libkernel` are the actual contribution. Do not adopt it as an operating system to run anything on: the README lists production hardening as a non-goal, there is no networking stack, no systemd, and while tmpfs is read-write, ext2/3/4 has partial write support.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The async core is the thesis, not a feature list

moss is a Unix-like, Linux-compatible kernel written in Rust and AArch64 assembly. What distinguishes it is its use of Rust's `async` and `await` model inside the kernel context, which the README calls one of the defining features.

The specific consequences are listed, and they are the reason to be interested:

All non-trivial system calls are written as `async` functions, with sleep-able functions prefixed with `.await`. The compiler enforces that spinlocks cannot be held over sleep points, eliminating a common class of kernel deadlocks. And any future can be wrapped with the `.interruptable()` combinator, allowing signals to interrupt the waiting future and appropriate action to be taken.

The second of those is the substantive claim. Deadlock from sleeping while holding a spinlock is a scheduling bug that conventional kernel code prevents by discipline and by careful comment placement. Here the type system refuses the construct. Whether that holds up in an interrupt context is a different question, but the stated mechanism is enforcement rather than vigilance.

The third point connects the async core to Linux semantics specifically. A future that can be interrupted is what lets a blocking syscall return `EINTR`, which is a property the userspace compatibility goal depends on rather than an aesthetic preference.

The VFS is described with the same vocabulary: a virtual file system with full async abstractions. So the async model reaches into the filesystem layer, not just the syscall entry points.

109 syscalls is the actual scope, and the roadmap says what is missing

moss currently implements 109 Linux syscalls, listed in a document at `./etc/syscalls_linux_aarch64.md`.

That number is the honest measure of the project, and the README does not obscure it. The roadmap lists expanding coverage beyond the current 109 calls as a current focus area, alongside a networking stack with TCP/IP, a fully read/write capable filesystem driver, and systemd bringup.

So four of the five things you would need to run anything real are on the to-do list. No networking means no remote access, no package updates and no services. No full writable filesystem means state does not persist to a block device in the normal way. No systemd means no conventional init. And the syscall count constrains everything: a program that calls something outside the implemented set does not get a partial result, it fails.

The Non-Goals section is unusually blunt about the boundaries, listing binary compatibility beyond AArch64 and production hardening. The closing line says it plainly: moss is an experimental kernel focused on exploring asynchronous design and Linux ABI compatibility in Rust.

What it can already do is specific. The README states it is capable of running a dynamically linked Arch Linux AArch64 userspace, including bash, BusyBox, coreutils, ps, top and strace. `strace` is the interesting one, since it requires ptrace, and the README confirms ptrace support sufficient to run strace on Arch binaries.

There is an `etc/moss_demo.gif` linked near the top showing a boot, which is the fastest way to see what the current state actually looks like before reading further.

Memory management, scheduling and filesystems, with the gaps marked

The architecture section covers AArch64 only, and states that there is a well-defined HAL allowing for easy porting to other architectures such as x86 or RISC-V. The contributing section invites porting to x86, so the HAL is designed to make porting to other architectures easier.

Memory management is the most complete-looking part of the list. Full MMU enablement and page table management. Copy-on-write pages. Safe copy to and from userspace async functions. Kernel and userspace page fault management. Kernel stack-overflow detection. Shared library mapping and relocation. And `/proc/self/maps` support.

That `/proc/self/maps` entry is more significant than it looks. It is the contract a dynamically linked loader depends on to resolve mappings, and it is the reason binary compatibility with a glibc or musl userspace is even possible.

Allocation is described with unusual specificity: a buddy allocator for physical addresses, `smalloc` for boot allocations and tracking memory reservations, and a full slab allocator for kernel object allocations featuring a per-CPU object cache.

Process management covers UP and SMP scheduling via EEVDF, task migration via IPIs, `fork()`, `execve()`, `clone()`, job control with process groups and waitpid, and signal delivery, masking and propagation for SIGTERM, SIGSTOP, SIGCONT and SIGCHLD among others.

Filesystems are where the gaps show. A ramdisk block device, FAT32 marked read-only, ext2/3/4 with read support and partial write support, a `devfs` driver for character device access, a read-write `tmpfs`, and `procfs`. Read-only FAT32 and partial-write ext2 is the current position, which matches the roadmap item about a fully read/write capable filesystem driver.

libkernel is architecture-agnostic, so the logic tests run on your host

The testing story is the part that makes moss practical to work on, and it comes from one design decision.

moss is built on top of `libkernel`, a utility library designed to be architecture-agnostic. The README states the consequence directly: this allows logic to be tested on a host machine such as x86 before running on bare metal.

That is not a small convenience. Kernel code normally can only be tested on the target, or through elaborate host builds with stubbed headers. Splitting the target-independent logic into its own crate means a developer on an x86 laptop runs the suite natively and gets a fast edit-compile-test loop.

What `libkernel` contains is listed. Strong typing for address types: `VA` for virtual, `PA` for physical, `UA` for user addresses. On its own that type separation prevents a whole class of pointer arithmetic bug where a physical address is used as a virtual one. Containers include `VMA` management, a generic page-based ring buffer called `kbuf`, and waker sets. Sync primitives are `spinlock`, `mutex`, `condvar` and `per_cpu`, which are the same primitives the async core relies on.

The suite is described as comprehensive, with 230+ tests validating functionality across architectures, and the README gives a concrete example: validating AArch64 page table parsing logic on an x86 host. So the architecture-specific parsing code is inside the architecture-agnostic crate precisely so it can be tested on the wrong CPU.

There is a second layer: `usertest`, a dedicated userspace test suite validating syscall behaviour at run time, which lives in its own workspace member and ships as a binary inside the boot image at `/bin/usertest`.

Building: an ARM toolchain, QEMU, and just as the task runner

The prerequisites are specific, and one of them is the thing that trips people up.

You need QEMU for AArch64 emulation, plus wget, e2fsprogs and jq for image creation. The project uses `just` as a task runner, and the README notes you can run the underlying commands directly instead.

The toolchain requirement is separate: a version of the `aarch64-none-elf` toolchain from ARM's GNU Toolchain downloads, installed by downloading the release, unpacking it and adding the `bin` directory to PATH.

Per-platform install commands are given:

bash
sudo apt install qemu-system-aarch64 wget jq e2fsprogs just
bash
brew install qemu wget jq e2fsprogs just

On Nix and NixOS, `nix develop` covers it, and the repository carries `flake.nix` and `flake.lock` at the root.

Running it is short:

bash
just run

Without `just`, the equivalent is a first-time image creation followed by a direct run:

bash
# First time only (to create the image)
./scripts/create-image.sh
# Then, to run the kernel in QEMU
# By default it will launch into bash, however `ash` and `sh` are both available.
cargo run --release -- /bin/ash

The comment corrects a small documentation trap: the kernel launches into bash by default, but Alpine does not have bash, and `ash` and `sh` are both available. That one word is the difference between a working and a confusing first run.

The kernel runs from `moss.img`, described as a minimal Alpine rootfs with the addition of the custom `usertest` binary in `/bin/usertest`. Note the discrepancy with the feature list, which mentions Arch Linux userspace support; the shipped image is Alpine.

Tests come in three flavours: `just test-unit` for the host-side `libkernel` suite, `just test-userspace` for the syscall suite in QEMU, or `cargo run -r -- /bin/usertest` directly. After changing the usertests, `just create-image` rebuilds the image.

Cargo metadata, a Dockerfile that boots the toolchain for you

The `Cargo.toml` describes a workspace of three members, `libkernel`, `moss-macros` and `usertest`, with the kernel package itself at version 0.1.0 on edition 2024.

The dependency list explains the boot path and the hardware abstraction in one glance. `aarch64-cpu` provides the CPU feature definitions. `arm-pl011-uart` and `arm_pl031` are the PL011 UART and PL031 RTC, which is how output appears on a QEMU virt machine. `tock-registers` provides register definitions, and `virtio-drivers` gives paravirtualised device access. `fdt-parser` handles the flattened device tree that QEMU hands the kernel at boot.

`smoltcp` is the interesting entry, listed with features for medium-ethernet, medium-ip, proto-ipv4, proto-ipv6, socket-tcp and socket-udp, all with alloc and without std. That is a TCP/IP stack sitting in the dependency list while the roadmap lists a networking stack as future work, which suggests the transport layer is being built ahead of the kernel's own socket layer.

Other entries are deliberate: `getargs` for the kernel command line, `object` configured for `core`, `elf` and `read_core` for parsing ELF binaries, `ringbuf`, `blake2`, `chacha20` with the rng feature, `rustc-hash`, `bitflags` and `atomic_enum`.

Workspace lints are configured with clippy rules `semicolon_if_nothing_returned` and `uninlined_format_args` both set to warn, so the kernel is held to the same clippy baseline as the utility library.

There is a Dockerfile, and it is the easiest path if you do not want to install the ARM toolchain locally. It starts from `ubuntu:latest`, installs build tools and `qemu-system-aarch64`, installs Rust through rustup, copies in `./scripts` and runs `./scripts/download-arm-toolchain.sh` to fetch and unpack the toolchain into `/opt/arm-toolchain`, then has a separate `image-builder` stage that runs `./scripts/create-image.sh` and a final stage that copies the resulting `moss.img` and builds the release kernel. The multi-stage split means the image is built once and reused, and the toolchain download is cached by Docker rather than repeated.

There is also a `rust-toolchain.toml`, so the toolchain version is pinned in the repository rather than left to whatever rustup defaults to.

Editorial conclusion

Adopt moss if you are studying asynchronous kernel design or need a Linux-ABI playground for driver and syscall work, since the async core and the host-runnable `libkernel` are the actual contribution. Do not adopt it as an operating system to run anything on: the README lists production hardening as a non-goal, there is no networking stack, no systemd, and while tmpfs is read-write, ext2/3/4 has partial write support. Verify first that you have the `aarch64-none-elf` toolchain on PATH and an emulator, since the kernel targets AArch64 and the documented path to running it is QEMU, and note that the crate version is still 0.1.0 with no GitHub releases to pin.

Frequently asked questions

How do I build and run the moss kernel?

Install `aarch64-none-elf`, QEMU, wget, e2fsprogs, jq and `just`, then run `just run`. Without `just`, run `./scripts/create-image.sh` once and then `cargo run --release -- /bin/ash`, since the kernel defaults to bash and Alpine does not have it.

How does moss use Rust async in the kernel?

All non-trivial system calls are written as `async` functions, sleep-able functions are prefixed with `.await`, the compiler enforces that spinlocks cannot be held across sleep points, and futures can be wrapped with an `.interruptable()` combinator so signals can interrupt a wait.

Can I run the libkernel tests on my own machine?

Yes. `libkernel` is architecture-agnostic by design so its logic can be tested on a host such as x86 before running on bare metal, and the README describes a suite of 230+ tests, including validating AArch64 page table parsing logic on an x86 host. Run it with `just test-unit`.

What is not finished in moss?

The roadmap lists a TCP/IP networking stack, a fully read/write capable filesystem driver, syscall coverage beyond the current 109 calls, and systemd bringup. The README also names production hardening and binary compatibility beyond AArch64 as non-goals.

Which filesystems does moss support?

A ramdisk block device, FAT32 as read-only, ext2/3/4 with read support and partial write support, a `devfs` driver for character device access, a read-write `tmpfs`, and `procfs`. The boot image is a minimal Alpine rootfs with the `usertest` binary added at `/bin/usertest`.

What is the licence and current version of moss?

MIT, with a LICENSE file at the repository root. The kernel package is at version 0.1.0 on edition 2024, the repository has no GitHub releases, and the last push was on 2026-10-13.

Official sources

  1. hexagonal-sun/moss-kernel on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hexagonal-sun-moss-kernel.svg)](https://hysenlabs.com/projects/hexagonal-sun-moss-kernel)

Was this useful?