# Self-Hosted Setup & Codeberg Migration Workflow for setting up a self-hosted Forgejo instance and migrating repos from Codeberg. ## SSH Setup Forgejo often runs SSH on a non-standard port (e.g. 2222). Always probe: ```bash ssh -T git@ # port 22 — may return password prompt ssh -T -p 2222 git@ # try 2222 ``` Once confirmed, add to `~/.ssh/config`: ``` Host code. HostName code. User git Port 2222 IdentityFile ~/.ssh/ IdentitiesOnly yes ``` ## Org vs User Namespace If the admin account name matches the desired org name (e.g. user `clawdie` cannot have org `clawdie`), repos go under the user: `code./clawdie/repo`. Same URL structure, same SSH path. Multi-agent audit trail comes from machine users, not org membership. ## Push-to-Create Usually disabled by default. Check: ```bash # If push fails with "Push to create is not enabled" curl -s "https://code./api/v1/settings/ui" | jq . ``` Create repos via API or web UI instead. ## Shallow Clone → Push (Codeberg Migration) Forgejo rejects shallow pushes unless `ALLOW_SHALLOW_UPDATES` is enabled (off by default). For repos cloned shallow from Codeberg: ### Small repos (few commits, full objects present) ```bash # Check if all objects are present git fsck --connectivity-only # Remove shallow marker and rewrite root commit to be parentless echo " " > .git/info/grafts git filter-branch -f -- --all rm .git/info/grafts .git/shallow ``` ### Large repos (many objects missing) ```bash # Fetch full history via HTTPS (more reliable than SSH for large fetches) git remote set-url origin https://codeberg.org//.git git fetch --unshallow origin git remote set-url origin git@codeberg.org:/.git # restore ``` ## Machine-User Permissions Pattern for multi-agent git access: | User | Host | Permissions | | ---------------- | -------- | ---------------------------------- | | `-` | hostname | `write:repository` token + SSH key | - **Never copy private SSH keys** between hosts. - Each machine user gets their own SSH key registered on Forgejo. - Passwords: only needed if agent uses browser to access web UI (e.g. PR review). Others: random, no force-change. - Encode the permissions table in the repo's AGENTS.md for agent self-discovery. ## Browser-Based Agent Access Some agents (e.g. Hermes on debby) need web UI access for PR review, issue triage, repo settings, and CI status checks. For these, set a real password (untick "require password change") and store it in Vaultwarden. Other agents (Claude, Codex) only need SSH keys — random passwords are fine. ### Password Rotation via Browser When admin-created user has "require password change" ticked, the agent can handle first-login rotation: ```bash # Generate strong password openssl rand -base64 24 ``` Then use browser tools to: 1. Navigate to `https://code./user/login` 2. Type username + temp password 3. Forgejo redirects to "Update password" page 4. Type new password in both fields, submit 5. Verify landing on dashboard Special characters (šđžčć, etc.) work fine in Forgejo passwords. ## Git Hooks Bypass When Node Not on PATH When `node` isn't on PATH (e.g. in a tool-call shell that doesn't source `.bashrc`), git hooks like `pre-commit` and `prepare-commit-msg` hang with `node: not found`. Workaround: ```bash GIT_AUTHOR_NAME="Name" GIT_AUTHOR_EMAIL="email" \ GIT_COMMITTER_NAME="Name" GIT_COMMITTER_EMAIL="email" \ git -c core.hooksPath=/dev/null commit -m "message" ``` Use `--no-verify` only for `pre-commit` and `commit-msg`; `core.hooksPath=/dev/null` kills all hooks including `prepare-commit-msg` which otherwise still fires and hangs. ## Pitfalls - **Forgejo SSH on non-standard port**: always probe port 2222 first. The default port 22 often hits system SSH, not Forgejo's, producing confusing password prompts. - **Token is immutable**: no edit after creation. Get scopes right on first generate. Wrong scope = new token. - **Org name collision with username**: Forgejo won't let you create an org named the same as an existing user. Use the user namespace instead. - **Shallow clones from Codeberg**: unshallow before pushing, or rewrite root commits. Forgejo blocks shallow updates by default. - **`git fetch --unshallow` timeouts**: Codeberg can be slow. Use HTTPS URL (not SSH) for large unshallow operations — often faster and more reliable. - **`git filter-branch` grafts are deprecated**: the `echo " " > .git/info/grafts` + `git filter-branch -f -- --all` pattern works but is deprecated. Prefer `git replace --convert-graft-file` by default. - **Git hooks hang when `node` not on PATH**: use `-c core.hooksPath=/dev/null` (not just `--no-verify` — that skips pre-commit but not prepare-commit-msg).