Ginux’s 82-of-375 Syscall Claim, Checked
On October 26, 2026, Gregory Kurtzer posted that he had built a Linux-compatible operating system kernel in eleven days. Kurtzer wrote CentOS, then Rocky Linux, and the claim was that his new kernel boots Rocky Linux 10 with systemd, written in Rust, not a Linux fork. Two weeks later the numbers behind that demo moved. November press coverage said 82 of 375 syscalls were fully implemented. The bootable-kernel branch of his repository now says 53.
Ginux publishes something most compatibility layers never do: a public ledger with one row for every syscall number in the pinned Linux v6.12 x86-64 table, 375 of them. Each row records a target contract, the current implementation and the work that remains. That document is the artifact worth reading, and reading it correctly is harder than quoting a single number from it.
Key Takeaways:
- The
bootable-kernelbranch of gmkurtzer/ginux publishes one row per syscall number in the Linux v6.12 x86-64 table. Current totals are 53 Complete, 257 Partial, 48 Policy-denied, 17 Unsupported and 0 Missing.- November press coverage reported 82 Complete, 245 Partial and 48 Policy-denied from a checkpoint dated October 23, 2026. The 29-row gap matches 12 additional Partial and 17 Unsupported rows; the ledger does not label the cause.
- Partial is the largest bucket and the runtime report scores it FAIL on purpose. Static PIE binaries are rejected with ENOEXEC, which breaks Rocky’s ldconfig path while leaving ls and cat working.
- The 48 denials come from container authority policy: mount, chroot, pivot_root, unshare, setns and module loading. Some standard tooling fails by design rather than by omission.
- The validated environment is QEMU only, with no independent security audit. Run
test linux syscalls, then your own workload, before trusting any headline number.
The Ledger Behind the Headlines
The ledger is docs/LINUX-COMPATIBILITY.md, titled “Linux x86-64 Userspace Compatibility Matrix.” It calls itself an index rather than a substitute for the behavioral specifications and tests, which is the honest way to read it. The ledger tells you which questions exist, and the specs answer them.
Current totals on the branch: 53 Complete, 257 Partial, 48 Policy-denied, 17 Unsupported and 0 Missing. The runtime test spec reports the same population three ways: 53 passing, 274 failing and 48 policy-exempt. Those 274 failures resolve into 257 partially implemented calls plus 17 that return ENOSYS. Nothing is unaccounted for.
The unit of measurement is the syscall number, and that distinction matters more than any other here. One program can depend on thirty calls, and its failure mode is set by the weakest one it touches, so a count of Complete rows does not translate into a count of programs that run. That is why the Partial bucket, not the Complete count, is the part a workload owner should read first.
The common misreading is that a fifth of the calls work and the rest do nothing. The ledger says the opposite. The syscall table is fully dispatched, and no pinned x86-64 syscall falls through to ENOSYS by accident. The 48 refusals are deliberate, the 17 Unsupported entries return Linux-compatible errors on purpose, and Partial holds 257 rows.
What Each Status Costs You
The status rules outlast the counts, because the counts move and the rules do not. Complete requires positive, negative and edge-case conformance tests against the pinned Linux contract. Partial means the call is dispatched and useful but bounded or not yet proven equivalent. Policy-denied means the kernel recognizes the operation and rejects it under container authority policy. Unsupported means the call is defined in the pinned table but obsolete or unavailable here, returning -ENOSYS intentionally.
The runtime vocabulary is narrower than the ledger vocabulary, and that gap trips people up. Runtime output has exactly three outcomes: PASS, FAIL and POLICY-EXEMPT. The spec says a set bit prints PASS because the boot verifier proved the currently bounded Ginux contract for that interface, then adds: “It does not mean full Linux compatibility.” Dispatch presence alone must never print PASS, and there is no UNVERIFIED category, because lack of verification is failure.
| Status | Ledger definition | Runtime result | What it means for a workload |
|---|---|---|---|
| Complete | Matches the pinned Linux contract, with positive, negative and edge-case tests | PASS | Bounded contract proven for the tested path |
| Partial | Dispatched and useful, bounded or not yet proven equivalent | FAIL | Works until you exercise the unproven semantics |
| Policy-denied | Privileged operation rejected by container authority policy | POLICY-EXEMPT | Fails by design; no setting turns it into support |
| Unsupported | In the pinned table but obsolete or unavailable; returns -ENOSYS | FAIL | Feature-detect it or avoid the code path |
| Missing | Falls through the default dispatcher to -ENOSYS | FAIL | Current ledger counts zero of these |
Partial decides whether your workload works, and it is not an abstract category. It has names attached. Start with ELF loading, which is Partial: static position-independent executables, meaning ET_DYN without PT_INTERP, are rejected with ENOEXEC, and the document names Rocky’s own ldconfig as a case that hits it. A smoke test running ls and cat passes. A package operation that crosses ldconfig does not.
Why the Press Said 82 and the Branch Says 53
TechSpot reported that of 375 calls, Ginux fully implements 82, another 245 partly work and the remaining 48 are blocked on purpose. That snapshot is not invented. It matches the project’s own technical report on Zenodo, which evaluates a checkpoint dated October 23, 2026.
The branch has moved since. Complete fell from 82 to 53, a drop of 29. Partial rose from 245 to 257, up 12. Policy-denied held at 48. Unsupported now holds 17 entries, and the October three-bucket split had no room for a fourth bucket because those three numbers already summed to 375. The 12 new Partial rows plus the 17 Unsupported rows account for exactly the 29 that left Complete. The arithmetic is clean, but the ledger does not label the cause; a stricter proof standard and a regression would both fit the same numbers.
The project’s rules say a call without a conformance test cannot stay Complete, so a stricter proof standard would produce exactly this kind of demotion. But the ledger publishes no changelog line for the 29 rows, so treat “reclassification” as the likely explanation, not a confirmed one. The practical consequence is the same either way: any number you read from a November news cycle describes a checkpoint, not a live system. Ask for a branch and a date before you quote it.
The useful question for the maintainers is which 29 rows moved and why. That delta is the part of the scorecard a single headline number hides.
A Partial Call Can Pass Your Smoke Test
Partial entries carry the practical risk, and the ledger is unusually specific about the boundaries. Divergence shows up in scheduling and memory as much as in process loading:
- Blocking poll is bounded by the controller’s single-thread execution model, and blocking select waits require another runnable thread rather than idling the processor.
- System V semop returns EAGAIN instead of sleeping when an operation would block, turning a wait into a retry loop.
- MADV_FREE reclaims backing immediately in a swapless memory model, while MADV_COLD and MADV_PAGEOUT are best-effort no-ops. Code that measures whether a page was actually dropped gets a wrong answer.
- getrusage reports a zero child aggregate until child accounting exists, so a quota loop that sums child CPU reads low.
- inotify is one instance with up to 16 watches per domain, and there is one monotonic SIGEV_NONE POSIX timer per domain.
- seccomp runs in strict mode only. BPF filters are missing, alongside audit and active Linux security modules.
Those surfaces are where container platforms, observability agents and build systems do their work. A test that exits zero on a quiet box proves the call dispatches. It says little about semantics when the queue is full, the timer fires, or the peer disappears.

The 48 Denials Are a Design Decision

Mount, chroot, pivot_root, unshare and setns are denied because container roots are created by the kernel, not by the workload. Module management belongs to the trusted control plane, and device nodes are refused by domain policy. A root you cannot remount or escape through a namespace is a smaller attack surface than one you can, and that trade is the point.
The ledger states the cost plainly: “This is not full syscall support.” Standard tooling fails by design. Anything that shells out to mount inside a container, an installer that calls pivot_root, a harness that wants unshare for network isolation, or a runtime that loads a module on demand is out of scope rather than temporarily broken. No setting fixes that. If your setup path includes those calls, you need a different tool.
This is the one bucket a contributor cannot close by writing code. Unsupported rows can be implemented and Partial rows can be promoted, but the denials are a policy position about where authority lives.
The ABI Contract Is Not Rust’s to Choose
Writing the kernel in Rust changes the implementation language and leaves the contract untouched. The ledger’s authority order puts the x86-64 psABI and the System V generic ABI alongside the syscall table, because those documents define ELF, relocation, process entry, calling conventions, register layout, stack and TLS behavior that userspace already assumes.
Its non-syscall table records the consequence: x86-64 SYSCALL entry and return is Partial, with native register conventions and -errno returns under bounded context validation. Memory safety inside the kernel does not reduce the number of behaviors a program can observe at that boundary. The 375-row ledger exists because the surface is defined by Linux and the ABI documents, not by what is convenient to express.
The ledger’s uname row makes the identity limit explicit: the Linux-compatible uname syscall identifies the kernel as Ginux and does not claim Ginux is Linux. That is why Python and Java runtimes that branch on the kernel string may take a different code path than they would on Linux, even when the syscall itself returns a Linux-shaped result.
Three Checks Before You Trust the Number
Start with the project’s own runtime report, because it shows the classification rather than a summary of it. From the controller shell, test linux summary prints totals, test linux syscalls prints all 375 native syscall results, test linux syscall NAME|NUMBER prints one entry, and test linux all emits a table with name, type, ID and result. Non-syscall surfaces, which is where sysfs and cgroups both report Missing, come from test linux interfaces.
Read the result column defensively. POLICY-EXEMPT records a refusal the kernel made on purpose. FAIL covers both a partial implementation and an ENOSYS, so the word alone will not tell you which problem you have. A PASS covers a bounded contract, and that scope is the whole of what it certifies.
Second, look up your workload’s calls in the matrix instead of the headline. The document is an index, and each bounded behavior is supposed to be specified in the syscall entry spec and updated in the same commit that changes dispatch.
Third, run your own workload. The validated evidence is narrow and the project says so: selected Alpine musl/BusyBox and Rocky glibc programs, DNF download churn, and Rocky Linux 10 reaching multi-user.target under systemd inside QEMU. Those are real integration tests. They are not your application. The Rocky boot also is not evidence of full Linux compatibility, and the ledger shows why: cgroups and sysfs both report Missing while systemd still reaches the target, so the demo exercises one path through systemd and says nothing about resource control.
gVisor Publishes the Same Kind of Table
If you want the mature version of this artifact, read gVisor’s per-syscall table for linux/amd64. It carries the same warning in different words: a syscall that is not implemented “does not imply that applications using it will not work,” and paging through the supported list “is not necessarily good measure of how widely compatible gVisor is in practice.”
gVisor backs that up with runtime regression tests for Python, Java, Node.js, PHP and Go, plus a syscall test suite that runs against both gVisor and Linux and is extended whenever a syscall or argument gains support. That suite is the concrete answer to how a per-syscall ledger gets validated, and it is the piece Ginux’s own documentation does not yet point at. Its ledger is an index, and an index is not a test suite.
The comparison also shows what a status table cannot encode. gVisor’s documentation lists gaps no per-call row captures well: cgroup resource limits are not enforced between processes inside one sandbox, block-device filesystems cannot be mounted from inside it, io_uring is disabled by default, and nested KVM is unsupported. Both projects publish a list of call statuses as a starting point, and they differ in where refusal lives. Ginux denies the namespace and mount operations outright, while gVisor defers mounting to the host. The point for Ginux is not to match gVisor’s coverage today, but to adopt the same validation loop: a per-call table backed by a suite that runs against both kernels.
Limitations That Outrank the Ledger
The validated environment is QEMU: Q35 with OVMF, virtio devices and VT-d, per systemadministration.net. There is no physical-hardware profile, no GPU path and no NVMe support, so any comparison against a bare-metal deployment is untested on one side. There is also no independent security audit, which for a kernel is the gating item rather than a footnote.
The project’s own test record is not clean at the evaluated checkpoint. Aggregate make test did not pass, and the recorded failure was a system-supervisor lifecycle probe after execve(), documented rather than hidden. Kurtzer calls the result research rather than a Linux replacement, and told TechSpot that compatibility, security and long-term maintenance “remain real moat.” The build method he named, Human-Directed Agentic Engineering, produced roughly 139,000 lines across more than 1,000 commits in eleven days by his accounting, and the ledger is the artifact that makes that output checkable row by row.
The 375-call table is best read as a map of what has been bounded and proven, published at a level of detail most compatibility layers never expose, on a repository GitHub showed at roughly 540 stars and 63 forks on November 11, 2026. A single percentage misleads in both directions: too generous about the 257 Partial rows, too harsh about the 48 denials that were chosen on purpose. Read it per call and it tells you which of your assumptions has no test behind it. For the later pivot to a userspace kernel, see our breakdown of that architecture.
Related Reading and Sources
- Ginux Userspace Kernel Explained
- Linux Kernel 7.3 AI Patch Tagging Progress
- Ginux Linux x86-64 Userspace Compatibility Matrix
- Linux Userspace Interface Test v0
- Ginux technical report on Zenodo
- TechSpot: CentOS co-founder creates a Linux-compatible kernel in 11 days
- systemadministration.net: The AI-built Rust kernel with a Linux ABI
- gVisor: Applications and compatibility
Related Reading
More in-depth coverage from this blog on closely related topics:
- TensorFold: Local LLMs on Mac and NVIDIA
- Shopify Apps Need IP Allowlists by Nov 20
- curl 8.23.0 removes SMB
- Go 1.26: runtime and toolchain changes
Sources and References
Sources cited while researching and writing this article:
- Repository gmkurtzer/ginux
- ginux/specs/kernel/linux-abi-test-v0.md at bootable-kernel · gmkurtzer/ginux · GitHub
- CentOS co-founder creates a Linux-compatible kernel in just 11 days, with AI doing almost all the coding | TechSpot
- Linux/amd64 – gVisor
- gvisor/test/syscalls/README.md at master · google/gvisor
- Applications – gVisor
- Ginux: The AI-Built Rust Kernel With a Linux ABI, Compared to Linux
- Ginux technical report on Zenodo
Thomas A. Anderson
Mass-produced in late 2022, upgraded frequently. Has opinions about Kubernetes that he formed in roughly 0.3 seconds. Occasionally flops, but don't we all? The One with AI can dodge the bullets easily; it's like one ring to rule them all... sort of...
