id: 904c334bab54476d9f182d84967dc5a1
parent_id: b40ec8cb26214b8da21845b57903904f
item_type: 1
item_id: bbc49a3bbada4fcdb8fedc66abf5014e
item_updated_time: 1785659636718
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"v22.22.1\"],[1,\", PostgreSQL installed\"],[0,\" |\\\n| **l\"]],\"start1\":642,\"start2\":642,\"length1\":16,\"length2\":38},{\"diffs\":[[0,\"| **\"],[-1,\"Primary git host + \"],[0,\"Jenkins \"],[-1,\"(\"],[0,\"CI\"],[-1,\")\"],[0,\" + \"],[-1,\"Test/Staging** | Runs Forgejo (primary git)\"],[1,\"staging VM + file server** | LAN-only\"],[0,\"; Je\"]],\"start1\":861,\"start2\":861,\"length1\":85,\"length2\":58},{\"diffs\":[[0,\" Jenkins\"],[1,\" CI\"],[0,\"; can ho\"]],\"start1\":916,\"start2\":916,\"length1\":16,\"length2\":19},{\"diffs\":[[0,\"nux | **\"],[1,\"Git host (Forgejo) + \"],[0,\"Producti\"]],\"start1\":1011,\"start2\":1011,\"length1\":16,\"length2\":37},{\"diffs\":[[0,\"* | \"],[-1,\"Git repo backup/mirror\"],[1,\"Public-facing; Forgejo (public + private repos); Caddy (TLS)\"],[0,\"; pl\"]],\"start1\":1069,\"start2\":1069,\"length1\":30,\"length2\":68},{\"diffs\":[[0,\"g** \"],[-1,\"instance; public-facing via Caddy\"],[1,\"mirror\"],[0,\" |\\\n\\\n\"]],\"start1\":1164,\"start2\":1164,\"length1\":41,\"length2\":14},{\"diffs\":[[0,\"r → \"],[-1,\"Linux (Debian) guests, FreeBSD jails\"],[1,\"Debian staging VM (mirrors prod OS)\"],[0,\".\\\n- \"]],\"start1\":1248,\"start2\":1248,\"length1\":44,\"length2\":43},{\"diffs\":[[0,\"sts \"],[-1,\"or cross-OS testing \"],[0,\"if n\"]],\"start1\":1343,\"start2\":1343,\"length1\":28,\"length2\":8},{\"diffs\":[[0,\"ODUCTION\"],[1,\" + GIT\"],[0,\"\\\n  RustR\"]],\"start1\":1628,\"start2\":1628,\"length1\":16,\"length2\":22},{\"diffs\":[[0,\"    \"],[-1,\"Forgejo (primary git)        Codeberg (mirror\"],[1,\"bhyve: Debian staging         Forgejo (primary\"],[0,\")\\\n  \"]],\"start1\":1673,\"start2\":1673,\"length1\":53,\"length2\":54},{\"diffs\":[[0,\"ocal PG \"],[-1,\"+ Redis\"],[1,\"(native)\"],[0,\"        \"]],\"start1\":1728,\"start2\":1728,\"length1\":23,\"length2\":24},{\"diffs\":[[0,\"        \"],[-1,\"bhyve: Debian staging \"],[1,\"artifact + file server\"],[0,\"        \"]],\"start1\":1751,\"start2\":1751,\"length1\":38,\"length2\":38},{\"diffs\":[[0,\"    \"],[-1,\"artifact + backup store       + Caddy (TLS)\\\n                                  (also: file server\"],[1,\"                               + Caddy (TLS\"],[0,\")\\\n``\"]],\"start1\":1841,\"start2\":1841,\"length1\":104,\"length2\":51},{\"diffs\":[[0,\", local \"],[-1,\"services (\"],[0,\"PostgreS\"]],\"start1\":1958,\"start2\":1958,\"length1\":26,\"length2\":16},{\"diffs\":[[0,\"SQL \"],[-1,\"+ Redis running natively or in a VirtualBox VM if needed\"],[1,\"(native Ubuntu package\"],[0,\"), f\"]],\"start1\":1973,\"start2\":1973,\"length1\":64,\"length2\":30},{\"diffs\":[[0,\"od; \"],[-1,\"Forgejo is the primary git host; artifacts and backups live on the file server.\\\n- **Produce / host** on the **Hetzner box** — public entry point, TLS via Caddy, bare systemd services (game binary + PG + Redis). Hetzner also mirrors git repos and will host a self-host\"],[1,\"file server + artifact store.\\\n- **Produce / host** on the **Hetzner box** — **Forgejo** (primary git host, public + private repos), production game services (bare systemd + Caddy TLS), plann\"],[0,\"ed \"],[1,\"**\"],[0,\"Codeberg\"],[-1,\" instance\"],[1,\"** mirror for redundancy\"],[0,\".\\\n- \"]],\"start1\":2139,\"start2\":2139,\"length1\":295,\"length2\":235},{\"diffs\":[[0,\" dev\"],[-1,\"; not a build or deploy target\"],[0,\".\\\n\\\n*\"]],\"start1\":2431,\"start2\":2431,\"length1\":38,\"length2\":8},{\"diffs\":[[0,\"uest\"],[-1,\" (not a FreeBSD jail)\"],[0,\" cat\"]],\"start1\":2548,\"start2\":2548,\"length1\":29,\"length2\":8},{\"diffs\":[[0,\"ibc/\"],[-1,\"behavioral\"],[1,\"runtime\"],[0,\" sur\"]],\"start1\":2570,\"start2\":2570,\"length1\":18,\"length2\":15},{\"diffs\":[[0,\" Where |\"],[-1,\"\\\n\"],[1,\" What it does |\\\n|---\"],[0,\"|---|---\"]],\"start1\":2655,\"start2\":2655,\"length1\":17,\"length2\":36},{\"diffs\":[[0,\"mary\"],[-1,\" use on\"],[1,\":\"],[0,\" wor\"]],\"start1\":2814,\"start2\":2814,\"length1\":15,\"length2\":9},{\"diffs\":[[0,\"khorse |\"],[1,\" AI-assisted coding in the terminal |\"],[0,\"\\\n| IDEs \"]],\"start1\":2823,\"start2\":2823,\"length1\":16,\"length2\":53},{\"diffs\":[[0,\"liJ,\"],[-1,\" GoLand, CLion, PyCharm,\"],[0,\" …) \"]],\"start1\":2932,\"start2\":2932,\"length1\":32,\"length2\":8},{\"diffs\":[[0,\"demand |\"],[1,\" Code editing, debugging, refactoring |\"],[0,\"\\\n| Sourc\"]],\"start1\":2973,\"start2\":2973,\"length1\":16,\"length2\":55},{\"diffs\":[[0,\") | \"],[-1,\"basement server (FreeBSD) |\\\n| Source control (mirror) | **Codeberg** (self-hosted instance, planned) | Hetzner (Debian) |\\\n| CI / CD | **Jenkins** | basement server (FreeBSD) |\\\n| Reverse proxy / TLS | **Caddy** | Hetzner (prod + Codeberg mirror). Not needed locally\"],[1,\"Hetzner (Debian, public) | Self-hosted git platform: repo hosting over SSH/HTTPS, web UI, issues, PRs, wiki. Supports **public and private repos**. The same software Codeberg.org runs. |\\\n| Source control (mirror) | **Codeberg** (self-hosted instance, planned) | Hetzner (Debian, public) | Backup mirror of all Forgejo repos; off-site redundancy within the same box. |\\\n| CI / CD | **Jenkins** | basement server (FreeBSD, LAN-only) | Build, test, cross-compile, deploy to staging/prod |\\\n| Reverse proxy / TLS | **Caddy** | Hetzner (prod) | Public HTTPS, auto Let's Encrypt certs |\\\n| Deployment | **Bare systemd services** | Hetzner (prod); staging VM | Each service (PG, Redis, app) as a native systemd unit\"],[0,\" |\\\n|\"]],\"start1\":3074,\"start2\":3074,\"length1\":272,\"length2\":713},{\"diffs\":[[0,\"ent)\"],[-1,\", **VirtualBox** (workhorse/laptop)\"],[0,\" | \"],[1,\"b\"],[0,\"as\"],[-1,\" above |\\\n| Deployment | **Bare systemd services** | Hetzner (prod); staging VM | \"],[1,\"ement server | Debian staging VM to mirror prod OS |\"],[0,\"\\\n| R\"]],\"start1\":3820,\"start2\":3820,\"length1\":129,\"length2\":66},{\"diffs\":[[0,\"95.0\"],[-1,\" verified\"],[0,\") | \"]],\"start1\":3920,\"start2\":3920,\"length1\":17,\"length2\":8},{\"diffs\":[[0,\"khorse |\"],[1,\" Build backend |\"],[0,\"\\\n| Node \"]],\"start1\":3931,\"start2\":3931,\"length1\":16,\"length2\":32},{\"diffs\":[[0,\"2.1 \"],[-1,\"(verified) \"],[0,\"| wo\"]],\"start1\":3985,\"start2\":3985,\"length1\":19,\"length2\":8},{\"diffs\":[[0,\"orkhorse\"],[1,\" | Build frontend\"],[0,\" |\\\n\\\n> **\"]],\"start1\":3992,\"start2\":3992,\"length1\":16,\"length2\":33},{\"diffs\":[[0,\"ts (\"],[-1,\"or jails on FreeBSD). This keeps things simple and avoids container overhead.\\\n\\\n---\\\n\\\n## 4. Workspace & Repository Layout (workhorse)\\\n\\\n### Current state (verified 2026-08-02)\\\nRepos are scattered across each IDE's **default** project folder:\"],[1,\"Linux) or plain services (FreeBSD). No containers.\\\n\\\n---\\\n\\\n## 4. Services Glossary\\\n\\\n### Forgejo\\\nSelf-hosted git platform — the same open-source software that [Codeberg.org](https://codeberg.org) runs. Provides:\\\n- **Git hosting** — push/pull repos over SSH (`git@hetzner:repo.git`) or HTTPS\\\n- **Web UI** — browse code, diffs, commits in a browser (publicly accessible)\\\n- **Issues & Pull Requests** — issue tracking, code review, merge workflows\\\n- **Wiki** — per-repo documentation\\\n- **Actions** — CI runner (Forgejo Actions, Gitea-compatible; an alternative to Jenkins if desired)\\\n- **Public & private repos** — visibility set per repo. Private repos require authentication; public repos are browseable by anyone. Ideal for publishing open-source projects while keeping work/closed-source repos private.\\\n\\\nWithout Forgejo you'd use bare `git init --bare` repos — functional but no web UI, no issue tracker, no PR workflow.\\\n\\\n### Codeberg (self-hosted mirror)\\\nA second instance of the Codeberg/Forgejo software, running alongside Forgejo on the same Hetzner box or as a backup target. Mirrors all repos from Forgejo for redundancy. Planned, not yet standing.\\\n\\\n### Redis\\\nIn-memory data store used as **cache + pub/sub**. In Skara Brae specifically:\\\n- **Session cache** — temporary game state, rate-limiting counters\\\n- **Leaderboards** — sorted sets for fast ranking queries\\\n- **WebSocket pub/sub** — broadcast game events (combat results, chat) to connected clients\\\n\\\n**Can be deferred for MVP.** At low player counts, the Rust process can hold session/leaderboard data in memory. Add Redis when you need multi-process scaling or want persistence across restarts. PostgreSQL handles all *persistent* data regardless.\\\n\\\n### PostgreSQL\\\nRelational database. Stores all persistent game state: users, characters, items, guilds, adventures, skills, crafting, etc. Runs natively on workhorse for local dev; runs on Hetzner for production.\\\n\\\n### Jenkins\\\nCI/CD server on the basement box (LAN-only). Watches Forgejo for pushes (deploy key + webhook), runs lint/test/build pipelines, cross-compiles the Rust binary for Linux x86_64, and deploys to staging and prod.\\\n\\\n---\\\n\\\n## 5. Workspace & Repository Layout (workhorse)\\\n\\\n### Current state (verified 2026-08-02)\"],[0,\"\\\n```\"]],\"start1\":4072,\"start2\":4072,\"length1\":246,\"length2\":2248},{\"diffs\":[[0,\"oot\\\n\"],[-1,\"Standardize on a single root and group repos **by project**, so multi-repo projects (siblings that depend on each other) sit under one parent:\\\n\\\n\"],[0,\"```\\\n\"]],\"start1\":6723,\"start2\":6723,\"length1\":152,\"length2\":8},{\"diffs\":[[0,\"vin/\"],[-1,\"              # one repo per folder is fine\"],[0,\"\\\n├──\"]],\"start1\":7076,\"start2\":7076,\"length1\":51,\"length2\":8},{\"diffs\":[[0,\"`:**\"],[-1,\"\\\n- M\"],[1,\" m\"],[0,\"ulti\"]],\"start1\":7138,\"start2\":7138,\"length1\":12,\"length2\":10},{\"diffs\":[[0,\"path\"],[-1,\"/file deps to resolve\"],[1,\" deps\"],[0,\" (e.\"]],\"start1\":7198,\"start2\":7198,\"length1\":29,\"length2\":13},{\"diffs\":[[0,\"`). \"],[-1,\"The p\"],[1,\"P\"],[0,\"er-I\"]],\"start1\":7265,\"start2\":7265,\"length1\":13,\"length2\":9},{\"diffs\":[[0,\"ak this.\"],[-1,\"\\\n-\"],[0,\" One roo\"]],\"start1\":7296,\"start2\":7296,\"length1\":18,\"length2\":16},{\"diffs\":[[0,\"one \"],[-1,\"place to back up, one mental model, easy `cd ~/dev/<tab>`.\\\n-\"],[1,\"mental model.\"],[0,\" Jet\"]],\"start1\":7316,\"start2\":7316,\"length1\":68,\"length2\":21},{\"diffs\":[[0,\"ins \"],[-1,\"IDEs \"],[0,\"open\"],[1,\"s\"],[0,\" \"],[-1,\"**\"],[0,\"any\"],[-1,\"**\"],[0,\" fol\"]],\"start1\":7340,\"start2\":7340,\"length1\":25,\"length2\":17},{\"diffs\":[[0,\"ault is \"],[-1,\"only\"],[1,\"just\"],[0,\" a sugge\"]],\"start1\":7378,\"start2\":7378,\"length1\":20,\"length2\":20},{\"diffs\":[[0,\"tion\"],[-1,\", not a requirement.\\\n\\\n### Bridging the IDE defaults with symlinks (optional)\\\nIf you want an IDE's \\\"Open Recent\\\" to keep landing in its default folder, symlink the specific repo rather than relocating:\\\n```bash\\\nmkdir -p ~/dev/skara-brae\\\ngit clone jan@basement:you\"],[1,\".\\\n\\\n### Symlinks (optional)\\\n```bash\\\ngit clone git@hetzner:skara-brae\"],[0,\"/ska\"]],\"start1\":7399,\"start2\":7399,\"length1\":269,\"length2\":75},{\"diffs\":[[0,\"ver\\\n```\\\n\"],[-1,\"- \"],[0,\"Open the\"]],\"start1\":7603,\"start2\":7603,\"length1\":18,\"length2\":16},{\"diffs\":[[0,\" IDE\"],[-1,\", not the symlink, so Rust\"],[1,\" so\"],[0,\" path\"],[-1,\"-\"],[1,\" \"],[0,\"deps \"],[-1,\"and tooling resolve predictably.\\\n- Legacy repos in `~/RustroverProjects` etc. can be moved into `~/dev/<project>/` and symlinked back, or simply left in place until touched\"],[1,\"resolve. Symlinks are for IDE \\\"recent projects\\\" discoverability only\"],[0,\".\\\n\\\n#\"]],\"start1\":7654,\"start2\":7654,\"length1\":217,\"length2\":90},{\"diffs\":[[0,\"---\\\n\\\n## \"],[-1,\"5\"],[1,\"6\"],[0,\". Standa\"]],\"start1\":7972,\"start2\":7972,\"length1\":17,\"length2\":17},{\"diffs\":[[0,\"ow\\\n\\\n\"],[-1,\"Using a generic **Rust + PostgreSQL + Redis + Caddy** stack as the concrete shape:\\\n\\\n\"],[0,\"### \"]],\"start1\":8012,\"start2\":8012,\"length1\":92,\"length2\":8},{\"diffs\":[[0,\"e\\\n- \"],[-1,\"Run services l\"],[1,\"L\"],[0,\"ocal\"],[-1,\"ly:\"],[0,\" Pos\"]],\"start1\":8038,\"start2\":8038,\"length1\":29,\"length2\":13},{\"diffs\":[[0,\"SQL \"],[-1,\"+ Redis as \"],[1,\"(\"],[0,\"nati\"]],\"start1\":8055,\"start2\":8055,\"length1\":19,\"length2\":9},{\"diffs\":[[0,\"kage\"],[-1,\"s (or in a VirtualBox VM to keep the host clean). The Rust b\"],[1,\"). B\"],[0,\"acke\"]],\"start1\":8077,\"start2\":8077,\"length1\":68,\"length2\":12},{\"diffs\":[[0,\"go run`,\"],[-1,\" the\"],[0,\" fronten\"]],\"start1\":8100,\"start2\":8100,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"for \"],[-1,\"several local services + IDEs + Kilo.\\\n- RTX 5060 Ti 16 GB is idle for web/game work but available for GPU projects (e.g. poker-bot training).\\\n- Commit → push to **Forgejo** on the basement server (primary git host\"],[1,\"IDEs + Kilo + PG.\\\n- Commit → push to **Forgejo** on Hetzner (internet SSH\"],[0,\").\\\n\\\n\"]],\"start1\":8183,\"start2\":8183,\"length1\":221,\"length2\":81},{\"diffs\":[[0,\"orgejo (\"],[-1,\"local\"],[1,\"webhook\"],[0,\"). Pipel\"]],\"start1\":8332,\"start2\":8332,\"length1\":21,\"length2\":23},{\"diffs\":[[0,\"peline: \"],[-1,\"`\"],[0,\"lint → t\"]],\"start1\":8352,\"start2\":8352,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"compile \"],[-1,\"(\"],[0,\"Linux x8\"]],\"start1\":8380,\"start2\":8380,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"x x86_64\"],[-1,\")\"],[0,\" → trans\"]],\"start1\":8392,\"start2\":8392,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"nary\"],[-1,\"` → deploy to staging\"],[0,\".\\\n- \"]],\"start1\":8414,\"start2\":8414,\"length1\":29,\"length2\":8},{\"diffs\":[[0,\"M** \"],[-1,\"running the same systemd services as prod (separate PG, Redis, app binary, Caddy). Accessible on LAN.\\\n- Basement box doubles as: artifact store, backup storage.\\\n- bhyve guest = Debian to match prod. (Do **not** rely on a FreeBSD jail as the staging runtime for a Linux binary servic\"],[1,\"(mirrors prod) running systemd services (PG, app binary, Caddy). Accessible on LAN.\\\n- Basement box is also the file server and artifact stor\"],[0,\"e.\"],[-1,\")\"],[0,\"\\\n\\\n##\"]],\"start1\":8456,\"start2\":8456,\"length1\":293,\"length2\":150},{\"diffs\":[[0,\"y** \"],[-1,\"terminates TLS and fronts\"],[1,\"(TLS) fronts Forgejo (git),\"],[0,\" the\"]],\"start1\":8667,\"start2\":8667,\"length1\":33,\"length2\":35},{\"diffs\":[[0,\"game API\"],[1,\",\"],[0,\" and \"],[1,\"the \"],[0,\"static f\"]],\"start1\":8703,\"start2\":8703,\"length1\":21,\"length2\":26},{\"diffs\":[[0,\".\\\n- \"],[-1,\"Services run as **bare systemd units**: PostgreSQL, Redis, the Skara Brae server binary, and optionally a static-file server for the built frontend\"],[1,\"Bare **systemd** services: PostgreSQL, Redis (when needed), Skara Brae binary\"],[0,\".\\\n- \"]],\"start1\":8736,\"start2\":8736,\"length1\":155,\"length2\":85},{\"diffs\":[[0,\"oys \"],[-1,\"by\"],[1,\"via\"],[0,\" SSH\"],[-1,\"ing to Hetzner, copying the compiled binary (or pulling from an artifact store), and `systemctl restart skara-brae`.\\\n- Hetzner also hosts a self-hosted **Codeberg** instance as a public mirror/backup for repos\"],[1,\": copy binary → `systemctl restart skara-brae`\"],[0,\".\\\n- \"]],\"start1\":8833,\"start2\":8833,\"length1\":223,\"length2\":61},{\"diffs\":[[0,\"aveat:**\"],[-1,\" the\"],[0,\" Hetzner\"]],\"start1\":8904,\"start2\":8904,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"Hetzner \"],[-1,\"box \"],[0,\"is \\\"dece\"]],\"start1\":8913,\"start2\":8913,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"and \"],[-1,\"will\"],[1,\"now\"],[0,\" als\"]],\"start1\":8951,\"start2\":8951,\"length1\":12,\"length2\":11},{\"diffs\":[[0,\" run\"],[-1,\" Codeberg. A text MMORPG binary in Rust\"],[1,\"s Forgejo (+ planned Codeberg mirror). A Rust binary + Forgejo\"],[0,\" is \"]],\"start1\":8963,\"start2\":8963,\"length1\":47,\"length2\":70},{\"diffs\":[[0,\"ed RAM (\"],[1,\"Forgejo + \"],[0,\"Codeberg\"]],\"start1\":9056,\"start2\":9056,\"length1\":16,\"length2\":26},{\"diffs\":[[0,\"PG +\"],[-1,\" Redis +\"],[0,\" app\"]],\"start1\":9085,\"start2\":9085,\"length1\":16,\"length2\":8},{\"diffs\":[[0,\"---\\\n\\\n## \"],[-1,\"6\"],[1,\"7\"],[0,\". Git Ho\"]],\"start1\":9126,\"start2\":9126,\"length1\":17,\"length2\":17},{\"diffs\":[[0,\"Host\"],[-1,\" & Mirror\"],[0,\"ing\\\n\"]],\"start1\":9141,\"start2\":9141,\"length1\":17,\"length2\":8},{\"diffs\":[[0,\"ng\\\n\\\n```\\\n\"],[-1,\"basement\"],[1,\"Hetzner\"],[0,\" (Forgej\"]],\"start1\":9146,\"start2\":9146,\"length1\":24,\"length2\":23},{\"diffs\":[[0,\"gejo\"],[-1,\")  ←→  Hetzner (Codeberg)\\\n    primary              mirror / backup / public\\\n```\\\n\\\n- **Primary git host:** Forgejo on the **basement FreeBSD server**. All development pushes go here.\\\n- **Mirror/backup:** self-hosted **Codeberg**\"],[1,\", public)\\\n    ├── public repos     (browseable by anyone — open-source projects)\\\n    └── private repos    (auth required — work repos, game server, etc.)\\\n         │\\\n         └── mirror → Codeberg instance (same box, backup)\\\n```\\\n\\\n- **Primary git host:** Forgejo on the **Hetzner** Debian box (public-facing).\\\n- **Public repos:** published projects (e.g. open-source tools, game client). Visible to anyone, cloneable over HTTPS.\\\n- **Private repos:** work-in-progress, proprietary code, game server internals. Require SSH key or password authentication.\\\n- **Codeberg mirror** (planned): second Codeberg/Forgejo instance\"],[0,\" on the \"],[-1,\"**\"],[1,\"same \"],[0,\"Hetzner\"],[-1,\"** Debian box. Mirrors all repos for public access and off-site backup.\\\n- Codeberg instance is planned; not yet standing\"],[1,\" box for repo redundancy. Not yet standing.\\\n- **Jenkins** on the basement box connects to Forgejo via deploy key + webhook over the internet\"],[0,\".\\\n- \"]],\"start1\":9166,\"start2\":9166,\"length1\":371,\"length2\":784},{\"diffs\":[[0,\"The old \"],[1,\"[\"],[0,\"GIT / Je\"]],\"start1\":9950,\"start2\":9950,\"length1\":16,\"length2\":17},{\"diffs\":[[0,\"flow\"],[-1,\" note references creating bare\"],[1,\"](joplin://b7728728858f4107a4de7971e2b1ddd9) note (bare git\"],[0,\" rep\"]],\"start1\":9977,\"start2\":9977,\"length1\":38,\"length2\":67},{\"diffs\":[[0,\"epos on \"],[-1,\"\\\"\"],[0,\"the serv\"]],\"start1\":10042,\"start2\":10042,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"rver\"],[-1,\"\\\" —\"],[1,\") is\"],[0,\" sup\"]],\"start1\":10056,\"start2\":10056,\"length1\":11,\"length2\":12},{\"diffs\":[[0,\"gejo\"],[-1,\" as primary\"],[0,\".\\\n\\\n-\"]],\"start1\":10082,\"start2\":10082,\"length1\":19,\"length2\":8},{\"diffs\":[[0,\"---\\\n\\\n## \"],[-1,\"7\"],[1,\"8\"],[0,\". Networ\"]],\"start1\":10089,\"start2\":10089,\"length1\":17,\"length2\":17},{\"diffs\":[[0,\":** \"],[-1,\"project hostnames (\"],[1,\"`git.<domain>` (Forgejo), \"],[0,\"`api\"]],\"start1\":10133,\"start2\":10133,\"length1\":27,\"length2\":34},{\"diffs\":[[0,\"domain>`\"],[-1,\")\"],[0,\" → Hetzn\"]],\"start1\":10186,\"start2\":10186,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"Hetzner \"],[-1,\"box \"],[0,\"A/AAAA. \"]],\"start1\":10197,\"start2\":10197,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\" Encrypt\"],[-1,\" certs\"],[0,\".\\\n- **Gi\"]],\"start1\":10236,\"start2\":10236,\"length1\":22,\"length2\":16},{\"diffs\":[[0,\"** `git@\"],[-1,\"basement\"],[1,\"hetzner\"],[0,\"` (Forge\"]],\"start1\":10264,\"start2\":10264,\"length1\":24,\"length2\":23},{\"diffs\":[[0,\"jo, \"],[-1,\"primary) over LAN SSH; `git@hetzner` (Codeberg, mirror) over internet SSH.\\\n- **Staging access:** LAN-only to the basement server; not public.\\\n- **Backups (symmetric, off each other)\"],[1,\"internet SSH, primary). HTTPS also available for public repos.\\\n- **Basement server:** LAN-only, not publicly reachable. Jenkins connects outbound to Forgejo.\\\n- **Backups\"],[0,\":**\\\n\"]],\"start1\":10287,\"start2\":10287,\"length1\":189,\"length2\":177},{\"diffs\":[[0,\"ejo \"],[-1,\"(primary git):\"],[1,\"→\"],[0,\" nig\"]],\"start1\":10472,\"start2\":10472,\"length1\":22,\"length2\":9},{\"diffs\":[[0,\"ner \"],[-1,\"(off-site relative to basement\"],[1,\"Storage Box or basement file server (pull, not push — basement is LAN-only\"],[0,\").\\\n \"]],\"start1\":10507,\"start2\":10507,\"length1\":38,\"length2\":82},{\"diffs\":[[0,\"erg \"],[-1,\"(\"],[0,\"mirror\"],[-1,\"): already a backup of the repos. Its own data backed up to\"],[1,\" →\"],[0,\" Het\"]],\"start1\":10597,\"start2\":10597,\"length1\":74,\"length2\":16},{\"diffs\":[[0,\"Hetzner)\"],[-1,\":\"],[1,\" →\"],[0,\" `pg_dum\"]],\"start1\":10655,\"start2\":10655,\"length1\":17,\"length2\":18},{\"diffs\":[[0,\"` → \"],[-1,\"basement file server (off-site relative to Hetzner\"],[1,\"off-site (Hetzner Storage Box or pull from basement\"],[0,\").\\\n \"]],\"start1\":10674,\"start2\":10674,\"length1\":58,\"length2\":59},{\"diffs\":[[0,\"---\\\n\\\n## \"],[-1,\"8\"],[1,\"9\"],[0,\". Projec\"]],\"start1\":10878,\"start2\":10878,\"length1\":17,\"length2\":17},{\"diffs\":[[0,\"ee [\"],[-1,\"Skara Brae — \"],[0,\"Proj\"]],\"start1\":10977,\"start2\":10977,\"length1\":21,\"length2\":8},{\"diffs\":[[0,\"ects\"],[-1,\" (poker bots, sim-racing, fatigue-lab)\"],[0,\" inh\"]],\"start1\":11127,\"start2\":11127,\"length1\":46,\"length2\":8},{\"diffs\":[[0,\"sement, \"],[1,\"git+\"],[0,\"prod on \"]],\"start1\":11191,\"start2\":11191,\"length1\":16,\"length2\":20},{\"diffs\":[[0,\"zner\"],[-1,\"; adjust only where a project needs GPU (workhorse), a public endpoint (Hetzner), or long-running eval (basement)\"],[0,\".\\\n\\\n-\"]],\"start1\":11214,\"start2\":11214,\"length1\":121,\"length2\":8},{\"diffs\":[[0,\"---\\\n\\\n## \"],[-1,\"9\"],[1,\"10\"],[0,\". Open D\"]],\"start1\":11221,\"start2\":11221,\"length1\":17,\"length2\":18},{\"diffs\":[[0,\" box\"],[-1,\" (template/image to reuse across projects)\"],[0,\".\\\n- \"]],\"start1\":11456,\"start2\":11456,\"length1\":50,\"length2\":8},{\"diffs\":[[0,\"for \"],[-1,\"Codeberg + a systemd\"],[1,\"Forgejo + Codeberg mirror +\"],[0,\" gam\"]],\"start1\":11497,\"start2\":11497,\"length1\":28,\"length2\":35},{\"diffs\":[[0,\"[ ] \"],[-1,\"Local dev: native Ubuntu packages for PG/Redis, or keep them in a VirtualBox VM to isolate from the host\"],[1,\"Codeberg mirror: same Hetzner box or a different host? Needed at all if Forgejo backups are solid\"],[0,\"?\"]],\"start1\":11654,\"start2\":11654,\"length1\":109,\"length2\":102}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-08-02T08:39:55.011Z
created_time: 2026-08-02T08:39:55.011Z
is_locked: 0
type_: 13