I'm Void, self-taught, based in Gdańsk. I write kernels, languages and frameworks because I don't trust anything I haven't taken apart. Mostly C and assembly, with Python and Rust where they fit.
A 64-bit OS in x86-64 assembly and C, written without a safety net. The first wall is long mode: get one bit wrong in the page tables and the CPU just resets, with nothing to tell you why. After that came page faults, and after that I read the NVMe spec until the queues finally made sense.
It boots from a raw disk image now, and it runs a scheduler, a filesystem and userspace programs. Getting there taught me more than any amount of reading about how it's supposed to work.
It booted, started a ring 3 shell, and then died. Sometimes after an hour, sometimes during init. No panic, no backtrace, no pattern. Just a reboot.
The cause turned out to be that my fault handler was faulting. A page fault, then a double fault, then the CPU gives up. The bug was destroying the one tool I had for finding it.
So I stopped hunting and built a better tool first. Every fault now goes through a single dump, and I finally printed CR2 and CR3, the two registers I hadn't thought of. For an address-space bug they're the most important numbers in the machine. CR2 turned a ghost into a stack trace.
Then came the digging, and it wasn't one bug. It was a pile of them, and every one destroyed evidence while I was looking at it.
You can't debug corruption with a debugger built on top of the corruption. When a machine reboots itself with no explanation, stop debugging and start instrumenting. And an error path you never test isn't an error path, it's a future kernel panic. Writing the failure branch is the actual work.
save_contextCalled kmalloc on every timer tick and orphaned the old block. The heap was gone in seconds.IST stacksAllocated, but not one IDT gate pointed at them. The double fault handler ran with no stack guaranteed.switch_contextLoaded the current process's page tables, not the target's. Scheduling a kernel thread dereferenced NULL.ring 0 threadsGot handed a stack pointer userspace could control.iretqAsked to do a same-privilege return that real CPUs don't agree on. That ambiguity was the randomness.kfreeA missing return after one call, and an operator-precedence mistake that freed wild pointers.sys_mmapValidated nothing, so ring 3 could unmap kernel pages.I'd been using compilers for years without knowing what one does between my source file and the CPU. It was a black box, and I didn't like that. So I wrote one: lexer, parser, bytecode compiler and a VM, all in C.
The VM is stack based and runs on computed gotos. What I'm proudest of is the memory model. It's a hybrid: arenas for things that live and die together, pools for things that get recycled, and wherever I can I keep values on the stack, because that's the fastest memory there is.
// every value in the VM is this typedef enum { VAL_UNDEF, // raises NameError on read VAL_NULL, VAL_INT, VAL_FLOAT, VAL_OBJ, // heap: string, list, function… } ValueType; typedef struct { ValueType type; union { int64_t i; double f; Object *obj; } as; } Value;
Single-threaded, warm cache, best of 5 runs. It loses two, and string concatenation isn't close.
I built Nebula to understand ASGI from the inside. Once I understood the event loop, I got bored and went back to C. So it's paused, and I'm fine with that. It did its job.
It still works, though. Decorator routing, middleware, sessions, templates and websockets, and it's on PyPI if you want to try it.
wrk, 30 seconds, 4 threads, 900 connections, local machine.
Two laptops and an Aula F75 Max keyboard. Both run zsh and kitty, one is on Hyprland and the other on i3.
Open to Python and full-stack roles, and happy to talk about systems work.