feat(git): server-configured SSH key and host trust for git_push/git_fetch #7

Merged
lucas merged 1 commit from feat/t-71 into main 2026-10-14 17:36:34 +02:00
Collaborator

git_push and git_fetch now work out the deck's SSH setup on every call and pass it to git as GIT_SSH_COMMAND. The setup has four parts:

  • Key: taken from OMP_DECK_SSH_PRIVATE_KEY, written to <ssh home>/id_deck.env (0600). A hand-placed <ssh home>/id_deck is used when the variable is unset.
  • Unknown hosts: trusted on first use and recorded in <ssh home>/known_hosts (StrictHostKeyChecking=accept-new).
  • Pinned host keys: optional, from OMP_DECK_SSH_KNOWN_HOSTS.
  • No prompts: BatchMode=yes, so ssh never waits for a password.

The SSH home defaults to <data dir>/ssh; OMP_DECK_SSH_HOME overrides it. With no OMP_DECK_SSH_* variable set and no id_deck file, git and ssh behave exactly as before. When ssh rejects a host key, the error now includes recovery steps.

A new Settings → Git section, backed by GET /api/git/ssh, POST /api/git/ssh/generate and POST /api/git/ssh/forget:

  • generates an ed25519 key and stores it through the same managed-env write path as Settings → Environment;
  • shows the public key, its fingerprint and where the key came from;
  • lists trusted hosts, with a Forget button on hosts that were learned rather than pinned.

The feature works on Linux and macOS only; on Windows it does nothing. The changes are documented in docs/configuration.md and docs/git-tools.md, with a CHANGELOG entry.

Decisions

  • The new protocol types sit in their own // Git SSH (git_push / git_fetch) section of packages/protocol/src/index.ts, using the same divider header as the existing Forgejo section.
  • If ssh-keygen -y or ssh-keygen -l fails on the configured key, GET /api/git/ssh sets both publicKey and fingerprint to null and reports the error in error.
  • The prepareDeckSsh tests are skipped on Windows, since process.platform can't be faked reliably and the feature does nothing there.

Verification

  • bun run typecheck at the repo root: protocol, server and web all exit 0.
  • bun test in apps/server: 637 pass, 0 fail. This includes 12 new cases in apps/server/src/bridge/git-tools/ssh.test.ts:
    • whether the feature switches on, including the default <data dir>/ssh home;
    • the key file is normalised to LF with one trailing newline and gets mode 0600, and the home directory gets 0700;
    • the contents of GIT_SSH_COMMAND;
    • managed-env vs process-env as the key source;
    • deleting id_deck.env when the key variable is cleared, and falling back to a hand-placed id_deck;
    • syncing and removing the pinned-keys file;
    • rejecting an SSH home path that contains whitespace;
    • when the host-key hint is added;
    • parsing ssh-keygen -l output.
  • As the plan specifies, there was no live, end-to-end or browser testing: no real remote, running server or UI check.
`git_push` and `git_fetch` now work out the deck's SSH setup on every call and pass it to git as `GIT_SSH_COMMAND`. The setup has four parts: - **Key:** taken from `OMP_DECK_SSH_PRIVATE_KEY`, written to `<ssh home>/id_deck.env` (0600). A hand-placed `<ssh home>/id_deck` is used when the variable is unset. - **Unknown hosts:** trusted on first use and recorded in `<ssh home>/known_hosts` (`StrictHostKeyChecking=accept-new`). - **Pinned host keys:** optional, from `OMP_DECK_SSH_KNOWN_HOSTS`. - **No prompts:** `BatchMode=yes`, so ssh never waits for a password. The SSH home defaults to `<data dir>/ssh`; `OMP_DECK_SSH_HOME` overrides it. With no `OMP_DECK_SSH_*` variable set and no `id_deck` file, git and ssh behave exactly as before. When ssh rejects a host key, the error now includes recovery steps. A new Settings → Git section, backed by `GET /api/git/ssh`, `POST /api/git/ssh/generate` and `POST /api/git/ssh/forget`: - generates an ed25519 key and stores it through the same managed-env write path as Settings → Environment; - shows the public key, its fingerprint and where the key came from; - lists trusted hosts, with a Forget button on hosts that were learned rather than pinned. The feature works on Linux and macOS only; on Windows it does nothing. The changes are documented in `docs/configuration.md` and `docs/git-tools.md`, with a CHANGELOG entry. ## Decisions - The new protocol types sit in their own `// Git SSH (git_push / git_fetch)` section of `packages/protocol/src/index.ts`, using the same divider header as the existing Forgejo section. - If `ssh-keygen -y` or `ssh-keygen -l` fails on the configured key, `GET /api/git/ssh` sets both `publicKey` and `fingerprint` to null and reports the error in `error`. - The `prepareDeckSsh` tests are skipped on Windows, since `process.platform` can't be faked reliably and the feature does nothing there. ## Verification - `bun run typecheck` at the repo root: protocol, server and web all exit 0. - `bun test` in `apps/server`: 637 pass, 0 fail. This includes 12 new cases in `apps/server/src/bridge/git-tools/ssh.test.ts`: - whether the feature switches on, including the default `<data dir>/ssh` home; - the key file is normalised to LF with one trailing newline and gets mode 0600, and the home directory gets 0700; - the contents of `GIT_SSH_COMMAND`; - `managed-env` vs `process-env` as the key source; - deleting `id_deck.env` when the key variable is cleared, and falling back to a hand-placed `id_deck`; - syncing and removing the pinned-keys file; - rejecting an SSH home path that contains whitespace; - when the host-key hint is added; - parsing `ssh-keygen -l` output. - As the plan specifies, there was no live, end-to-end or browser testing: no real remote, running server or UI check.
git_push and git_fetch resolve a deck-managed SSH setup per call and pass it
to git as GIT_SSH_COMMAND: an identity from OMP_DECK_SSH_PRIVATE_KEY (or a
hand-placed <ssh home>/id_deck), trust on first use into <ssh home>/known_hosts
(StrictHostKeyChecking=accept-new), optional pinned keys from
OMP_DECK_SSH_KNOWN_HOSTS, and BatchMode. With no OMP_DECK_SSH_* var and no
id_deck, git/ssh behave as before. Host-key rejections carry a recovery hint.

New Settings -> Git section (GET /api/git/ssh, POST /api/git/ssh/generate,
POST /api/git/ssh/forget) generates an ed25519 key, shows the public key and
fingerprint, and lists trusted hosts with Forget for learned ones.
Linux/macOS only.

Co-Authored-By: Claude Opus 5.5 via omp-deck
lucas approved these changes 2026-10-14 17:36:28 +02:00
lucas merged commit 407bbb9f9c into main 2026-10-14 17:36:34 +02:00
lucas deleted branch feat/t-71 2026-10-14 17:36:34 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
lucas/omp-deck!7
No description provided.