id: b40ec8cb26214b8da21845b57903904f
parent_id: 
item_type: 1
item_id: bbc49a3bbada4fcdb8fedc66abf5014e
item_updated_time: 1785659226809
title_diff: "[{\"diffs\":[[1,\"Development Environment & Server Topology\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":41}]"
body_diff: "[{\"diffs\":[[1,\"# Development Environment & Server Topology\\\n\\\n> Canonical reference for **all** development projects. Defines which machine does what, the toolchain, the repo/workspace layout, and the standard dev → test → prod flow. Every project should consult this before deciding where to build, test, and deploy.\\\n> Created 2026-08-02.\\\n\\\n---\\\n\\\n## 1. Machine Inventory\\\n\\\n| Name | Hardware | OS | Primary Role | Verified facts |\\\n|---|---|---|---|---|\\\n| **workhorse** (basement desk) | AMD Ryzen 9 5950X (16C / 32T), 128 GiB RAM, RTX 5060 Ti 16 GB; `/` 457 GB SSD, `/home` 3.6 TB (2.2 TB free) | Ubuntu 26.04 LTS (Studio) | **Development** | cargo 1.95.0, Node v22.22.1 |\\\n| **laptop** | Windows 11, RTX 3060 (laptop) | Windows 11 | Work / secondary dev | Kilo + JetBrains installable on demand |\\\n| **basement server** | Dual Xeon; ample RAM & disk | FreeBSD | **Primary git host + Jenkins (CI) + Test/Staging** | Runs Forgejo (primary git); Jenkins; can host **bhyve** VMs |\\\n| **Hetzner box** | Decent (not a powerhouse) | Debian Linux | **Production + Codeberg mirror** | Git repo backup/mirror; planned self-hosted **Codeberg** instance; public-facing via Caddy |\\\n\\\n### Virtualization available\\\n- **bhyve** on the FreeBSD basement server → Linux (Debian) guests, FreeBSD jails.\\\n- **VirtualBox** on workhorse / laptop → throwaway guests or cross-OS testing if needed.\\\n\\\n---\\\n\\\n## 2. Role Assignment (the model)\\\n\\\n```\\\nworkhorse (Ubuntu)              basement (FreeBSD)              Hetzner (Debian)\\\n=================               ================               ================\\\n  CODE  +  LOCAL RUN       →     Jenkins CI               →   PRODUCTION\\\n  RustRover / WebStorm            Forgejo (primary git)        Codeberg (mirror)\\\n  local PG + Redis               bhyve: Debian staging         bare systemd services\\\n  Kilo + JetBrains                artifact + backup store       + Caddy (TLS)\\\n                                  (also: file server)\\\n```\\\n\\\n- **Develop** on **workhorse** — coding (Kilo + JetBrains IDEs), local services (PostgreSQL + Redis running natively or in a VirtualBox VM if needed), fast incremental builds (32 threads).\\\n- **Test / stage** on the **basement server** — Jenkins drives CI; a **bhyve Debian VM** mirrors prod; 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-hosted Codeberg instance.\\\n- **laptop** — work machine and occasional mobile/secondary dev; not a build or deploy target.\\\n\\\n**Why match the staging OS to prod?** Production is Debian on Hetzner. Running staging on a bhyve **Debian** guest (not a FreeBSD jail) catches kernel/glibc/behavioral surprises before they hit prod.\\\n\\\n---\\\n\\\n## 3. Toolchain\\\n\\\n| Concern | Tool | Where |\\\n|---|---|---|\\\n| AI coding | **Kilo** (Kilocode) — z.ai coding plan, primarily **GLM 5.1**, sometimes **GLM 5.2** | Any machine; primary use on workhorse |\\\n| IDEs | **JetBrains All Products** (RustRover, WebStorm, IntelliJ, GoLand, CLion, PyCharm, …) | workhorse (primary); laptop on demand |\\\n| Source control (primary) | **Forgejo** (self-hosted) | 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 |\\\n| VM hypervisor | **bhyve** (basement), **VirtualBox** (workhorse/laptop) | as above |\\\n| Deployment | **Bare systemd services** | Hetzner (prod); staging VM | \\\n| Rust toolchain | cargo / rustup (1.95.0 verified) | workhorse |\\\n| Node toolchain | Node v22.22.1 (verified) | workhorse |\\\n\\\n> **No Docker.** Services run as native systemd units (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:\\\n```\\\n~/RustroverProjects/    keyfob-station, rusty-marvin, rusty-telemetry, rusty_emu, super_marvin, top_drives\\\n~/WebstormProjects/     joplin-plugin-mcp, racecraft, super-marvin-userscripts   (note: lowercase 'storm')\\\n~/IdeaProjects/         ac-telemetry-plugin\\\n~/Development/poker/    old_bot, references          (legacy, pre-dates the IDE dirs)\\\n```\\\n\\\n### Recommended convention: one canonical workspace root\\\nStandardize 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```\\\n~/dev/                         # canonical workspace root (NEW)\\\n├── skara-brae/                # multi-repo project: parent is a plain folder, NOT a git repo\\\n│   ├── skara-brae-server/        # open in RustRover\\\n│   ├── skara-brae-client/        # open in WebStorm\\\n│   └── skara-brae-common/        # shared; 3rd window or \\\"Attach\\\"\\\n├── super-marvin/              # one repo per folder is fine\\\n├── racecraft/\\\n└── ...\\\n```\\\n\\\n**Why `~/dev/<project>/<repo>`:**\\\n- Multi-repo projects need siblings under one parent for path/file deps to resolve (e.g. `sb-common = { path = \\\"../skara-brae-common/rust\\\" }`). The per-IDE default folders break this.\\\n- One root = one place to back up, one mental model, easy `cd ~/dev/<tab>`.\\\n- JetBrains IDEs open **any** folder — the per-IDE default is only a suggestion, 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/skara-brae-server.git ~/dev/skara-brae/skara-brae-server\\\nln -s ~/dev/skara-brae/skara-brae-server ~/RustroverProjects/skara-brae-server\\\n```\\\n- Open the **real path** (`~/dev/...`) in the IDE, not the symlink, so Rust path-deps 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.\\\n\\\n### Decision to confirm\\\n- [ ] Adopt `~/dev/<project>/<repo>/` as the canonical root? (vs. `~/Development/...` which already holds `poker/`)\\\n- [ ] Migrate legacy repos into the new layout, or leave in place + symlink as touched?\\\n\\\n---\\\n\\\n## 5. Standard Dev → Test → Prod Flow\\\n\\\nUsing a generic **Rust + PostgreSQL + Redis + Caddy** stack as the concrete shape:\\\n\\\n### Develop — workhorse\\\n- Run services locally: PostgreSQL + Redis as native Ubuntu packages (or in a VirtualBox VM to keep the host clean). The Rust backend via `cargo run`, the frontend via Vite.\\\n- 32 threads → fast `cargo` builds; 128 GiB RAM → room for 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).\\\n\\\n### Test / Stage — basement server (FreeBSD)\\\n- **Jenkins** watches Forgejo (local). Pipeline: `lint → test → cross-compile (Linux x86_64) → transfer binary` → deploy to staging.\\\n- Staging target: a **bhyve Debian VM** 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 service.)\\\n\\\n### Produce — Hetzner box (Debian)\\\n- Public entry point. **Caddy** terminates TLS and fronts the game API and static frontend.\\\n- Services run as **bare systemd units**: PostgreSQL, Redis, the Skara Brae server binary, and optionally a static-file server for the built frontend.\\\n- Jenkins deploys by SSHing 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.\\\n- **Sizing caveat:** the Hetzner box is \\\"decent, not a powerhouse\\\" and will also run Codeberg. A text MMORPG binary in Rust is light, but watch combined RAM (Codeberg + PG + Redis + app). Be ready to upsize or split.\\\n\\\n---\\\n\\\n## 6. Git Host & Mirroring\\\n\\\n```\\\nbasement (Forgejo)  ←→  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** on the **Hetzner** Debian box. Mirrors all repos for public access and off-site backup.\\\n- Codeberg instance is planned; not yet standing.\\\n- The old GIT / Jenkins Workflow note references creating bare repos on \\\"the server\\\" — superseded by Forgejo as primary.\\\n\\\n---\\\n\\\n## 7. Networking, TLS, Backups\\\n\\\n- **DNS:** project hostnames (`api.<domain>`, `play.<domain>`) → Hetzner box A/AAAA. Caddy auto-issues Let's Encrypt certs.\\\n- **Git transport:** `git@basement` (Forgejo, 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):**\\\n  - Forgejo (primary git): nightly `forgejo dump` → Hetzner (off-site relative to basement).\\\n  - Codeberg (mirror): already a backup of the repos. Its own data backed up to Hetzner Storage Box.\\\n  - PostgreSQL (prod on Hetzner): `pg_dump` → basement file server (off-site relative to Hetzner).\\\n  - workhorse dev: repos recoverable from Forgejo; local DB is ephemeral.\\\n- **Restore test:** exercise one restore of each before relying on it.\\\n\\\n---\\\n\\\n## 8. Project-Specific Application\\\n\\\n- **Skara Brae** — first project to adopt this topology. See [Skara Brae — Project Plan](joplin://088737f31c514f1da21cc42c0ab6acc1) and [Tech Stack & Architecture](joplin://980c3eb587294e4383474b94988f2f88).\\\n- Future projects (poker bots, sim-racing, fatigue-lab) inherit the same model: code on workhorse, CI/staging on basement, prod on Hetzner; adjust only where a project needs GPU (workhorse), a public endpoint (Hetzner), or long-running eval (basement).\\\n\\\n---\\\n\\\n## 9. Open Decisions (cross-project)\\\n\\\n- [ ] Canonical root: `~/dev/` (recommended) vs. `~/Development/`.\\\n- [ ] Migrate legacy IDE-folder repos now, or symlink-on-touch.\\\n- [ ] Stand up a **bhyve Debian staging VM** on the basement box (template/image to reuse across projects).\\\n- [ ] Confirm Hetzner box headroom for Codeberg + a systemd game workload; set a RAM threshold for splitting.\\\n- [ ] Backups: pick off-site targets per service and run a restore test.\\\n- [ ] Local dev: native Ubuntu packages for PG/Redis, or keep them in a VirtualBox VM to isolate from the host?\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":10586}]"
metadata_diff: {"new":{"id":"bbc49a3bbada4fcdb8fedc66abf5014e","parent_id":"cd0501fe1cc24c93bc6bc3e78fb324fd","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":1785658010225,"markup_language":1,"is_shared":0,"share_id":"","conflict_original_id":"","master_key_id":"","user_data":"","deleted_time":0},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-08-02T08:29:54.721Z
created_time: 2026-08-02T08:29:54.721Z
is_locked: 0
type_: 13