huntedbytheirs d0ff52a5d7
CI / build-and-test (push) Successful in 42s
test: add 7 tests for kappa bump and bump-all
- 4 tests for kappa bump: version update, version written to file,
  source URL preserved, requires version arg
- 3 tests for kappa bump-all: finds newer version, reports count,
  requires directory arg

Total: 103 → 110 in test-full.sh (30+23+110 = 163 overall)
2026-08-05 07:58:35 -04:00
2026-07-29 19:59:49 -04:00

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 with indexed repos. Declare named repos with channels and mirrors in your config. Kappa fetches index.kap from each repo, caches it, and only re-fetches when the remote changes. Package recipes are resolved from the index — fast, offline-tolerant, and mirror-aware.

repos {
    kappa-os {
        url      = "https://packages.kappa-os.org/"
        channels = ["stable"]
        mirrors  = [
            "https://cdn.kappa-os.org/",
            "https://eu.kappa-os.org/",
        ]
    }
}

remotes = [...] still works. Repos are tried first, then legacy remotes.

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

  • Env operators. Three ways to set build environment variables: = (hard set), += (append with space), ?= (soft set — only if not already defined). System-level env propagates to all packages.

env {
    CFLAGS  = "-O2 -march=native"    # overwrite
    CFLAGS += "-pipe"                # append → "-O2 -march=native -pipe"
    CFLAGS ?= "-g"                   # soft — only if not set
}
  • Conflicts. systemd declares conflicts = ["eudev", "elogind"]. The resolver catches mutual incompatibility before a build starts — and now actually reports it, rather than silently ignoring it.

  • 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

# Add packages to your system config
build/kappa add make
build/kappa add nginx ">=1.24"
build/kappa add zlib

# Rebuild — kappa fetches recipes, resolves deps, builds everything
build/kappa rebuild $KAPPA_ROOT/system/config.kap

# Remove packages
build/kappa remove zlib

No config files to write by hand. kappa add writes the packages {} block for you. kappa rebuild handles the rest.

Subcommands

Command What it does
add <pkg> [version] Add a package to system config
remove <pkg> Remove a package from system config
build <package> Build a single package from its .kap definition
rebuild <config> Diff config against installed state, rebuild changed
resolve <config> Compute a build plan (shows order, deps, conflicts)
doctor <file> Check a file for issues and warnings
fetch <package> Download and verify source tarballs
fetch-package <name> Fetch a package recipe from remotes
format <file> Pretty-print to canonical style
index <dir> Build an index.kap from .kap files in a directory
list Show installed packages
parse-config <file> Validate a system configuration
parse-package <file> Validate a .kap package definition
rollback Show available generations
validate <file> Validate any kappa file (package, config, or index)

Repo maintenance

# Generate an index from a directory of .kap files
kappa index ./packages/
# → packages/index.kap

# Host the directory behind any HTTP server. That's your repo.

The index is a tiny text file listing every package and version. Clients fetch it once, cache it, and check for updates via HTTP headers.

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. See STYLEGUIDE.md for code conventions.

Tests: 138 integration tests across three suites. CI runs on Arch Linux. Everything passes or nothing merges.

S
Description
A egotistical competitor to the package manager from the ZereneOS project that is better and smarter than Iota. TLDR; The package manager from the NixOS Maintainer's nightmares, or in other words, the package manager from hell. Are we rubbing it in? Yeah? Why wouldn't we?
Readme BSD-3-Clause
462 KiB
Languages
C++ 83.4%
Shell 15.2%
Makefile 0.7%
CMake 0.5%
Dockerfile 0.2%