← Back to writing

IsmenaOS — The Swift Runtime That Never Was

A C/Assembly kernel with a Swift runtime for userspace. The Swift runtime assumes Darwin or Linux exists. It doesn't. What happens when you couple your kernel to a language runtime you don't control.

IsmenaOS was the project where I repeated KairosOS’s mistake — overscoping — but in a new and instructive way. The idea: a C/Assembly kernel with a Swift runtime for userspace applications. Swift’s safety guarantees (optionals, value types, ARC) felt like a natural fit for a system that prioritized correctness.

The project has been put on hold for further research. That’s the honest line from the README, and it tells the whole story.

What Got Built

The familiar parts worked. After three kernels (two KairosOS versions and Voleta), I could produce a booting C kernel with a custom libc almost mechanically:

  • Kernel entry point with GDT, IDT, and console initialization
  • Custom libc with the standard functions (memset, memcpy, strlen, basic I/O stubs)
  • Cross-compiler targeting x86_64-ismena-elf
  • QEMU boot scripts

IsmenaOS’s kernel wasn’t a breakthrough. It was a refinement — the same architecture as Voleta, with cleaner code because I’d written it three times before. The libc was the best version yet, with better header organization and more complete string handling.

The kernel was never the problem.

The Swift Problem

Swift’s runtime is not a thin layer. It’s substantial infrastructure:

Memory management. Swift uses ARC (Automatic Reference Counting), which requires runtime support for reference counting operations, weak reference tracking, and deallocation. The runtime manages a heap, tracks object lifetimes, and handles retain/release cycles. On macOS, this infrastructure is provided by libswiftCore.dylib. On Linux, by libswiftCore.so. On IsmenaOS, by nothing.

Type metadata. Swift’s generics and protocols require runtime type information — metadata structures that describe types, protocol conformances, and witness tables. This metadata is generated by the compiler and consumed by the runtime. Without the runtime, generic code and protocol dispatch don’t work.

Error handling. Swift’s throw/catch relies on a runtime unwinding mechanism. On Darwin, this uses the Objective-C exception infrastructure. On Linux, it uses libunwind. On a custom OS, you’d need to implement unwinding from scratch or replace Swift’s error handling model entirely.

Module initialization. Swift modules have initialization functions that the runtime calls at load time. Without a dynamic loader that understands Swift module conventions, static linking is the only option — and even static linking requires the runtime to be present.

The gap between “Swift compiles to machine code” and “Swift runs on my kernel” was far wider than I anticipated. You can compile Swift without a target OS — swiftc supports cross-compilation — but the resulting binary expects a runtime that expects an OS. The circular dependency is fundamental.

The Libc Bridge

Every kernel I’d built needed a custom libc. What IsmenaOS taught me is that the libc is the bridge between the kernel and everything else. The Swift runtime calls libc functions. The Python interpreter calls libc functions. Lua calls libc functions. Any language runtime you want to host on your kernel goes through your libc first.

The quality of your libc determines what you can run. KairosOS’s minimal libc couldn’t support Lua. IsmenaOS’s improved libc still couldn’t support Swift’s runtime requirements — not because the functions weren’t there, but because the Swift runtime expected POSIX semantics (threading, file I/O, signals) that a minimal libc doesn’t provide.

This is why musl exists as a separate project from the Linux kernel. Building a libc that supports real-world software is a multi-year effort by a dedicated team. My custom libcs were educational, not practical.

What Carried Forward

Don’t couple your kernel to a language runtime you don’t control. The D exokernel taught this partially — D’s runtime was easier to strip down than Swift’s, but still assumed an OS. IsmenaOS made the lesson explicit: if the language runtime requires POSIX threads, virtual memory semantics, and dynamic loading, and your kernel doesn’t have those yet, the runtime won’t work. Period.

The libc is the universal adapter. Whatever language your userspace uses, it talks to the kernel through the libc. Investing in the libc is investing in every future userspace program. TerranoxOS’s syscall ABI (91 syscalls) was designed with this in mind — the syscalls are the foundation that a libc wraps, and the libc is the foundation that language runtimes wrap.

Some projects teach more by failing. IsmenaOS’s kernel code was its best artifact — the cleanest, most refined version of the C kernel pattern I’d developed across KairosOS and Voleta. The Swift integration failure taught me about runtime dependencies, coupling, and the hidden complexity of language platforms. Both lessons were worth the project.


This is Part 5 of 8 in the OS Kernel Museum series.