Files
kappa/README.md
T
huntedbytheirs 7e93db2d07 feat: portability, correctness, and quality improvements for v0.2
Portability (Linux distro-agnostic):
- Remove hardcoded Clang compiler enforcement; GCC now builds
- Add find_package(Threads REQUIRED) for older glibc
- Add cmake install() target
- FHS 3.0 default root: /kappa -> /usr/local/kappa
- fs::path operator/ for all init/bootloader paths (fixes prefix fragility)
- Multi-distro zoneinfo search (FHS, NixOS, Guix, alt)
- Portable tar extraction (drop GNU-only --no-same-permissions)
- Runit enable/disable commands now prefix-aware
- --root CLI flag before/after subcommand, lazy directory creation
- Shebang constants de-duplicated to types.hpp

Correctness (race conditions, UB, corruption):
- Fix CWD race in scheduler: per-child chdir() instead of process-global
- Fix UB const_cast in exec_cmd/exec_capture: mutable argv buffers
- Fix non-atomic installed DB writes: tmp+rename pattern
- Fix read_file() no longer calls exit(1), throws instead
- Fix silent catch(...) parse errors now print diagnostics
- Fix rebuild false positives with config_hash change detection
- Fix s6 disable_cmd copy-paste bug (was identical to enable)
- Fix runit enable_cmd incomplete, disable_cmd wrong target
- Fix dinit env vars: functional env-file + companion .env

Quality:
- Add -Wall -Wextra -Wpedantic to CMake, fix 2 pre-existing warnings
- Move parse_int from error.hpp to parse_util.hpp
- Fix hash verification guard checks all three hash types
- Check patch return code in fetch.cpp
- Add explicit system_dir creation in ensure_directories()
- Add resolve to needs_dirs for build_registry() consistency
- Update stale /kappa path references in examples
- Remove inaccurate -Werror claim in CONTRIBUTING.md
- Add build-gcc/ and agent dirs to .gitignore
- Suppress clang-tidy portability-avoid-pragma-once
- Fix .gitignore /kappa pattern (was matching include/kappa/)
- Delete stale vcpkg_installed/ directory

54/54 tests pass. Builds on Clang and GCC with 0 warnings.
2026-07-31 08:28:49 -04:00

5.3 KiB

kappa mascot

kappa

Anywhere, any init, anytime.

A declarative, source-based package manager that doesn't care what init system you run. Or what bootloader. Or what CPU architecture. Kappa builds your entire system from source — and lets you swap the init system like you'd swap a wallpaper.


Why

Every other package manager picked a side. apt married systemd. pacman shackled itself to Arch's ecosystem. emerge gave you choice but at the cost of your weekend. Nix gave you reproducibility but took your filesystem with it.

Kappa is what you get when you stop negotiating. You declare what your system is, and kappa figures out how to build it. Change your mind about the init system? Rebuild only the packages that care — the other 800 stay put.

What it does

# Your system, in one file:
boot {
    kernel     = "linux"
    init       = "s6"          # swap to "systemd" anytime
    bootloader = "limine"      # or "grub"
    root       = "/dev/sda1"
}

packages {
    nginx     { version = ">=1.24" }
    postgresql {}
    zlib      {}
}

services {
    nginx { enable = true }
}

Then:

kappa rebuild config.kap   # builds everything, generates service files
kappa rebuild config.kap   # boot.init = "openrc" — only 5 packages actually rebuild

Features that nobody else has

  • Init-system-as-configuration. boot.init = "s6" → generates s6 service directories. Change it to "systemd" → regenerates .service units. Change it to "openrc" → generates init.d scripts. The package definitions don't know or care which init you picked. That's kappa's problem.

  • Post-install init switching. Change boot.init, run kappa rebuild, reboot. You're now on a different init system. Only packages that actually use ${enabledinit} in their build scripts need recompiling. Everything else just gets new service files generated.

  • Bootloader rollback. Every rebuild creates a fallback boot entry pointing at the previous generation's init. If the new one doesn't boot, the old one is one reboot away.

  • Parallel scheduler. -w 4 -j 8 means four packages building simultaneously, eight jobs each. The scheduler uses depth-based priority grouping so leaf dependencies unblock as much work as possible first.

  • Package recipe caching. Declare remotes = ["https://repo.example.com/"] in your config. Kappa fetches .kap files on demand, caches them, and only re-fetches when the remote version is newer.

  • Source tarball caching. Downloaded once, stored at $KAPPA_ROOT/cache/ (default: /usr/local/kappa/cache/). Rebuilds don't touch the network unless versions change.

  • Conflicts. systemd declares conflicts = ["eudev", "elogind"]. The resolver catches mutual incompatibility before a build starts.

  • Init-agnostic system config. groups { wheel { gid = 998 } } — kappa creates the groups. system { hostname = "mybox" } — kappa writes /etc/hostname. No systemctl, no rc-update, no init dependency.

5 init systems. 2 bootloaders. Zero lock-in.

Init Service location Enable command
systemd /etc/systemd/system/{name}.service systemctl enable
openrc /etc/init.d/{name} rc-update add
s6 /etc/s6/sv/{name}/run s6-rc-bundle-update
runit /etc/sv/{name}/run ln -sf /etc/sv/{name} /var/service/
dinit /etc/dinit.d/{name} dinitctl enable
Bootloader Config path
limine /boot/limine.cfg
grub /boot/grub/grub.cfg

Quick start

Note for 0.1.x users: The default store root has moved from /kappa to /usr/local/kappa (FHS 3.0). Set KAPPA_ROOT to your existing /kappa directory to keep using the old location.

# Build kappa (needs Clang 17+, CMake 3.20+, C++23)
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
cmake --build build

# Write a config
cat > system.kap << 'EOF'
system { hostname = "kappa.local" }
packages { nginx {} }
services { nginx { enable = true } }
boot {
    kernel = "linux"; init = "s6"; root = "/dev/sda1"; bootloader = "limine"
}
users { root { shell = "/bin/zsh" } }
remotes = ["https://packages.kappa-os.org/stable/"]
EOF

# Parse it
build/kappa parse-config system.kap

# Rebuild
build/kappa rebuild system.kap

Subcommands

Command What it does
parse-package <file> Validate a .kap package definition
parse-config <file> Validate a system configuration
validate <file> Validate any kappa file
format <file> Pretty-print to canonical style
doctor <file> Check for issues and warnings
resolve <config> Compute a build plan
fetch <package> Download and verify source tarballs
fetch-package <name> Fetch a package recipe from remotes
build <package> Build a single package
rebuild <config> Diff config against installed state, rebuild changed
list Show installed packages
rollback Show available generations

License

BSD 2-Clause. Do whatever you want. Just don't sue us.

Contributing

See CONTRIBUTING.md. We're opinionated but we merge good code.