Skip to content

fix(kickoff): store agent keys in main repo so they survive worktree cleanup (GH#610)#612

Merged
dollspace-gay merged 1 commit into
developfrom
fix/610-kickoff-signing-key-location
May 18, 2026
Merged

fix(kickoff): store agent keys in main repo so they survive worktree cleanup (GH#610)#612
dollspace-gay merged 1 commit into
developfrom
fix/610-kickoff-signing-key-location

Conversation

@dollspace-gay
Copy link
Copy Markdown

Summary

  • Closes GH#610 — kickoff worktree cleanup leaving the hub-cache's worktree-scoped user.signingkey dangling at a deleted path, wedging every subsequent crosslink sync.
  • Relocates new agent ed25519 keys from <worktree>/.crosslink/keys/ to the main repo's .crosslink/keys/ (option A from the issue) so the worktree-scoped reference written into .git/worktrees/-hub-cache/config.worktree continues to point at a real file after git worktree remove.
  • Legacy agents whose keys still live in their own worktree continue to sign via a worktree-local fallback in the new resolver, until their worktree is cleaned up.

Why this and not the existing GH#565 repair

GH#565 already added repair_stale_signingkey which catches a missing-file user.signingkey at commit time and falls back to the driver key or unsigned. That fix is the C-path safety net — it stops crosslink sync from staying broken, but the broken state still gets created on every kickoff cleanup. This PR addresses the cause: the worktree-scoped reference is rewritten by configure_signing with an absolute path under the main repo, which doesn't go away when the agent worktree does. The existing C-path repair remains in place as belt-and-suspenders for any other case where a configured key gets removed.

Changes

  • signing::host_crosslink_dir() — new helper resolving any .crosslink/ back to the main repo's .crosslink/ (no-op when already in main, fall-through on bare tempdirs without git so unit tests still work).
  • sync::trust::resolve_agent_key() — host-first lookup with worktree-local fallback for pre-kickoff signing flow: worktree-scoped signingkey config leaves hub-cache unsync-able after kickoff cleanup #610 agents. Wired into both branches of configure_signing that resolve agent.json.ssh_key_path.
  • commands/agent.rsagent init and agent bootstrap now generate keys under the host .crosslink/keys/. The --force reuse path probes host-first before falling back to worktree-local.
  • commands/init/mod.rs — same helper for consistency (driver init always runs in the main repo so it's a no-op in practice).
  • identity.rsAgentConfig.ssh_key_path docstring documents the new "relative to main repo's .crosslink/" semantics + legacy fallback.

Test plan

  • 3 unit tests for host_crosslink_dir — no-git fallback, plain-repo identity, real linked-worktree resolution to main.
  • 3 resolver tests for resolve_agent_key — legacy worktree-only path, neither-path-exists default, end-to-end "agent.json in worktree resolves to main-repo key".
  • All existing configure_signing / repair_stale_signingkey tests still pass unchanged (signing path is transparent to non-worktree callers).
  • cargo test --lib — 1899 passed, 0 failed.
  • cargo clippy -- -D warnings -W clippy::unwrap_used -W clippy::expect_used clean.
  • cargo fmt --check clean.

🤖 Generated with Claude Code

…cleanup (GH#610)

Before this change, `crosslink agent init` generated each kickoff
agent's ed25519 keypair at `<worktree>/.crosslink/keys/<agent>_ed25519`
and then wrote a worktree-scoped `user.signingkey` into the hub-cache's
`.git/worktrees/-hub-cache/config.worktree` pointing at that path.
When `crosslink kickoff cleanup --force` later removed the agent
worktree, the key file vanished but the worktree-scoped reference on
the hub-cache survived — and every subsequent `crosslink sync` failed
to commit hub state with "Couldn't load public key … No such file
or directory". GH#565's `repair_stale_signingkey` catches the residual
breakage at commit time, but doesn't address the architectural cause:
the worktree-scoped config legitimately needs to outlive the worktree,
so the file it points at must too.

Fix is the one option from #610 that addresses the cause rather than
the symptom (option A in the issue): relocate new agent keys to the
*main repo's* `.crosslink/keys/` via a new `host_crosslink_dir()`
helper that walks back from any worktree's crosslink dir to the main
repo (no-op when already in the main repo). The hub-cache's
worktree-scoped `user.signingkey` then references a stable
main-repo path that survives `git worktree remove`. A new
`resolve_agent_key()` helper in `sync::trust` resolves
`agent.json.ssh_key_path` against the host crosslink dir first and
falls back to the worktree-local path so legacy agents created before
this change keep signing until their worktree is cleaned up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dollspace-gay dollspace-gay self-assigned this May 18, 2026
@dollspace-gay dollspace-gay merged commit 33bf5e1 into develop May 18, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kickoff signing flow: worktree-scoped signingkey config leaves hub-cache unsync-able after kickoff cleanup

1 participant