Horus

Architecture

One place authorises a syscall, before the handler runs. The numbers below are derived from the tree, not typed in.

The ABI defines 128 syscall numbers. A default build installs 108.

The number is in rax. The arguments are in rbx, rcx, rdx, rsi, rdi and r8. Entry is int 0x80. This is not the Linux register convention. docs/SYSCALLS.md is the reference, and the dispatch table in src/kernel/syscall.c is the gate.

A number with no handler, including one that used to exist and was withdrawn, answers SYS_ERR_NOSYS. A handler does not repeat the capability check. The table says which slot and which rights the call needs. cap_lookup resolves that slot through the Rust security core. A missing capability, the wrong rights, or the wrong object type all leave through SYS_ERR_PERM.

The difference between 128 defined numbers and 108 installed handlers is the fail-closed property: the numbers that are not implemented do not fall through to a neighbour.

The kernel is not the whole of what you must trust.

In the trusted computing base: the kernel (src/kernel/, src/boot/, rust/src/), userspace/init, fs_server, console_server, GRUB and the firmware. The shell, the coreutils, TCC and any program you write are outside it. They hold only the capabilities init lent them.

The Rust core is no_std. It does not trust a length the C side passes in. unsafe is confined to the FFI boundary and must carry a safety comment. The C kernel is freestanding, built without SSE or the FPU in ring 0, with a stack protector and with variable-length arrays rejected.

Isolation

Each task has its own address space. User pointers are checked before the kernel reads them. The pool uses RAM the firmware map names below 4 GiB of address. RAM the firmware parked above 4 GiB is not used. That is a ceiling, recorded in docs/LIMITATIONS.md section 3.1, not a claim that every byte of a machine is mapped.

Message passing

IPC is a capability to an endpoint. The message is at most 256 bytes. A server learns which client called by the capability the client was given, not by a sender id the client chose. A reply capability is one-shot.

Memory

Frames, endpoints, notifications and tasks are paid for from untyped memory the caller holds a capability to. Destroying a retyped object frees its name. It does not reuse the bytes. A free list before that reset would let a stale capability meet a page that has been retyped as something else. Reuse is queued, and it waits on revoking the untyped capability first.

Storage

The volume is sealed. The kernel keeps the keys. A Merkle tree covers the metadata, and a TPM counter can anchor the whole volume against replacement by an older copy. That anchor exists only for a volume formatted on a machine with a TPM, and it moves once per boot. Both limits are open.