Skip to content

SSH keys are stored under the cache root and destroyed by cleanup --all #48

Description

@xai

Bug Description:

enclave ssh-init generates a dedicated SSH keypair and copies known_hosts
into ~/.cache/enclave/ssh/ (config.HostSSHDir, internal/config/host_paths.go:36).
The cache root is the wrong home for that content on two counts:

  1. A private key is not regenerable. The cache root is documented as
    "regenerable" (internal/config/host_roots.go:18-23), and the XDG spec
    treats cached data as deletable at any time without loss of function.
    Deleting this key loses the identity: the matching public key is already
    registered with the user's Git providers, so it cannot be recreated, only
    replaced and re-registered.
  2. enclave cleanup --all removes the entire cache root
    (internal/app/cleanup.go:216) and only skips it when --keep cache is
    passed. The --keep auth gate that protects the other credential stores
    does not cover the SSH directory, because those stores live under the state
    root while SSH does not.

The same reasoning applies to system cache sweepers, which are free to reap
~/.cache contents.

ssh-init also invites users to drop their own keys into the directory
("Alternatively, replace the keys in %s with your desired keys",
internal/app/ssh.go:66), which makes it user-owned content by intent.

Steps to Reproduce:

  1. enclave ssh-init — generates ~/.cache/enclave/ssh/id_ed25519 and prints
    the public key to register with a Git provider.
  2. Register the public key with the Git provider, and confirm git push works
    from inside a session.
  3. enclave cleanup --all — the SSH directory is removed along with the rest
    of the cache root. Sessions fall back to the "SSH not configured" warning
    (internal/runtime/runtime.go:1047) and the registered public key is now
    dead.

Additional Information

  • Operating System: Linux
  • Enclave Version: rolling-12-g6ab6e6f

Proposed change

Move the SSH directory to the config root: ~/.config/enclave/ssh/ on Linux,
the corresponding Apple config subtree on macOS. Cleanup targets are already
forbidden from resolving under the config root, so this makes the keys
structurally safe from enclave cleanup, and it matches the fact that users
are expected to edit the directory's contents.

The state root is the alternative, consistent with the auth stores under
~/.local/state/enclave/tools/. It needs an explicit cleanup exemption to be
safe, and it fits user-supplied keys less well. Maintainer call — this issue
assumes the config root.

Migration

Existing users must not silently lose SSH access, and the key must be moved
rather than regenerated:

  • ssh-init creates and reports the new location.
  • On startup, if the old ~/.cache/enclave/ssh/ exists and the new location
    does not, move it and log the relocation once. Preserve 0700 on the
    directory and 0600 on the key files.
  • Leave the old path alone when both exist; the new location wins.

Affected code and docs

  • internal/config/host_paths.go:36HostSSHDir root.
  • internal/app/ssh.gossh-init setup and messages.
  • internal/runtime/runtime.go:1041 — mount lookup, plus the fallback probe
    for the old path.
  • internal/app/cleanup.go — verify no cleanup target resolves to the new
    path.
  • docs/auth.md:186, docs/cli-reference.md:86, docs/ARCHITECTURE.md:33,
    and the host-data section in AGENTS.md.

Acceptance criteria

  • enclave ssh-init writes to the config root with 0700/0600 permissions.
  • enclave cleanup --all leaves the SSH directory intact, with no --keep
    flag required.
  • A pre-existing ~/.cache/enclave/ssh/ is relocated on the next run, keeping
    the same keypair and permissions.
  • Sessions mount the SSH directory read-only from the new location.
  • Tests cover the migration (old-only, new-only, both present) and assert that
    no cleanup target resolves under the SSH directory.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions