Normalize markdown formatting after the latest main updates.\n\nChecks: python3 scripts/layered_soul.py validate .; npx --yes prettier@3 --check '**/*.md'; git diff --check.
4.8 KiB
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:
ssh -T git@<host> # port 22 — may return password prompt
ssh -T -p 2222 git@<host> # try 2222
Once confirmed, add to ~/.ssh/config:
Host code.<domain>
HostName code.<domain>
User git
Port 2222
IdentityFile ~/.ssh/<key>
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.<domain>/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:
# If push fails with "Push to create is not enabled"
curl -s "https://code.<domain>/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)
# Check if all objects are present
git fsck --connectivity-only
# Remove shallow marker and rewrite root commit to be parentless
echo "<root-commit> " > .git/info/grafts
git filter-branch -f -- --all
rm .git/info/grafts .git/shallow
Large repos (many objects missing)
# Fetch full history via HTTPS (more reliable than SSH for large fetches)
git remote set-url origin https://codeberg.org/<owner>/<repo>.git
git fetch --unshallow origin
git remote set-url origin git@codeberg.org:<owner>/<repo>.git # restore
Machine-User Permissions
Pattern for multi-agent git access:
| User | Host | Permissions |
|---|---|---|
<agent>-<host> |
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:
# Generate strong password
openssl rand -base64 24
Then use browser tools to:
- Navigate to
https://code.<domain>/user/login - Type username + temp password
- Forgejo redirects to "Update password" page
- Type new password in both fields, submit
- 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:
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 --unshallowtimeouts: Codeberg can be slow. Use HTTPS URL (not SSH) for large unshallow operations — often faster and more reliable.git filter-branchgrafts are deprecated: theecho "<hash> " > .git/info/grafts+git filter-branch -f -- --allpattern works but is deprecated. Prefergit replace --convert-graft-fileby default.- Git hooks hang when
nodenot on PATH: use-c core.hooksPath=/dev/null(not just--no-verify— that skips pre-commit but not prepare-commit-msg).