Files
kappa/CONTRIBUTING.md
T
huntedbytheirs dd984f96d4 feat: system-agnostic package manager — 5 inits, 2 bootloaders, parallel scheduler
Complete rewrite of kappa from a sequential build tool into a
system-agnostic package manager with runtime init switching.

Core additions:
- 5 init system backends: systemd, openrc, s6, runit, dinit
  (service file generation, enable/disable, init_paths)
- 2 bootloader backends: grub, limine (config generation, fallback entries)
- Parallel scheduler with worker pool, depth-based priority (Beta/Alpha/Zeta),
  atomic claiming, dependency tracking, deduplication, and failure propagation
- Init-switch impact analysis: only rebuild packages using ${enabledinit}
- Init-agnostic service definitions: flat NamedService blocks replace
  per-init nesting
- Package conflicts: mutual incompatibility detection in resolver
- System groups: init-agnostic group creation in DSL
- Init-agnostic hostname/timezone: direct /etc/hostname and /etc/localtime writes
- Source tarball caching at /kappa/cache/ with atomic write-then-rename
- Package recipe caching with remote fetching and version comparison
- remotes = [...] block in system config for package repositories
- Auto-fetch: rebuild resolves missing packages from remotes
- uninstall phase in package definitions
- ${enabledinit} eval variable for init-conditional builds
- Shared util module (to_lower, shell_escape)
- 54 integration tests across two shell test suites
- Comprehensive README and CONTRIBUTING guide

Bug fixes from review:
- CRITICAL: Replace std::system() with fork+execvp (command injection)
- CRITICAL: Fix scheduler deadlock on successful completion
- CRITICAL: Fix rebuild init/kernel/bootloader change detection
- HIGH: Fix path traversal via unsanitized package names in cache
- HIGH: Fix TOCTOU race in cache write with atomic rename
- HIGH: Fix formatter dropping remotes/imports blocks
- HIGH: Fix formatter stripping empty-string assert values
- HIGH: Fix formatter non-idempotent output (sorted key iteration)
- HIGH: Populate ${enabledinit} from boot.init in BuildStep
- MEDIUM: Fix data race on non-atomic scheduler stop flag
- MEDIUM: Fix compute_depths() traversal direction
- MEDIUM: Add runit to doctor supported-init warning
- MEDIUM: Extract to_lower/shell_escape to shared kappa::util
- MEDIUM: Consolidate generator declarations in headers
2026-07-31 05:32:57 -04:00

4.4 KiB

Contributing to kappa

We're building a package manager that doesn't care about your init system, your bootloader, or your life choices. If that sounds like your kind of project, keep reading.

The rules

These aren't guidelines. They're the deal.

1. C++23 or don't bother

We compile with Clang, -std=c++23, and -Werror. If your code needs a polyfill for std::format or can't handle designated initializers, it doesn't belong here. The standard library is your only dependency. Zero external C++ libraries. Not even Boost.

// ✓ yes
auto msg = std::format("building {} (depth={})", name, depth);

// ✗ no
auto msg = fmt::format("building {} (depth={})", name, depth);

2. Every new module mirrors the existing structure

include/kappa/{module}/
├── types.hpp       # enums, structs, parse/validate declarations
├── {feature}.hpp   # public interface
src/{module}/
├── types.cpp       # implementations
├── backend_a.cpp   # per-variant generators
├── backend_b.cpp
└── install.cpp     # dispatch + orchestration

If you're adding a feature, look at src/service/ or src/boot/ for the pattern. If your new module doesn't look like those, you're doing it wrong.

3. Never suppress type safety

There is no as any, no @ts-ignore, no reinterpret_cast abuse, and no void* unless you're talking to the kernel. If the type system is fighting you, you're fighting the design. Fix the design.

4. Use the namespace. All of it.

namespace kappa::module {
    // everything goes here
} // namespace kappa::module

No using namespace std; at file scope. No anonymous namespaces for functions that are used across files — extract to util.hpp instead.

5. Thread safety is not optional

The scheduler is multithreaded. If you touch shared state, you own the lock. std::mutex, std::atomic, std::condition_variable — use them correctly or don't use them at all. If you don't know what memory_order means, stay out of the scheduler.

6. Tests are shell scripts. For now.

Integration tests live in test.sh and test-init-switch.sh. They exercise the CLI. If you add a subcommand, add a test. C++ unit tests are welcome — set up Google Test or Catch2 in CMake and we'll merge it.

./test.sh               # 31 tests, must pass
./test-init-switch.sh   # 23 tests, must pass

7. Backward compatibility is mandatory

The .kap DSL is the contract. You can add keywords. You cannot remove them. You can extend syntax. You cannot break existing configs. If your change means someone's config.kap stops parsing, it doesn't ship.

How to contribute

Pick something

Good first issues:

  • Adding a 6th init system backend
  • Adding a 3rd bootloader backend
  • C++ unit test framework setup
  • Shell completion scripts

Ambitious issues:

  • Binary package support (pre-built caches)
  • Remote build farm (distcc-style)
  • Signed package verification
  • Filesystem overlay activation (like Nix profiles)

Send a PR

  1. Fork the repo
  2. Create a branch: feat/my-thing or fix/my-bug
  3. Write code that follows the rules above
  4. Run ./test.sh && ./test-init-switch.sh — both must pass
  5. Open a PR against main

PR requirements

  • Build must pass: cmake --build build with zero warnings
  • Tests must pass: both shell test suites
  • No commented-out code. No dead code. No TODO without a date.
  • Commit messages in imperative: Add runit backend not Added runit backend

What we won't merge

  • systemd-only features. If it can't work on at least two init systems, it goes in a systemd package definition, not in kappa.
  • Dependency on a specific distro. Kappa runs on any Linux kernel. No hardcoded paths to /usr/lib/systemd, no assumptions about /etc/os-release.
  • Abstract nonsense. FactoryFactoryBuilder patterns. Premature generalization. If you need three layers of indirection to add a feature, the feature is too complicated.
  • AI slop. If it looks like ChatGPT wrote it, it gets rejected. We can tell. Write code like a human who's been doing this for a decade.

Communication

We don't have a Discord. We don't have a forum. Open an issue. Write a clear title, a reproduction case, and what you expected. We'll respond when we respond.

If you want to propose a major feature, open an issue first. Surprise PRs that rewrite half the codebase get closed without review.


Kappa is 0.1.0. Everything is subject to change except the rules above.