Forgejo on moria — Installation Record

# Forgejo on moria — Installation Record

> Installed 2026-08-29 by agent + Jan (sudo). Canonical git forge per the
> topology in "Hetzner Server — Audit & Container Setup (Plan)".
> **COMPLETE + VERIFIED 2026-08-29 22:00: web, git-SSH (2222, key auth),
> 12 repos imported (8 public, 4 private).**
>
> **31.08.: Open items als infra#3–8 in Forgejo gemeldet** (plus infra#8 =
> Actions instance-weit klären — Dashboard-LED/404 rusty_emu, behelfsmäßig
> has_actions=false; und infra#9 = Jenkins-Multi-Repo-Migration aus der
> Sim-Racing-To-do-Notiz). Liste unten = ursprüngliche Erfassung.
> **31.08. neu im Betrieb:** Repo **home-energy** (privat, HA-Konfiguration
> + Haus-Energie-Issues #1–18) via API angelegt; Access-Token liegt auf dem
> Laptop in `C:\Users\jan\.forgejo-token-moria` (Scopes write:issue +
> write:repository — **Achtung: Repo-Anlage per API braucht zusätzlich
> write:user**, sonst 403 „write:user required").

## What exists now

| Item | Value |
|---|---|
| Host | moria (FreeBSD 14.4-RELEASE-p5), LAN 192.168.1.2 |
| Jail | `forgejo` (auto JID), IP **192.168.1.10** on igb0, conf in `/etc/jail.conf.d/forgejo.conf` + master `/etc/jail.conf` with include |
| Jail filesystem | `zroot/jails/forgejo` (mountpoint `/jails/forgejo`), FreeBSD 14.4 base.txz |
| **Repo data** | **`zstorage/forgejo-data`** nested-mounted at `/jails/forgejo/root/var/db/forgejo` (12.4T pool) |
| **REAL config** | **`/usr/local/etc/forgejo/conf/app.ini`** (= `$FORGEJO_CUSTOM/conf/app.ini`; the rc script sets FORGEJO_CUSTOM). The file at `/usr/local/etc/forgejo/app.ini` was never read — archived as `.unused` |
| Forgejo | pkg **forgejo 15**, service user **git** (UID 211, nologin), logs `/var/log/forgejo` |
| Web | http://192.168.1.10:3000 |
| Git SSH | **port 2222**: `ssh://git@192.168.1.10:2222/jan/<repo>.git` — verified key auth ("Laptop Jan") |
| Admin | user `jan` |
| Privacy | DEFAULT_PRIVATE=true, DISABLE_REGISTRATION (now actually applied via conf/app.ini) |
| Repos | 13: rusty_emu, rustysar, rusty-marvin, top_drives, joplin-plugin-mcp, ac-telemetry-plugin, rusty-telemetry, racecraft, **home-energy** (public) + keyfob-station, super_marvin, super-marvin-userscripts, pentest_scripts, **process-data** (private) |
| Boot | jail_enable=YES, jail_list=forgejo, forgejo_enable=YES (in jail) |
| Fallback | pre-migration data at `forgejo.old` — Lage 31.08. nicht verifizierbar (siehe infra#6) |
| Hardening | devfs_ruleset=4, exec.clean, no raw sockets; ZFS dataset-per-jail |
| **Push-to-Create** | **DISABLED für Users** (31.08. getestet) → Repos via API (Token mit write:user) oder UI anlegen |

## Lessons learned (do not rediscover — this cost an evening)

1. **The FreeBSD forgejo port reads `$FORGEJO_CUSTOM/conf/app.ini` =
   `/usr/local/etc/forgejo/conf/app.ini`** — NOT `/usr/local/etc/forgejo/app.ini`.
   The rc script exports FORGEJO_CUSTOM and runs `forgejo` without `--config`.
   Editing the wrong file fails SILENTLY (forgejo never logs which file it
   read; symptom: settings ignored, log shows default ROOT_URL localhost)
2. **Unprivileged git user cannot bind ports <1024** → built-in SSH must use
   ≥1024 (2222 here). On :22 it never starts and logs nothing obvious
3. **Jail port fallback trap**: with nothing bound in the jail on :22, the
   HOST's wildcard sshd answers the jail IP (FreeBSD semantics) — a
   misleading OpenSSH banner masked the missing Forgejo SSH
4. **Jail base sshd squatting**: a full base.txz jail runs the base rc;
   disable sshd_enable in the jail (script 6) — it has no users
5. `service jail start` needs a MASTER `/etc/jail.conf` with the include line
6. jan's login shell is csh — always `sudo sh /tmp/x.sh`; pasting scripts
   into csh mangles them
7. First run without config initializes data dirs but the DB migration must
   be run explicitly (`forgejo migrate` as git) after config exists
8. `database is locked` = another forgejo process is mid-migration; wait and
   retry the admin command
9. Ownership fixes must run INSIDE the jail (host has no git user)
10. Forgejo prints `generated random password is 'X'` BEFORE the DB insert —
    a failed create still shows a password
11. rc prestart runs `forgejo doctor check` — config errors surface there
12. **(31.08.) Forgejo 15 API-Scopes:** Issue-Anlage braucht `write:issue`,
    Repo-Anlage (POST /user/repos) zusätzlich `write:user`. Scoped Tokens
    ohne write:user → 403 „write:user required". `lovelace/config/get` gibt
    es im HA-Websocket nicht (nur save) — aber das ist HA, nicht Forgejo.
13. **(31.08.) PS-5.1-Invoke-RestMethod sendet String-Bodies als
    ISO-8859-1** → Umlaut-Mojibake in Issues. Fix: Body als UTF8-ByteArray
    übergeben (`[Text.Encoding]::UTF8.GetBytes($json)`).

## Open items

> **31.08.: als infra#3–8 gemeldet (plus infra#8 Actions-Klärung, infra#9
> Jenkins-Multi-Repo) — Tracking läuft in Forgejo, Liste hier = Historie.**

1. Dev-machine remote cut-over to `ssh://git@192.168.1.10:2222/jan/<repo>.git`
   (Jan, when ready; old bare repos stay as fallback) — **31.08.: process-data
   (Laptop) + Ubuntu-Klone nutzen moria; Rest = Jenkins + alte bare repos
   (infra#3)**
2. Push mirrors moria → git.hunnius.net (needs a hunnius token stored on
   moria; set per repo in Settings → Mirror) → **infra#5**
3. Jenkins re-point after cut-over → **infra#3**
4. Delete `forgejo.old` after confidence → **infra#6 (Lage 31.08. nicht an
   bekannten Pfaden gefunden)**
5. Snapshot routine: `zfs snapshot zstorage/forgejo-data@…` + jail dataset
   before upgrades → **infra#7**
6. Revoke today's tokens (moria/hunnius/codeberg) once mirrors are wired →
   **infra#5** · 31.08.-Befund: das gespeicherte moria-Credential (40 Zeichen)
   lief 401 auf die API — vermutlich bereits rotiert; das neue
   Allzweck-Token liegt auf dem Laptop (s. oben)

**Verknüpfung:** [[Heim-Energie-Setup: Equipment & Fakten (Stand Aug 2026)]] · [[Dokumentations- & Referenz-Architektur (Joplin ↔ Forgejo)]] · [[Development Projects Index — Repos & Verweise]]

id: 60fdddf4784c4d8cb17a733366977241
parent_id: cd0501fe1cc24c93bc6bc3e783fb324fd
created_time: 2026-08-29T08:24:55.195Z
updated_time: 2026-08-31T18:35:51.487Z
is_conflict: 0
latitude: 0.00000000
longitude: 0.00000000
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 1787991895195
user_created_time: 2026-08-29T08:24:55.195Z
user_updated_time: 2026-08-31T18:35:51.487Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
is_locked: 0
extracted_resource_ids: 
type_: 1