Files
huntedbytheirs 5607be0f1c
CI / build-and-test (push) Successful in 37s
docs: update README and CONTRIBUTING for v0.2 features
README:
- Quick Start shows kappa add workflow instead of manual configs
- New features: indexed repos, env operators (+=, ?=), conflict reporting
- Updated subcommand table (add, remove, index, validate-index)
- Repo maintenance section (kappa index)
- Link to STYLEGUIDE.md, test counts, CI info

CONTRIBUTING:
- Updated test counts (30+23+85 instead of 31+23)
- Added test-full.sh to mandatory test suite
- Referenced STYLEGUIDE.md in PR requirements
- Updated good-first/ambitious issues for current state
2026-08-04 14:40:24 -04:00

204 lines
7.0 KiB
Markdown

<p align="center">
<img src="https://git.spectoria.dev/repo-avatars/f111d66d4a9972c13eb8aa4dddc7fe2c39aabdf0ab0ff0dc27a31a9aa1c2e968" width="180" alt="kappa mascot" />
</p>
# 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:
```sh
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.
```kap
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.
```kap
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.
```sh
# 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
```sh
# 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](CONTRIBUTING.md). We're opinionated but we merge good
code. See [STYLEGUIDE.md](STYLEGUIDE.md) for code conventions.
Tests: 138 integration tests across three suites. CI runs on Arch Linux.
Everything passes or nothing merges.