The repo is indexed with cocoindex-code; semantic search beats grep for "where is X / which bundle does Y" questions where you don't know the exact identifier. Without `--path '**'` ccc scopes to the current working directory, which is rarely what you want when navigating ckn-bw — call it out so agents don't get confusing empty results.
6.5 KiB
6.5 KiB
ckn-bw — agent & contributor guide
What this repo is
A BundleWrap configuration-management repo
for ~22 personal/family-infra nodes. Nodes, groups, and bundles are
defined in plain Python; bw apply deploys the resulting state to
real machines.
Note: the root README.md is the maintainer's personal scratchpad,
not project documentation. Onboarding lives here, in AGENTS.md.
Quickstart for agents
Six rules; follow these and you won't break things:
- Read-only by default. Never run
bw apply,bw run, orbw lockwithout explicit user request — even with-i. Stick tobw test,bw nodes,bw groups,bw items,bw metadata,bw hash,bw verify,bw debug. Seedocs/agents/commands.mdand the fork's safety envelope. - Never echo decrypted secrets. Don't print, paste, or log the
value behind a
!password_for:,!decrypt:, or!32_random_bytes_as_base64_for:magic string — not even frombw debugexploration. Seeconventions.md#secrets. - Don't touch the do-not-modify list.
.secrets.cfg*,.venv,.cache,.bw_debug_history,.envrc, rootREADME.md. Treathooks/anditems/(custom item types) with extra care: a broken hook or item type breaks everybwcommand repo-wide. - Use the fork. The venv runs editable from
github.com/CroneKorkN/bundlewrap(branchmain). Behavior tracks upstreammain; the fork'sAGENTS.mdis the canonical bundlewrap-language reference. Seeconventions.md#bundlewrap-version. - Prefer adding helpers to
libs/over duplicating logic across bundles. Repo-wide helpers go inlibs/, reachable asrepo.libs.<x>. - Search semantically. This repo is indexed with
ccc— tryccc search '<concept>' --path '**'beforegrepfor "where is X / which bundle does Y" questions. Without--path '**', results are filtered to the current working directory's subtree.
Layout
| Dir | What's there |
|---|---|
bundles/ |
103 bundles. One subdir per bundle (items.py, metadata.py, files/). |
nodes/ |
One file per node (~22). eval()-loaded; demagified through repo.vault. |
groups/ |
Group definitions, organized by axis (applications/, locations/, machine/, os/). |
libs/ |
Shared Python helpers reachable as repo.libs.<modulename>. |
hooks/ |
bw lifecycle hooks (apply_start, test, node_apply_start, …). |
data/ |
Out-of-bundle data assets (apt keys, grafana dashboards, …). |
items/ |
Custom item types (currently download:). |
bin/ |
Operator scripts; not invoked by bundlewrap. |
docs/agents/ |
Repo conventions and command deltas. |
How nodes, groups, and bundles fit together
- A node (
nodes/<location>.<role>.py) declares the groups it belongs to and any node-local bundles + metadata overrides. - A group (
groups/<axis>/<x>.py) attaches bundles and shared metadata to its members. Groups inherit viasupergroups. - A bundle (
bundles/<x>/) is one chunk of configuration:items.pyproduces the items (files, services, packages),metadata.pydeclaresdefaultsand@metadata_reactorfunctions that derive metadata from other metadata. - The repo-root loaders (
nodes.py,groups.py) walk these dirs andeval()each file.nodes.pyadditionally demagifies the result, resolving!password_for:etc. throughrepo.vault. Seeconventions.md#eval-loaded-node-and-group-filesfor the constraints this places on editors. - Metadata merges along:
all → location → os → machine → applications → node.
Conventions you must know
| Topic | Where |
|---|---|
| Bundlewrap-language reference (item types, dep keywords, reactors) | Fork's AGENTS.md — read first if new to bundlewrap |
| Vault / demagify magic strings | conventions.md#secrets |
| Bundlewrap install (editable from the fork) | conventions.md#bundlewrap-version |
| Group inheritance order, naming patterns | conventions.md#group-inheritance-order, #naming-conventions |
| Repo-specific bw command deltas (apt keys, suspended nodes, vault echo) | commands.md |
| Lib helpers | top-of-file docstrings in libs/*.py (head -1 libs/*.py) |
Suspension idioms (*.py_, _old/, "for now") |
conventions.md#suspension-and-soft-delete-idioms |
Where to look for examples
When writing a new bundle, copy patterns from one that already does the thing you need:
| Pattern | Look at |
|---|---|
| Vault calls inside metadata reactors | bundles/dm-crypt/metadata.py (compact, focused) |
| Mako-templated files | bundles/bind/items.py (DNS zonefile rendering) |
| Cross-bundle reactor writing | bundles/nextcloud/metadata.py (writes into apt.packages, archive.paths) |
Custom download: items |
bundles/minecraft/items.py |
| Node file (single-purpose) | nodes/home.server.py |
Group with supergroups chain |
groups/os/debian-13.py |
Where this doc lives
- This file:
AGENTS.mdat the repo root. CLAUDE.mdis a symlink to this file — both names point to the same content so different tools can find it.- The personal TODO scratchpad (
README.md) is separate and not project documentation.