Commit graph

433 commits

Author SHA1 Message Date
bb5460427d docs: drop sudo from flash commands, append sync
All decompress-and-write one-liners now share the same form:
  xz -dc ...img.xz | of=/dev/sdX bs=4M status=progress conv=fsync && sync

- sudo removed (operator runs as root on USB stick)
- && sync appended to all image-write commands
- /dev/zero wipe commands unchanged
- build.sh echo updated to match
2026-06-23 06:41:58 +02:00
af83717524 Merge pull request 'feat(seed): outbound SSH client material for hands-free node→mother' (#115) from seed-ssh-client-material into main
Reviewed-on: #115
2026-06-22 20:26:07 +02:00
800658b47c docs: drop sudo from flash commands, append sync
All decompress-and-write one-liners now share the same form:
  xz -dc ...img.xz | of=/dev/sdX bs=4M status=progress conv=fsync && sync

- sudo removed (operator runs as root on USB stick)
- && sync appended to all image-write commands
- /dev/zero wipe commands unchanged
- build.sh echo updated to match
2026-06-22 20:24:25 +02:00
862af0583b feat(seed): outbound SSH client material for hands-free node->mother
The baked mother key (build/mother-ssh-key) puts a private key in the image,
which only works for a non-published personalized stick. The offline FAT32
seed is the correct home for per-node secrets.

Teach the importer to install outbound SSH client material from an agent's
ssh/ dir into the agent home:
  - config       -> ~/.ssh/config       (0600)
  - known_hosts* -> ~/.ssh/known_hosts* (0644, merged + de-duped)
  - <name>.pub   -> ~/.ssh/<name>.pub   (0644)
  - <name>       -> ~/.ssh/<name>        (0600, any other file = private key)
authorized_keys stays inbound-only via _seed_install_authorized_keys.

This closes the 'without manual key exchange' gap: known_hosts pins mother's
host key so the first node->mother connect does not prompt, and the private
client key rides on the offline seed instead of the base image — so the
published image stays secret-free. Supersedes the baked-key path (#112),
which can retire once this is validated on hardware.

Verified offline (CLAWDIE_SEED_TEST): correct perms (key 0600, pub/known_hosts
0644, config 0600, .ssh 0700) and idempotent known_hosts merge across re-runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:55:56 +02:00
48418af783 Merge pull request 'build: fail-fast release gate for baked mother SSH key' (#114) from release-gate-mother-key into main
Reviewed-on: #114
2026-06-22 09:54:20 +02:00
80dcbfef2e build: fail-fast release gate for baked mother SSH key
The image-assembly guard (build/mother-ssh-key, #113) refuses to copy the
mother key into a release image, but only after a full build run. Add the
same check to check_release_gate so a BUILD_CHANNEL=release build with the
key present on the host aborts in seconds, not after fetch/build/assemble.

The assembly-time guard stays as defense in depth.

(BUILD_CHANNEL already defaults to dev in build.cfg:17, so no change needed
there.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:53:36 +02:00
72491ee3b8 Merge pull request 'build: refuse to bake mother SSH key into release images' (#113) from build/mother-ssh-key into main
Reviewed-on: #113
2026-06-22 09:49:14 +02:00
b489d147d4 build: refuse to bake mother SSH key into release images
The trigger copies osa-mother-2026 from the build host into any ISO
as long as the key file exists (which it does permanently on OSA).
A BUILD_CHANNEL=release build would embed the private key into a
publicly hosted image = mother compromise.

Add a fail-closed guard: release builds exit with an error before
copying the key. Dev builds (including personalized sticks) are
unaffected.
2026-06-22 09:42:00 +02:00
9f75767c63 Merge pull request 'build: pre-stage mother SSH key for USB→osa connectivity' (#112) from build/mother-ssh-key into main
Reviewed-on: #112
2026-06-22 09:32:42 +02:00
4addf6fa89 build: pre-stage mother SSH key for USB→osa connectivity
Copies /home/clawdie/.ssh/osa-mother-2026 (ed25519) from the build
host into the ISO at /home/clawdie/.ssh/osa-mother-2026 (0600).
Public key is in mother's authorized_keys. Lets the live USB node
SSH straight into the mother server without manual key exchange.

Skipped silently if the key file doesn't exist on the build host.
2026-06-22 09:30:15 +02:00
d39e47f1e5 Merge pull request 'feat(seed): zero-touch boot from a personalized seed (provider keys → provider.env)' (#110) from seed-zero-touch-provisioning into main
Reviewed-on: #110
2026-06-22 08:57:46 +02:00
ba2f09f290 feat(seed): route seeded provider keys to provider.env for zero-touch boot
The live seed importer merged the active agent's provider keys into the
operator ~/.env, but colibri_daemon reads /usr/local/etc/colibri/provider.env
(rc.conf colibri_daemon_provider_env). So a personalized seed carrying real
provider keys never reached the daemon and no agent auto-spawned.

Route the active agent's non-BW_* keys into provider.env (0600 root) in
addition to ~/.env. The importer runs as root BEFORE LOGIN and colibri_daemon
REQUIREs LOGIN, so the daemon starts after the keys land and auto-spawns the
agent on first boot — no Join Hive click, no Vaultwarden round-trip, no typing.

This makes a personalized seed the zero-touch onboarding primitive: the image
stays generic/publishable, the FAT32 seed is the (offline) personalization
layer. BW_* still route to vault-bootstrap.env for the vault-fetch path.

Docs: seed README, START-HERE, and ONBOARDING-SIMPLIFICATION updated to
describe the direct-keys path (supersedes the xdg-autostart plan).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 08:56:46 +02:00
343ba35536 Merge pull request 'docs: onboarding simplification + drop clawdie-ai source snapshot' (#109) from docs/onboarding-simplification into main 2026-06-22 08:09:54 +02:00
addf43056e docs: prettier-format ONBOARDING-SIMPLIFICATION
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 08:09:44 +02:00
143ff02f8f build: remove clawdie-ai source snapshot from ISO staging
clawdie-ai (TypeScript) is being phased out in favor of the colibri
(Rust) control plane. Remove its shallow git checkout from
/home/clawdie/ai/ on the ISO. The build manifest still records
clawdie-ai provenance; skills import and release gate checks are
unaffected — this only drops the source snapshot.

Also update the ai/README.txt to note the phase-out.
2026-06-22 08:05:26 +02:00
9a159b8593 docs: onboarding simplification — seed partition zero-touch vs zot extensions
Documents the proven end-to-end chain: seed importer (rc.d BEFORE LOGIN)
→ BW creds in provider.env → clawdie-vault-fetch → colibri_daemon restart.
The only remaining click is the 'Join Hive' desktop launcher; the seed
partition already reduces onboarding from 3 typed secrets to one
double-click. True zero-touch requires ~30 lines of shell (xdg autostart).

Also records the zot extensions verdict: onboarding panel rejected
(chicken-and-egg), guard deferred (zot is not OOTB runtime), MCP bridge
kept as the first extension worth building (gated on colibri#143).
2026-06-22 07:51:13 +02:00
7fd49363d2 Merge pull request 'prepare-0.11.0-publish-docs' (#108) from prepare-0.11.0-publish-docs into main
Reviewed-on: #108
2026-06-22 07:29:14 +02:00
ff36ef1800 Merge branch 'main' into prepare-0.11.0-publish-docs 2026-06-22 07:28:18 +02:00
babe68e461 Merge pull request 'build: pin zot v0.2.42 + sync release runbook to 0.11.0' (#105) from update-zot-pin-and-release-runbook into main
Reviewed-on: #105
2026-06-22 07:27:35 +02:00
2d000f06e7 Merge pull request 'chore(zot): pin operator image to zot v0.2.42 (Sam & Pi)' (#107) from chore/zot-0.2.42-pin into main
Reviewed-on: #107
2026-06-22 07:27:08 +02:00
ccd13e4506 docs: prep 0.11.0 publish — artifact names, download URLs, CHANGELOG
Stages the publish-time doc bump from 0.10.0 to 0.11.0: artifact filenames and
osa download/verify URLs (FLASHING, README, TESTING, BUILD, iso-publish skill),
the ISO product-version claims (README, BUILD), and enriches the existing
CHANGELOG [0.11.0] entry with this cycle's operator-facing ISO merges
(Join Hive vault provisioning, Tailscale auto-join, Mother MCP link, jq).

Left untouched: --clawdie-version examples (clawdie-ai namespace) and the
[0.10.0] CHANGELOG history. HOLD until the 0.11.0 image is built + hosted —
the download URLs 404 until then.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 06:33:25 +02:00
67c8b9ee6d docs: sync release runbook to 0.11.0 / zot v0.2.42
build.cfg already targets ISO_VERSION 0.11.0; the runbook still said 0.10.0.
Scoped to the release-cutting doc only — download-URL docs (FLASHING/README/
TESTING) stay at 0.10.0 until 0.11.0 is actually published, and CHANGELOG
history is untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 06:27:38 +02:00
ec89eb58bd build: pin zot v0.2.29 -> v0.2.42
Bumps the zot agent tag the image ships, consistently across build.cfg,
the preflight hint (build.sh), the staging hint (stage-zot-iso.sh), and the
live-rebuild doc. Continues work started by Codex (chore/zot-0.2.42-pin).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 06:27:38 +02:00
2af8a65cae chore(zot): pin operator image to zot v0.2.42 (Sam & Pi)
Update the ISO default Zot pin and build docs from v0.2.29 to v0.2.42 so the next image stages the current rebuilt FreeBSD zot binary instead of recording a mismatched checkout/binary pair. Also refresh the release runbook's 0.11.0 examples.\n\nValidation: ./scripts/check-format.sh; sh -n build.sh scripts/stage-zot-iso.sh; BUILD_CHANNEL=dev build.cfg default check; git diff --check.
2026-06-22 06:08:42 +02:00
90de50f5e9 Merge pull request 'docs(stage): guardrail — zot needs the rpc driver before autospawn' (#104) from docs/zot-autospawn-guardrail into main 2026-06-21 22:31:53 +02:00
871b04ed7a docs(stage): cite colibri#143 in the zot autospawn guardrail
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 22:31:19 +02:00
eb919a435f docs(stage): guardrail comment — zot needs rpc driver before autospawn
The spawner uses stdin(Stdio::null()); zot's --json and rpc modes
both require input. Pi's --mode json is autonomous. Document the
blocker inline so nobody tries the pi→zot config flip without the
driver. Ref: colibri issue zot-rpc-driver + ADR-agent-harness-consolidation.md.
2026-06-21 22:28:41 +02:00
98c1010bc4 Merge pull request 'fix(tailscale): make vault auto-join work on the OOTB operator image' (#103) from fix-tailscale-vault-autojoin-ootb into main
Reviewed-on: #103
2026-06-21 21:52:36 +02:00
6ad3fe5533 fix(tailscale): make vault auto-join work on the OOTB operator image
PR #102 wired the standalone tailscale-auth-key vault item, but the
out-of-the-box path (no baked key) could not actually start the service:

- clawdie-tailscale-up kept required_files=<keyfile>, which onestart still
  enforces; the keyfile is absent on the OOTB image. Removed it — the start
  function already returns 0 when neither provider.env nor the keyfile carries
  a key, so the guard is redundant.
- join-hive called `service ... start`: refused because the service defaults to
  enable=NO without a baked key, and it lacked root. Now `mdo -u root service
  ... onestart` (root + bypass rcvar).
- join-hive's post-join cleanup ran `sed ... provider.env/d` — a stray /d on the
  file path made it error. Dropped it; the rc.d strips the key on success.
- join-hive interpolated the key into `sh -c "..."` argv (visible in ps). Now
  piped via stdin.

Also keep provider.env at 0600 after the rc.d rewrite (it still holds BW_*).

Validated: sh -n on both scripts, ./scripts/check-format.sh clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 21:48:14 +02:00
50629cd7a1 Merge pull request 'feat/tailscale-vault-autojoin' (#102) from feat/tailscale-vault-autojoin into main
Reviewed-on: #102
2026-06-21 21:16:34 +02:00
a62105525f fix(join-hive): add missing /d to sed pattern for TAILSCALE_AUTH_KEY removal
- join-hive.sh: sed -i '' '/^TAILSCALE_AUTH_KEY=*** → .../d' (delete was missing)
- tailscale-up: grep -v pattern aligned to match any value, not literal ***
- Both files pass sh -n
2026-06-21 21:14:50 +02:00
772e32d8bb feat(join-hive): fetch Tailscale auth key from Vaultwarden on boot
Adds step [2b] to join-hive: if bw is available and the node is not
yet on Tailscale, fetch the tailscale-auth-key item from Vaultwarden,
write TAILSCALE_AUTH_KEY to provider.env, and trigger tailscale-up.

- Handles both naming variants (tailscale-auth-key / tailscale_auth_key)
- One-shot: key removed from provider.env after successful join
- tailscale-up now reads from provider.env first, legacy key file as fallback
- Graceful: no vault item → clear message, no break
2026-06-21 21:11:37 +02:00
b1202bb235 Merge pull request 'fix(packages): keep agent jail drift gate parseable (Sam & Pi)' (#101) from fix/post-pull-format-and-mother-sync-notes into main
Reviewed-on: #101
2026-06-21 20:39:04 +02:00
237ab20043 fix(packages): keep agent jail drift gate parseable (Sam & Pi)
Move jq into the agent-jail section so the package list mirrors Colibri's agent-jail-bootstrap baseline, and apply Prettier to pulled markdown drift.\n\nValidation: ./scripts/check-format.sh; sh -n build.sh scripts/stage-colibri-iso.sh live/operator-session/clawdie-join-hive.sh live/operator-session/clawdie-enable-mother.sh live/operator-session/colibri-live-rebuild; ./scripts/test-release-gate.sh; git diff --check.
2026-06-21 20:38:33 +02:00
67e7bef002 Merge pull request 'docs: document ISO versioning & release schema in AGENTS.md' (#94) from document-iso-versioning into main
Reviewed-on: #94
2026-06-21 20:37:44 +02:00
a7102d293d Merge pull request 'feat(enable-mother): publish colibri pubkey to Vaultwarden for hive auth' (#99) from hive-key-exchange into main 2026-06-21 20:31:07 +02:00
3a2228a6b7 feat(enable-mother): publish colibri pubkey to Vaultwarden for hive auth
Vault-mediated key exchange (direction B — we call mother). After ensuring the
colibri SSH identity, enable-mother now upserts the pubkey into Vaultwarden as
`hive-pubkey-<hostname>` (via bw, run as root so it can read the BW_* bootstrap
creds from provider.env). Mother's mother-sync-hive-keys rebuilds its
authorized_keys from these items, so no operator copy-paste between machines.

The printed pubkey + restricted command= line remain as a manual fallback when
the vault publish is unavailable. Uses the bitwarden-cli-vault skill's
session+upsert pattern. sh -n clean; embedded JSON/id-extraction tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 20:18:27 +02:00
6465bb7a35 Merge pull request 'feat(enable-mother): jq-merge the mother entry instead of overwriting' (#98) from enable-mother-jq-merge into main 2026-06-21 19:31:21 +02:00
c49fe82ea8 feat(enable-mother): jq-merge the mother entry instead of overwriting
Track C's enable-mother overwrote external-mcp.json with a single mother
server. Use jq to merge the mother entry into the existing registry so other
configured servers are preserved, written atomically (mktemp in same dir + mv).
This is the concrete consumer that makes jq a real dependency of the MCP path;
fails loudly if jq is absent.

(Re-applied: the original commit was lost to a branch-recreation race when #97
merged at the packages-only commit.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:30:40 +02:00
f0c1b5ea37 Merge pull request 'feat(packages): add jq for the MCP tool path (live image + jails)' (#97) from add-jq-for-mcp into main 2026-06-21 19:26:28 +02:00
f251c05002 feat(packages): add jq for the MCP tool path (live image + jails)
The MCP tooling needs jq on PATH to parse colibri-mcp / external MCP JSON-RPC
output. Add it to the live operator image (where the auto-spawned Pi and
colibri-mcp run) and to the jail package union. The jail entry mirrors the
matching addition in Colibri's agent-jail-bootstrap.sh (kept in sync per the
list header).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:25:08 +02:00
3078d50f05 Merge pull request 'feat(iso): wire Colibri OOTB defaults + opt-in Mother MCP link' (#96) from colibri-ootb-mcp-defaults into main 2026-06-21 19:22:42 +02:00
02507476b8 Merge pull request 'feat(join-hive): capture vault creds and pull provider keys on first boot' (#95) from joinhive-cred-capture into main 2026-06-21 19:22:22 +02:00
34259e3312 feat(join-hive): confirm the auto-spawned Pi after daemon restart
Per Hermes' review of the cred-capture flow: after the daemon restart that
loads the pulled keys, poll colibri status (up to 10s) for a live agent so the
operator sees confirmation that the Pi auto-spawn actually came up — instead of
just "daemon restarted". Prints "Pi agent is live." or a check hint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:59:15 +02:00
a788d99967 feat(iso): wire Colibri OOTB defaults + opt-in Mother MCP link
Workstream C of the next ISO rebuild.

C1 — Auto-spawn lit up out of the box:
  provider.env now ships COLIBRI_AUTOSPAWN_PI="YES", so colibri#137 fires on
  the booted image once a DeepSeek key is present (pulled by Join Hive, A).

C2 — External MCP registry staged:
  /usr/local/etc/colibri/external-mcp.json shipped as {"servers":{}} at the
  path colibri-mcp reads by default. Empty = mother off by default.

C3 — Opt-in "Enable Mother Link" (clawdie-enable-mother + desktop entry):
  Direction is "our Pi calls mother's tools" — colibri-mcp dials OUT to mother
  over SSH-stdio and proxies mother's tools to the Pi via its external-call
  path. The toggle:
   - provisions an SSH identity for the colibri service account
     (/var/db/colibri/.ssh — the daemon and its Pi run as `colibri`),
   - writes the mother entry into external-mcp.json (ssh -i <key> ... mother),
   - upserts COLIBRI_MCP_EXTERNAL_CALL=1 into provider.env,
   - restarts the daemon and prints colibri's pubkey to authorize on mother.

provider.env.sample documents the new toggles. sh -n clean on all scripts;
the empty default and the emitted mother entry validate as JSON and match the
ExternalMcpRegistry {servers:{command,args,env}} shape.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:53:41 +02:00
f9e47ab516 feat(join-hive): capture vault creds and pull provider keys on first boot
Workstream A of the next ISO rebuild. The booted XFCE image's "Join Hive"
flow now collects the 3 Vaultwarden bootstrap values and pulls the provider
keys, instead of only warning when they are missing.

Step [2/4] now:
- If provider.env lacks BW_*, prompts for BW_CLIENTID/BW_CLIENTSECRET/
  BW_PASSWORD (secret + password read with echo off) and upserts them into
  provider.env (root-owned 0600). Entering nothing skips — manual floor intact.
- Then runs clawdie-vault-fetch against provider.env (as bootstrap and as
  --write-env target) to pull DEEPSEEK_API_KEY (and other agent-secrets), and
  restarts colibri_daemon so it loads the new keys — which triggers the Pi
  auto-spawn (colibri#137).

Secrets never appear in process arguments: values stay in shell variables and a
0600 temp under ~/.cache/clawdie; provider.env is read/written via mdo. The
upsert preserves the endpoint line and other keys (verified: special characters
in the secret/password survive, no duplicate BW_* lines).

provider.env stays the single secret store — the daemon's vault provisioning and
the existing provider_env_has_bw_creds check already assume that.

sh -n clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:22:45 +02:00
3b96c5215d docs: document ISO versioning & release schema in AGENTS.md
The product-version scheme was only captured in scattered build.cfg/build.sh
comments and agent memory. Promote it to contributor-visible guidance:
- ISO_VERSION is an explicit product version in build.cfg (0.11.0, unified
  with Colibri); no-version builds fail fast; image name = codename + version.
- Component versions are provenance in build-manifest.json (version_scheme
  "product"), not the image identity.
- BUILD_CHANNEL dev|release; release gate (build.sh:check_release_gate)
  requires clean staged trees so the manifest fully describes the artifact.

Matches shipped code; no behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 16:47:23 +02:00
0deaae2586 Merge pull request 'fix(build): resolve shared npm snippet via COLIBRI_REPO + preflight it' (#93) from harden-shared-npm-snippet into main
Reviewed-on: #93
2026-06-21 16:41:01 +02:00
eec69caa5f fix(build): resolve shared npm snippet via COLIBRI_REPO + preflight it
PR #92 wired the ISO to the shared clawdie-npm-profile.sh but hardcoded
${SCRIPT_DIR}/../colibri and had no existence guard. Every other colibri
consumer in build.sh resolves through resolve_colibri_paths (default
/home/clawdie/ai/colibri, honoring COLIBRI_REPO), so the hardcoded path
diverged from the real build-host layout and ignored the override; a
missing file let cat fail silently into a half-written snippet.

Now: resolve via resolve_colibri_paths and preflight the file with a
clear error pointing at COLIBRI_REPO, matching preflight_colibri_artifacts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 16:39:25 +02:00
4a43e6d0d6 Merge pull request 'fix(build): source shared npm-global snippet instead of inline heredoc' (#92) from fix/unify-npm-profile-snippet into main
Reviewed-on: #92
2026-06-21 16:25:34 +02:00