Closes the OOTB config gaps from the kitty swap (b4c86b6). Kitty installed but
shipped with bare defaults and no tmux passthrough; rescue path lost its
headless fallback when xfce4-terminal was removed.
Changes:
- panel-skel/.config/kitty/kitty.conf: font 13pt (Hack, from hack-font pkg),
quiet-dark theme + full 16-color palette, scrollback, tab bar, beam cursor.
Lands at ~/.config/kitty/kitty.conf for the clawdie user via existing
panel-skel staging (no build.sh change).
- panel-skel/.config/tmux/tmux.conf: extended-keys on + csi-u so modifier
keys (Shift-Enter, Ctrl-Arrow, Alt-Enter) reach TUIs (pi, zot, colibri-tui)
run inside tmux. XDG path (~/.config/tmux/tmux.conf), read natively by the
live USB's tmux 3.5a. Plus escape-time 0, focus-events, mouse, sane indexing.
- clawdie-xfce-session-inner: rescue terminal now falls back kitty -> xterm.
Kitty is GPU-only; without a GL surface (bhyve no-GPU VMs) it can fail to
start, leaving the operator with no rescue shell. xterm stays installed.
No build.sh change needed: both configs ride the existing
`cp -R panel-skel/.config/.` into /etc/skel/.config and /home/clawdie/.config
(build.sh:2087, :2095).
Pre-merge: confirm `pkg search kitty` on the FreeBSD build host (couldn't
reach the network from the dev sandbox).
(Sam & Claude)
Step 4 of clawdie-enable-mother.sh rewrote provider.env in place with
`cat "$tmp" >"$f"`, which truncates the live secrets file before
streaming the new content. A crash, signal, disk-full, or concurrent
read during that window leaves provider.env empty or partial — and the
colibri_daemon prestart sources it for the provider key + BW_* creds.
Switch to the write-temp-then-rename pattern already used for
external-mcp.json in step 3: mktemp in provider.env's own directory (so
the rename stays on one filesystem and is atomic), chmod 0600 before the
swap, then mv. A reader now always sees a complete file, old or new.
Also add jq to the disk-install extras so disk-deployed hosts can run
the script post-deploy (step 3 hard-requires jq; the live image already
ships it).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build.cfg: FREEBSD_VERSION -> ${FREEBSD_VERSION:-15.1-RELEASE}. The memstick
URL, checksum URL, cache path, and build-manifest all derive from it, so the
live-USB build bumps from this one line. The :- form also lets an operator
override at build time (FREEBSD_VERSION=15.2-RELEASE ./build.sh) without
editing git — previously a plain assignment clobbered any env value.
- docs: de-version README/BUILD/REQUIREMENTS + iso-build skill from '15.0' to
'15.x' so they stop drifting on every point release.
Verified: default derives the 15.1 memstick URL (HTTP 200, dated 12.jun.2026);
env override cascades to 15.2. ABI stays FreeBSD:15:amd64 (same major — no
package rebuild needed). build-vps.sh (mfsbsd) and poudriere keep their own
version knobs and are intentionally untouched (ABI-compatible, separate paths).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the fetch-skip flags to a consistent, descriptive scheme:
--skip-fetch skip BOTH fetches (packages + memstick) — assemble from cache
--skip-fetch-pkg skip only the package + Clawdie-AI fetch (new granular)
--skip-fetch-memstick skip only the FreeBSD memstick fetch (was --skip-memstick-fetch)
Internals split SKIP_FETCH into SKIP_PKG_FETCH + SKIP_MEMSTICK_FETCH; the umbrella
--skip-fetch sets both. The memstick step collapses to a clean skip-or-fetch (the
old three-branch form only existed to couple the pkg-skip flag to memstick reuse,
which the split removes). No legacy alias kept — the flag names state the current
way directly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The rc.conf.sample on the live USB now sets require_secured=YES.
Together with the paired colibri change, this ensures the daemon
disables autospawn until the console gate writes .secured.
clawdie-iso half of the .secured interlock:
- build.sh writes colibri_daemon_require_secured="YES" to the operator image's
rc.conf. Opt-in so DEPLOYED colibri hosts (shared colibri_daemon.in via the
FreeBSD port, no firstboot gate) are unaffected — they never set this knob.
- gate skip message upgraded to 'agent will NOT start or register until secured'.
Depends on the colibri-side consumer (colibri_daemon.in prestart): when
colibri_daemon_require_secured is YES and /var/db/colibri/.secured is absent,
export COLIBRI_AUTOSPAWN=NO (after the provider.env source block). Tracked as the
colibri follow-up; both must ship in the same 0.12 image for the message to hold.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The '.secured' marker is written but not yet consumed by colibri, so the gate
must not imply colibri/zot are blocked. Reword the skip message to state the
node is UNSECURED and the agent SHOULD NOT register/run while unsecured — true
as a policy statement, without claiming enforcement we haven't built. Upgrade to
'will not' once the colibri .secured interlock lands.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the silent trailing sleeps with a counting-down message so the operator
sees the result (secured / skipped) and a clear cue before clawdie_live_gpu
repaints the screen. Same ~3s pause, now visible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reorder the gate to REQUIRE: FILESYSTEMS devfs / BEFORE: clawdie_live_gpu LOGIN
so it runs on the plain early boot text console, before clawdie_live_gpu does its
KMS/framebuffer mode-switch. That removes the console-flush race entirely, so the
sleep 1 + screen-clear workaround is gone. Still before LOGIN, hence before sddm
and colibri_daemon (race-free property preserved).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds clawdie_firstboot_rootpw, an rc.d gate ordered BEFORE sddm and
colibri_daemon. On the text console (operator present at first boot) it runs a
15s countdown to engage; if engaged it forces a root AND operator (clawdie)
password, echo-off, applied via 'pw usermod -h 0' over stdin (secret never in
argv/ps, never near the agent). Idempotent via a persistent success marker
/var/db/colibri/.secured (/var persists: varmfs=NO). Skipping leaves the node
open and re-prompts next boot — never bricks an unattended/headless boot.
Running before the daemon means the security decision is always made before any
agent can autospawn/node_register, so no cross-component interlock is needed
(rc ordering replaces it). The .secured marker is also the signal a future
colibri change can read to label an unsecured node to mother.
Tests: tests/firstboot-rootpw-test.sh proves marker skip, password validation,
and that the secret is delivered on stdin and NEVER appears in argv (10/10).
Console interactivity (read -t countdown, stty echo-off on /dev/console) must be
verified by booting on osa/bhyve before merge.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Layer 0 (seed-import-test.sh): runs the real clawdie-live-seed importer in a
sandbox (CLAWDIE_SEED_TEST=1, all paths overridden) and asserts the seed->runtime
propagation contract — env split, provider.env, dual-home ssh, soul staging, and
AGENTS.md -> $ZOT_HOME (the global slot the autospawned zot reads). Idempotent
re-import is checked. REQUIRE_AGENTS_MD=1 enforces the AGENTS.md install added on
this branch; it passes 23/23 here.
Layer 2 (mcp-boundary-test.sh): exercises the mother MCP-over-SSH boundary on
Linux — colibri-mcp-ssh forced-command allowlist (""/"tools" route, everything
else rejected) and the MCP tools/list handshake, including a real loopback sshd
with command=. Skips cleanly when colibri isn't a sibling checkout.
Verified end to end on Linux before merge: importer target path and the ZOT_HOME
pin in colibri (fix/zot-home) both resolve to /var/db/colibri/.local/state/zot.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A. ZOT_VERSION drift: build.cfg defaulted to v0.2.42 while build.sh
preflight hint said v0.2.47. Now both default to v0.2.47.
B. AGENTS.md hw-probe phrasing: told zot to run clawdie-hw-probe,
but the daemon already collects it into CLAWDIE_HW_PROFILE at
autospawn time. zot should read the env var, not shell out.
C. RPC_PROMPT missing: COLIBRI_AUTOSPAWN=YES starts zot in RPC mode,
but without RPC_PROMPT, zot blocks on stdin and idles. Added
a prompt telling zot to read CLAWDIE_HW_PROFILE, call node_register
on mother, and report its assigned capabilities.
Adds seed/ directory with:
- AGENTS.md: zot operational rules (mother, verbs, capabilities)
- harness.toml: harness="zot", model="deepseek-v4-pro"
- env.placeholder: template for API key injection
build.sh seed population step reads provider keys from the build host's
/usr/local/etc/colibri/provider.env and writes them to the seed partition's
env file. Also installs AGENTS.md, harness.toml, and the layered-soul backup.
Keys are NEVER committed — only placeholders. Real keys are injected at build
time from the build host's provider.env (DEEPSEEK_API_KEY, OPENROUTER_API_KEY).
The seed importer already dual-writes SSH material to the daemon home.
Adds an AGENTS.md install block targeting /var/db/colibri/.local/state/zot/ —
the path pinned by colibri_daemon.in's ZOT_HOME export. Zot reads this as
its first AGENTS.md source, giving the autospawned agent operational rules
(mother, verbs, capabilities) on first boot.
The preflight checkout hint defaulted to v0.2.42 while the FreeBSD
build-lane handoff still named v0.2.29. Pin both to the current latest
zot tag (v0.2.47) so every reference the FreeBSD agent follows agrees,
and a release build targets one fixed agent version.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convert US/ISO prose dates to EU format across iso docs (CHANGELOG, plans,
handoffs, wiki-linked docs). Left as-is (data, not prose): the sample log lines
in FIRSTBOOT.md and the ADMIN-PANEL.md UI mockup (timestamps/snapshot names);
ISO is correct for machine output.
Markdown format gate clean.
Three blockers fixed from review of fix/ootb-mother-mcp:
1. Real Tailscale IP removed from image/repo.
- external-mcp.json uses "mother" host alias (resolved by SSH config).
- Key path: /var/db/colibri/.ssh/mother-mcp (daemon user home).
- The real IP lives only on the offline seed (ssh/config), never in
the repo or the shipped image.
2. Cross-user key access fixed.
- The daemon runs as colibri (home /var/db/colibri), not clawdie.
- Seed importer now installs SSH material to both clawdie AND
colibri homes (same seed material, same key, separate ~/.ssh).
- build.sh dev convenience also copies to both homes.
- clawdie-live-seed.README.txt already documents the seed layout.
3. Doc fully de-obfuscated.
- All m0th3r/c0l1br1/n0d3_r3g1st3r → mother/colibri/node_register.
- All real IPs → <mother-tailscale-ip> placeholder.
- Removed Step 2 (manual external MCP) + Step 3 (register) — both
are now baked into the ISO.
- Removed trailing "colibri-mcp" remote command from examples
(hardened wrapper rejects non-allowlisted commands).
Two changes so the USB connects to mother on first boot with no manual steps:
1. stage-colibri-iso.sh: external-mcp.json is now pre-configured with the
mother server entry (colibri@100.72.229.63, no remote command — the
hardened wrapper starts colibri-mcp in stdio MCP mode). Previously
staged as empty {}; the operator had to create it manually or run
clawdie-enable-mother.
2. provider.env now includes COLIBRI_MCP_EXTERNAL_CALL=1 by default
(already set on osa; missing from the ISO defaults).
3. SETUP-USB-TO-MOTHER.md: removed Step 3 (manual external-mcp.json),
fixed the diagram to match the hardened wrapper (no remote command),
corrected the server name from "m0th3r"/"c0l1br1" to the real names.
The SSH key, config, and known_hosts still come from the CLAWDIESEED
seed partition — the image carries no secrets. Without the seed the
connection fails gracefully.
README said 'no separate feature flag', but the build keeps FEATURE_COLIBRI
(build.cfg default YES) as an internal escape hatch (e.g. building without a
colibri checkout). Clarify: colibri is staged by default; FEATURE_COLIBRI=NO is
an internal build-time hatch, not a user-facing option. Resolves a wiki-ledger
residue item.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The mother MCP scripts were copied into clawdie-iso (packaging/mother/) AND
colibri. The iso copies drifted: node-register-mcp on iso main was the old,
SQL-injectable version (E'${HOST_ESCAPED}' string interpolation) using
usb_nodes — while colibri #161 carries the reviewed, parameterized (psql -v
:'var') hive_nodes version.
One canonical home: colibri. Remove packaging/mother/ from the iso (nothing in
the iso build references it), redirect the two doc path references to the colibri
repo, and align the docs to hive_nodes (matching the colibri schema rename).
Supersedes #127 (which only renamed docs and conflicted after the iso copies
landed). Doc-only + file removals; markdown gate green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New packaging/mother/node-register-mcp accepts JSON-RPC tools/call,
inserts hw_profile into mother_hive.usb_nodes, and returns the row
with auto-derived capabilities (derive_capabilities trigger fires).
Requires one-time PostgreSQL setup on mother:
CREATE ROLE colibri WITH LOGIN;
GRANT CONNECT ON DATABASE mother_hive TO colibri;
GRANT INSERT, UPDATE ON usb_nodes TO colibri;
GRANT USAGE ON SEQUENCE usb_nodes_id_seq TO colibri;
Also updates docs to reflect 0.12 daemon behavior: hw-probe is
collected by the daemon (not the agent) and passed via CLAWDIE_HW_PROFILE
env var. COLIBRI_AUTOSPAWN_ARGS default is binary-dependent (zot->rpc,
others->--mode json).
The image shipped a hard pin (@earendil-works/pi-coding-agent@0.78.0) while
'pi upgrade' on hosts had moved to 0.80.2, so builds lagged. Switch Pi to the
@latest dist-tag so every image bundles the newest Pi.
To keep the floating spec traceable, record the version that actually got
fetched in build-manifest.json as pi_version, derived from the bundled tarball
name (earendil-works-pi-coding-agent-<version>.tgz) after fetch+install.
fetch-npm-globals.sh now also echoes the resolved tarball so the build log
shows the version a dist-tag resolved to.
Other globals (bw) stay pinned. Image is node24, compatible with current Pi
(the legacy-node20 dist-tag is for node20 only).
Verified: fetch resolves @latest → 0.80.2; version extraction matches npm.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>