Git transport (SSH)
Forge stores bare repositories under FORGE_REPOS_PATH (default ./data/repos, Docker volume /data/repos).
Docker Compose
docker compose up starts:
| Service | Port | Role |
|---|---|---|
forge |
8770 | HTTP API + WitDB |
git-ssh |
2222 | OpenSSH git user, repos via symlinks |
Required env (forge)
FORGE_PUBLIC_BASE_URL=http://127.0.0.1:8770
FORGE_GIT_BASE_URL=ssh://git@127.0.0.1
FORGE_GIT_SSH_PORT=2222
FORGE_GIT_SSH_SYMLINK_ROOT=/data/git-home
cloneUrl is scp-style (git@host:repo.git), not ssh:// — git-shell rejects absolute paths from ssh:// URLs on push.
FORGE_GIT_SSH_PORT (default 2222 in Compose) is not embedded in cloneUrl. Use one of:
Host 127.0.0.1
Port 2222
export GIT_SSH_COMMAND="ssh -p 2222"
git clone git@127.0.0.1:my-repo.git
Authorized keys (git-ssh)
Inject deploy keys for the pilot:
export FORGE_GIT_AUTHORIZED_KEYS="$(cat ~/.ssh/id_ed25519.pub)"
docker compose up -d --build
Or mount authorized_keys at /etc/forge/authorized_keys in the git-ssh container.
SSH host keys (persist across redeploy)
Git-ssh stores its RSA host key on the shared Docker volume, not in the container layer:
| Path | Role |
|---|---|
/data/git-ssh/hostkeys/ssh_host_rsa_key |
Stable fingerprint after first docker compose up |
/data/repos |
Bare repositories (unchanged) |
Override with FORGE_GIT_SSH_HOSTKEYS_PATH on the git-ssh service.
After a first deploy on a fresh volume, clients add the fingerprint once (ssh-keyscan -p 2222 …). Later docker compose up -d --build redeploys reuse the same key — no REMOTE HOST IDENTIFICATION HAS CHANGED from routine deploys.
If you intentionally wipe the volume, expect a new host key and update known_hosts.
Local dev (no Docker)
Forge creates bare repos and optional symlinks under GitSshSymlinkRoot when creating a repo. Point your SSH git user at the same directory layout, or clone via filesystem path for tests.
Smoke test
.\scripts\smoke-git-transport.ps1
.\scripts\smoke-git-transport.ps1 -SshKeyPath "$env:USERPROFILE\.ssh\id_ed25519.pub"
Push hooks
Each bare repo gets a post-receive hook that POSTs to /api/v1/git/push. Set FORGE_GIT_HOOK_SECRET on both forge and git-ssh when FORGE_REQUIRE_GIT_HOOK_SECRET=true or FORGE_REQUIRE_AUTH=true (public AMS).