From 2b4b93d7ea1b6d3bab17e2b2755c6f8ed09f4afb Mon Sep 17 00:00:00 2001 From: kamaji Date: Tue, 24 Feb 2026 22:37:12 -0600 Subject: [PATCH] Add Claude Code memory files and track them in git Co-Authored-By: Claude Opus 4.6 --- .../projects/-home-kamaji/memory/MEMORY.md | 279 ++++++++++++++++++ .../-home-kamaji/memory/nextsnap-files.md | 18 ++ .../projects/-home-kamaji/memory/retro-vms.md | 48 +++ .gitignore | 9 +- 4 files changed, 352 insertions(+), 2 deletions(-) create mode 100644 .claude/projects/-home-kamaji/memory/MEMORY.md create mode 100644 .claude/projects/-home-kamaji/memory/nextsnap-files.md create mode 100644 .claude/projects/-home-kamaji/memory/retro-vms.md diff --git a/.claude/projects/-home-kamaji/memory/MEMORY.md b/.claude/projects/-home-kamaji/memory/MEMORY.md new file mode 100644 index 0000000..9afbd8d --- /dev/null +++ b/.claude/projects/-home-kamaji/memory/MEMORY.md @@ -0,0 +1,279 @@ +# Project Memory + +## Receipt Manager (separate from NextSnap) +- **Source**: `compute1:~/` (receipts.html + server.py), Gitea repo `kamaji/receipt-manager` +- **Container**: `receipt-manager` on docker1 (`~/receipt-manager/`), port 8082 +- **Deploy**: `scp receipts.html server.py docker1:~/receipt-manager/ && ssh docker1 'cd ~/receipt-manager && docker-compose down && docker-compose up -d --build'` +- **Stack**: Python stdlib HTTP server, vanilla JS frontend, Nextcloud WebDAV for storage +- **NOT** part of NextSnap — separate app, separate container, separate repo + +## NextSnap Architecture +- Flask PWA on docker1 (~/nextsnap), Docker container +- Deploy: `ssh docker1 'cd ~/nextsnap && docker-compose down && docker-compose up -d --build'` +- Files accessed via SSH (scp to deploy, ssh to run commands) +- Frontend: Vanilla JS, Dexie.js (IndexedDB), Service Worker for offline-first +- Reverse proxy: nginx-reverse host, config at `/etc/nginx/sites-available/reverse-proxy.conf` + +## Infrastructure - nginx-reverse +- **VM**: on host `compute1` (migrated from console, 2026-02-22) +- **Disk**: `/1TB/vm/nginx-reverse.qcow2` on compute1 +- **IP**: `192.168.128.19` +- **Sudo**: `%sudo` group has NOPASSWD:ALL; kamaji is in sudo group +- **SSH from compute1**: Uses `id_rsa` key (added to authorized_keys) +- **Certbot**: Webroot at `/var/www/letsencrypt`, auto-renewal enabled +- **Services**: nginx (reverse proxy), iperf3 (speed test), WireGuard (wg0 — direct VPN server) +- **WireGuard**: Direct VPN server on port 51820, clients connect via `104.52.199.76:51820` +- Autostart enabled on compute1 + +## Infrastructure - docker1 +- **VM**: "docker1" on host `compute1` (migrated from "docker-box" on console, 2026-02-22) +- **Disk**: `/1TB/vm/docker-box.qcow2` on compute1 +- **RAM**: 8 GB, **vCPUs**: 8 +- **IP**: `192.168.128.5` +- **Root disk**: 24 GB `/dev/vda1` — tight with Docker; monitor usage +- **NFS mount**: `console:/Storage` → `/Storage` (in fstab with `_netdev`) +- **Nextcloud data**: symlinked `/opt/nextcloud/data` → `/Storage/nextcloud/data` +- **Nextcloud compose**: `/opt/nextcloud/docker-compose.yml` (app + redis + mariadb) +- **Nextcloud Office**: Built-in CODE (richdocumentscode AppImage), runs COOLWSD on port 9983 inside container +- Autostart enabled on compute1 + +## Nextcloud Backup (legacy) +- **Script**: `docker1:~/nextcloud-backup.sh` — hourly cron, predates snapback +- **Note**: Snapback now handles the same backups (nextcloud-data, nextcloud-app, nextcloud-db jobs) +- SSH from docker1→compute1 uses kamaji's ed25519 key; sudo rsync needs explicit SSH opts + +## Two User Classes +- **Admin**: NC admin login (username+password), full access including admin panel +- **Tech**: Field workers, login with username+PIN, access to capture/queue/browser only +- Tech users stored in `data/tech_users.json` (Docker named volume `app_data` at `/app/data`) +- PIN hashed with werkzeug.security; NC password stored plaintext (admin-visible) +- Tech user creation auto-provisions NC account via OCS API +- Session stores NC password (base64) for transparent API calls +- `session['user_type']` = `'admin'` | `'tech'`; `session['is_admin']` for nav guards +- Admin panel: tappable user list → detail modal (enable/disable, reset PIN, reset NC password, delete) +- Login page: tabbed UI — "Tech Login" (default) and "Admin Login" + +## Key Bugs & Lessons + +### Service Worker Caching (CRITICAL) +- SW was cache-first for ALL routes including HTML pages → stale code served forever +- Fix: Only `/static/` uses cache-first; pages + API use network-first +- Always bump SW cache versions when changing any static asset +- Even after SW bump, user needs one hard refresh to pick up new SW + +### DOM Element Destroyed by innerHTML (queue.html) +- `getElementById('empty-state')` returned null after `innerHTML = ''` destroyed it +- Caused silent crash on every subsequent `loadQueue()` call +- Counters still worked because `updateStats()` ran independently +- Lesson: Never reference DOM elements inside a container you clear with innerHTML + +### iOS Safari Blob Eviction +- Blobs stored in IndexedDB are file-backed references that iOS can evict +- `instanceof Blob` and `.size` checks pass but reading data throws "Load failed" +- Fix: Store as ArrayBuffer (inline bytes) + `navigator.storage.persist()` +- `Storage.getBlob(photo)` converts ArrayBuffer→Blob at read time + +### iOS Safari Fetch "Load Failed" +- Generic error for any network failure (backgrounding, connectivity) +- Upload POST fails client-side, never reaches server +- Show friendly messages, hide transient errors (< 3 retries) from UI + +### Nextcloud OCS API 412 Error +- All OCS endpoints require `OCS-APIRequest: true` header (CSRF protection) +- Without it → 412 Precondition Failed + +### Docker Volume Permissions +- Named volumes mounted to dirs created in Dockerfile inherit ownership from image +- If volume already exists with root ownership, must `docker-compose down -v` to recreate +- Container runs as `nextsnap` (uid 1000) — `/app/data` must be owned by nextsnap + +### iOS Safari `inputmode="numeric"` + `type="password"` +- Combining these causes browser validation error "string did not match expected pattern" +- Fix: Use `type="text"` with `inputmode="numeric"` and validate in JS + +### Nextcloud CODE "Document loading failed" +- COOLWSD (CODE server) stores temp files in `/tmp` jails inside the container +- Disk full → "Low disk space" / "Out of storage" in COOLWSD logs → 400/408 on proxy.php +- Memory exhaustion → inconsistent loading (works sometimes), CODE processes killed/timed out +- Fix: Free disk space, increase VM RAM; COOLWSD log at `/tmp/coolwsd.*/coolwsd.log` inside container +- After fixing, restart container so COOLWSD gets a clean start + +### MikroTik (cornerLot) +- **Model**: RB750Gr3, RouterOS 6.48.2, identity `cornerLot` +- **IP**: `192.168.88.1` (clients), `192.168.128.1` (servers) +- **SSH**: Port 65523, user `kamaji` +- **SSH keys**: RouterOS 6.x disables password auth once a key is imported; key must work or user is locked out (WinBox recovery only) +- **SSH from compute1**: Dedicated key `~/.ssh/mikrotik_rsa`, SSH config alias `mikrotik`; requires `ssh-rsa` algo + `DEFAULT:SHA1` crypto policy (RHEL 9 blocks SHA-1 by default) +- **NAT rules**: Working dstnat rules use `dst-address=104.52.199.76`, NOT `in-interface=ether1` +- **Hairpin NAT**: srcnat masquerade for `192.168.0.0/16` at rule 2 +- **Forward chain**: `action=accept` with no conditions at rule 3 (accepts all) + +### Podman SELinux on compute1 +- Volume mounts require `:z` flag (e.g. `-v /path:/mount:ro,z`) +- Without it: `PermissionError: [Errno 13] Permission denied` inside container + +### NFS Mount Not in fstab +- `/Storage` was NFS-mounted manually (`console:/Storage`) but not in fstab +- After VM reboot, Docker couldn't start nextcloud-app: broken symlink to `/Storage/nextcloud/data` +- Fix: Added `console:/Storage /Storage nfs defaults,_netdev 0 0` to fstab + +### Infrastructure - console +- **IP**: `192.168.88.5` (servers subnet) +- **Role**: KVM host (libvirt, Debian), NFS server (`/Storage`) +- **VMs remaining**: devtest1 (autostart), pihole2, unifi, win10-signed_to_outlook.com +- **Migrated off** (2026-02-22): docker-box→docker1, docker2, nginx-reverse, pihole, jellyfin2, git1 — all to compute1 +- **win10-signed_to_outlook.com**: moved from compute1 (2026-02-23), `/var/lib/libvirt/images/win10.qcow2`, 8 GB RAM, 4 vCPUs, autostart enabled, Hyper-V enlightenments enabled (vpindex, synic, stimer, frequencies, reenlightenment, tlbflush — dropped idle CPU from ~32% to ~5%) + +## Gitea (git1) +- **VM**: on host `compute1` (migrated from console, 2026-02-22) +- **Disk**: `/1TB/vm/git1.qcow2` on compute1 +- **IP**: `192.168.128.23` +- **URL**: https://git.sdanywhere.com/ +- **Version**: 1.25.4 +- **Config**: `/etc/gitea/app.ini`, runs as `kamaji` user +- **Repo root**: `/srv/git/` +- **SSH push**: Use `kamaji@git1:kamaji/.git` format (Gitea SSH wrapper) +- **Do NOT** use bare repo paths (`git1:/srv/git/...`) — pre-receive hook rejects without Gitea env +- **authorized_keys**: Plain keys (docker1, nginx-reverse, dns1) for shell access; RSA key has Gitea `command=` wrapper for git operations +- **Shell access to git1**: Use docker1 (ed25519 key), not this machine (RSA key goes through Gitea wrapper) +- **API**: Available at `https://git.sdanywhere.com/api/v1/`, generate tokens via `gitea --config /etc/gitea/app.ini admin user generate-access-token` +- **Repos**: `kamaji/receipt-manager` (Receipt Manager), `kamaji/snapback` (backup system) + +## Snapback (compute1) +- **Repo**: `kamaji/snapback` on Gitea +- **Deployed to**: `compute1:~/snapback/` +- **Backup root**: `/1TB/backups/` on compute1 +- **Cron**: `0 * * * *` runs all jobs hourly +- **SSH key**: `id_rsa` on compute1 (not ed25519) +- **Log**: `~/snapback/snapback.log` (not /var/log — not writable by kamaji) +- **ntfy**: Sends to `https://ntfy.sdanywhere.com` topic `backups` +- **Retention**: 24 hourly, 7 daily, 4 weekly +- **Jobs**: nextcloud-data (rsync), nextcloud-app (rsync+sudo), nextcloud-db (db_dump), gitea-repos (rsync), gitea-data (rsync), gitea-db (db_dump), nginx-config (rsync), nginx-certs (rsync+sudo), nginx-wireguard (rsync+sudo), mikrotik-config (db_dump, `/export`), mikrotik-backup (db_dump, local, binary `.backup`) +- **Local jobs**: `local: true` on db_dump runs command directly instead of via SSH +- **MikroTik SSH**: Dedicated key `~/.ssh/mikrotik_rsa` on compute1; SSH config alias `mikrotik` (port 65523, user kamaji, `ssh-rsa` algo for RouterOS 6.x); requires `DEFAULT:SHA1` crypto policy on compute1 (RHEL 9) +- **MikroTik helper**: `~/snapback/mikrotik-backup.sh` — creates backup on router, SCPs it off, outputs to stdout + +## Snapback Web Browser (compute1) +- **Container**: `snapback-web` via podman on compute1, port 8082 +- **Public URL**: `https://backups.sdanywhere.com` (via nginx-reverse proxy) +- **Auth**: HTTP Basic Auth (BROWSER_USER/BROWSER_PASSWORD env vars) +- **Source**: `compute1:~/snapback/web/` (Flask + gunicorn, vanilla JS SPA) +- **Mounts**: `/1TB/backups:/backups:ro,z`, `~/.ssh:/ssh:ro,z`, `config.yml:/app/config.yml:ro,z` +- **Features**: Browse snapshots, preview text/images, download files/zips, restore via rsync +- **SSH key handling**: Copies /ssh/* to /tmp/.ssh/ with correct permissions at startup +- **Restore**: Only rsync-type jobs; builds rsync command from config job metadata +- **SELinux**: Podman on compute1 requires `:z` flag on volume mounts +- **Deploy**: `ssh compute1 'podman build -t snapback-web ~/snapback/web/ && podman stop snapback-web && podman rm snapback-web && podman run -d --name snapback-web -p 8082:8082 -v /1TB/backups:/backups:ro,z -v /home/kamaji/.ssh:/ssh:ro,z -v /home/kamaji/snapback/config.yml:/app/config.yml:ro,z -e BROWSER_USER=kamaji -e BROWSER_PASSWORD= --restart unless-stopped snapback-web'` + +## Sysmon (compute1) +- **Source**: `devtest1:~/sysmon/`, deployed to `compute1:~/sysmon/` +- **Dashboard**: Podman container `sysmon` on compute1, port 8083 (pure aggregator, no `/proc`) +- **Stack**: Flask + gunicorn, vanilla JS frontend, dark theme +- **Architecture**: Single codebase, two modes controlled by `SYSMON_SERVERS` env var + - **Agent mode** (no env var): Reads local `/proc` + `sudo virsh` for VMs, serves `/api/stats` + - **Dashboard mode** (env var set): Pure aggregator, no `/proc` or CPU sampler; serves UI at `/` +- **Config**: `SYSMON_SERVERS="compute1:http://host.containers.internal:8084,console:http://192.168.88.5:8083"` +- **Agents**: compute1 (port 8084, systemd), console (port 8083, systemd) — both bare installs, `python3 -m gunicorn` +- **UI**: Hash-based routing — dashboard summary cards (`#`) + server detail view (`#`) with CPU grid, memory, load, uptime, VM table +- **VM base info**: `sudo virsh dominfo` per VM (30s cache) — name, state, vcpus, memory, autostart +- **VM CPU %**: `sudo virsh domstats --cpu-total --balloon` (5s cache), delta tracking for CPU % +- **VM memory**: From balloon stats (`available - unused`); Windows VMs lack guest-side stats, show allocated only +- **VM disk**: `sudo virsh guestinfo --filesystem` per VM (30s cache, async background refresh to avoid blocking) +- **Dashboard fetch timeout**: 8s (cold-cache agent responses can take 2-3s due to dominfo calls) +- **Deploy dashboard**: `scp ~/sysmon/{app.py,templates/index.html,run.sh,Dockerfile} compute1:~/sysmon/ && ssh compute1 'mkdir -p ~/sysmon/templates && mv ~/sysmon/index.html ~/sysmon/templates/ && ~/sysmon/run.sh'` +- **Deploy agent**: `~/sysmon/deploy-agent.sh [port]` (default port 8083) +- **SELinux note**: compute1 (RHEL 9) blocks `~/.local/bin/gunicorn` from systemd; use `python3 -m gunicorn` instead +- **Adding servers**: Add `name:url` to `SYSMON_SERVERS`, deploy agent on new host, rebuild dashboard + +## iperf3 (nginx-reverse) +- **Service**: `iperf3.service` (systemd, installed via apt) +- **Port**: 5201 (TCP+UDP) +- **DNS**: `speed.sdanywhere.com` +- **MikroTik NAT**: dstnat rules use `dst-address=104.52.199.76` (NOT `in-interface=ether1` — WAN traffic doesn't match ether1) +- **Hairpin NAT**: srcnat masquerade for `192.168.0.0/16` (rule 2 in NAT table) + +## Infrastructure - Pi-hole HA +- **pihole1** (primary): `192.168.128.3` on compute1, `/1TB/vm/pihole.qcow2`, 2 GB RAM, 2 vCPUs +- **pihole2** (replica): `192.168.128.2` on console, `/var/lib/libvirt/images/pihole2.qcow2`, 1 GB RAM, 2 vCPUs +- Both running Pi-hole v6.4, autostart enabled +- **Sync**: Nebula Sync container (`nebula-sync`) on docker1, full sync every 30 min (pihole1 → pihole2) +- **DHCP DNS**: MikroTik hands out both `192.168.128.3,192.168.128.2` +- **Cross-host redundancy**: pihole1 on compute1, pihole2 on console +- **Admin password**: both set to same password via `sudo pihole setpassword` +- **pihole2 SSH**: kamaji user, ed25519 key generated on pihole2, can SSH to pihole1 + +## Infrastructure - unifi +- **VM**: on host `console` (moved back from compute1, 2026-02-23) +- **Disk**: `/var/lib/libvirt/images/unifi.qcow2` on console +- **IP**: `192.168.128.6` +- **RAM**: 1 GB, **vCPUs**: 2 +- **Services**: UniFi Network Controller +- Autostart enabled on console + +## Infrastructure - jellyfin2 +- **VM**: on host `compute1` (migrated from console, 2026-02-22) +- **Disk**: `/1TB/vm/jellyfin2.qcow2` on compute1 +- **IP**: `192.168.128.4` +- **RAM**: 1 GB, **vCPUs**: 2 +- **NFS mount**: `console:/Storage` → `/Storage` (in fstab with `_netdev`) +- **Services**: Jellyfin media server (port 8096) +- Autostart enabled on compute1 + +## Infrastructure - zoneminder +- **VM**: on host `compute1`, cloned from debian12-template (2026-02-23) +- **Disk**: `/1TB/vm/zoneminder.qcow2` on compute1 +- **IP**: `192.168.128.20` +- **RAM**: 4 GB, **vCPUs**: 4 +- **OS**: Debian 12 Bookworm +- **Services**: ZoneMinder 1.36.33 (CCTV/surveillance), Apache2, MariaDB +- **DB**: `zm` database, user `zmuser`/`zmpass` +- **Web UI**: `http://192.168.128.20/zm/` +- **Config**: `/etc/zm/zm.conf` (group `www-data`), overrides in `/etc/zm/conf.d/` +- **ffmpeg**: Configured in `/etc/zm/conf.d/03-ffmpeg.conf` +- **Sudo**: kamaji has NOPASSWD:ALL via `/etc/sudoers.d/kamaji` +- Autostart enabled on compute1 + +## Infrastructure - compute1 (VM host) +- **IP**: `192.168.88.9` (servers subnet), also reachable as `compute1` +- **OS**: RHEL 9 (Rocky), QEMU at `/usr/libexec/qemu-kvm` +- **libvirt**: Machine type `q35` (RHEL variant), VNC graphics (no SPICE support) +- **Networking**: Bridge `br0` on `enp4s0f1` for VM bridged networking +- **VM storage**: `/1TB/vm/` on 932 GB local disk +- **VMs hosted**: docker1, docker2, nginx-reverse, pihole, jellyfin2, git1, zoneminder, macos/SheepShaver, dos/DOSBox +- **Migration notes**: Adapted from console (Debian): emulator path, machine type, macvtap→bridge, SPICE→VNC + +## Retro VMs +See [retro-vms.md](retro-vms.md) — DOSBox (192.168.128.31) and SheepShaver/Mac OS 9 (192.168.128.30) on compute1, via Guacamole + +## Guacamole (docker1) +- **Containers**: `guacamole`, `guacd`, `guac-mysql` on docker1 +- **Compose**: `docker1:~/guacamole/docker-compose.yml` +- **DB**: MySQL 8.0, user `guacuser` / `guacpassword`, database `guacamole` +- **Port**: 8080 (guacamole webapp) +- **Auth**: MySQL + LDAP (OpenLDAP, `ou=users,dc=sdanywhere,dc=com`) +- **kamaji entity_id**: 2 + +## VPN (Direct WireGuard) +- **Server**: nginx-reverse, `10.10.10.1/24` (wg0), ListenPort 51820 +- **Public endpoint**: `104.52.199.76:51820` (MikroTik dstnat → `192.168.128.19:51820`) +- **Config**: `/etc/wireguard/wg0.conf` on nginx-reverse, `wg-quick@wg0` enabled +- **Split tunnel**: Clients route `192.168.128.0/24`, `192.168.88.0/24`, `10.10.10.0/24` through VPN +- **DNS**: `192.168.128.3` (Pi-hole primary), `192.168.128.2` (Pi-hole replica) — no public fallback +- **Clients**: iPhone at `10.10.10.2/32`, MikroTik travel router at `10.10.10.3/32`, Windows laptop at `10.10.10.4/32`, Linux client at `10.10.10.5/32` +- **Routing**: No masquerade — MikroTik has static route `10.10.10.0/24 via 192.168.128.19` so LAN devices can reach VPN clients directly; bidirectional FORWARD rules; MSS clamping for TCP on both enp1s0 and wg0 +- **MikroTik notrack**: Raw prerouting rule `dst-address=10.10.10.0/24 action=notrack` — required because nginx-reverse delivers VPN→LAN packets directly on L2 (same subnet), creating asymmetric routing; without notrack, MikroTik conntrack marks reply packets as invalid and drops them intermittently +- **sdanywhere.com**: VPN services disabled (2026-02-22), still active as web server (64.227.104.26, Debian 12, login `root`) + +## ntfy (docker2) +- **VM**: on host `compute1` (migrated from console, 2026-02-22) +- **Disk**: `/1TB/vm/docker2.qcow2` on compute1 +- **IP**: `192.168.128.8` +- **Container**: `binwiederhier/ntfy` on docker2, port 8080; uses `docker compose` v2 +- **Deploy**: `ssh docker2 'cd ~/snapback && docker compose down && docker compose up -d'` +- **URL**: `https://ntfy.sdanywhere.com`, auth deny-all, admin user `kamaji` +- **Config**: `docker2:~/snapback/ntfy/server.yml`, upstream relay to ntfy.sh for mobile push +- **Topic**: `backups` (requires auth) +- Autostart enabled on compute1 + +## NextSnap File Locations +See [nextsnap-files.md](nextsnap-files.md) diff --git a/.claude/projects/-home-kamaji/memory/nextsnap-files.md b/.claude/projects/-home-kamaji/memory/nextsnap-files.md new file mode 100644 index 0000000..0beda24 --- /dev/null +++ b/.claude/projects/-home-kamaji/memory/nextsnap-files.md @@ -0,0 +1,18 @@ +# NextSnap File Locations (docker1:~/nextsnap/) + +- `app/services/tech_users.py` - Tech user JSON CRUD (create/verify_pin/list/update/delete/reset_nc_password) +- `app/services/nextcloud.py` - Nextcloud WebDAV/OCS client (incl. ocs_set_password) +- `app/routes/auth.py` - Dual login: `/api/auth/login` (admin), `/api/auth/login/tech` (tech) +- `app/routes/admin.py` - Admin API + tech user CRUD endpoints +- `app/routes/views.py` - Page routes with admin guard (tech users → /capture) +- `app/templates/login.html` - Tabbed login (Tech/Admin) +- `app/templates/admin.html` - Tech user management UI +- `app/static/js/auth.js` - Dual login form logic +- `app/static/js/admin.js` - Tech user list + detail modal +- `app/static/js/sync.js` - Upload sync engine +- `app/static/js/storage.js` - IndexedDB wrapper (Dexie) +- `app/static/sw.js` - Service worker +- `app/templates/queue.html` - Upload queue page (inline JS) +- `app/templates/capture.html` - Camera/capture page (inline JS) +- `app/templates/browser.html` - File browser with gallery viewer +- `app/static/js/filebrowser.js` - File browser + gallery logic diff --git a/.claude/projects/-home-kamaji/memory/retro-vms.md b/.claude/projects/-home-kamaji/memory/retro-vms.md new file mode 100644 index 0000000..eb17522 --- /dev/null +++ b/.claude/projects/-home-kamaji/memory/retro-vms.md @@ -0,0 +1,48 @@ +# Retro VMs (KVM/libvirt) + +Display stack: Xvfb :99 (1024x768x24) + openbox + x11vnc on port 5900 (no password). +Openbox forces all windows fullscreen/undecorated. + +## DOSBox VM (dos / 192.168.128.31) — on compute1 +- **Rebuilt**: 2026-02-23 on compute1, cloned from debian12-template +- **Disk**: `/1TB/vm/dos.qcow2` on compute1 +- **RAM**: 2 GB, **vCPUs**: 2, autostart enabled +- **SSH**: `ssh kamaji@192.168.128.31` +- **DOSBox config**: `~/dosbox/dosbox.conf` (SVGA, 64 MB RAM, `cycles=max`, fullscreen, overlay output) +- **C: drive**: `~/dosbox/drives/c/` — drop games/apps here +- **A: floppy**: `~/dosbox/drives/c/STETRIS/` mounted as `-t floppy` (Super Tetris, `A:\SUPERTET.EXE`) +- **B: floppy**: `~/dosbox/drives/c/TETRISC/` mounted as `-t floppy` (Tetris Classic 1992, `B:\TETRISC.EXE`) +- **Startup script**: `~/dosbox/start.sh` (launches Xvfb, openbox, DOSBox) +- **Systemd services**: `dosbox.service` + `x11vnc.service` (both enabled) +- **Guacamole**: "DOSBox" VNC connection (ID 10) on docker1, granted to kamaji +- **VNC access**: `vncviewer 192.168.128.31:5900` or via Guacamole + +## SheepShaver VM (macos / 192.168.128.30) — on compute1 +- **Rebuilt**: 2026-02-23 on compute1, cloned from debian12-template +- **Disk**: `/1TB/vm/macos.qcow2` on compute1 +- **RAM**: 2 GB, **vCPUs**: 2, autostart enabled +- **SSH**: `ssh kamaji@192.168.128.30` +- **Emulator**: SheepShaver built from source (`~/sheepshaver/SheepShaver-native`, kanjitalk755/macemu) +- **Note**: AppImage segfaults on Debian 12 kernel 6.1; must build from source +- **ROM**: `~/sheepshaver/newworld86.rom` (New World PPC, from GitHub oldweb-today/netcapsule) +- **Disk image**: `~/sheepshaver/macos904.dsk` (Mac OS 9, 120 MB from SourceForge bundle) +- **Prefs**: `~/.sheepshaver_prefs` (`nogui true`, 256 MB RAM, slirp networking, `screen win/1024/768`) +- **Startup script**: `~/sheepshaver/start.sh` (launches Xvfb, openbox, SheepShaver-native) +- **Systemd services**: `sheepshaver.service` + `x11vnc.service` (both enabled) +- **sysctl**: `vm.mmap_min_addr=0` in `/etc/sysctl.d/99-sheepshaver.conf` +- **Guacamole**: "Mac OS 9" VNC connection (ID 9) on docker1, granted to kamaji +- **VNC access**: `vncviewer 192.168.128.30:5900` or via Guacamole + +## FS-UAE Amiga VM (amiga / 192.168.128.32) — on compute1 +- **Created**: 2026-02-24 on compute1, cloned from debian12-template +- **Disk**: `/1TB/vm/amiga.qcow2` on compute1 +- **RAM**: 2 GB, **vCPUs**: 2, autostart enabled +- **SSH**: `ssh kamaji@192.168.128.32` +- **Emulator**: FS-UAE (apt package), A500 model +- **ROM**: Built-in AROS replacement (`kickstart_file = internal`) +- **Boot floppy**: `~/amiga/AROS-20260223-amiga-m68k-boot-floppy/bootdisk-amiga-m68k.adf` (from SourceForge nightly) +- **Config**: `~/amiga/amiga.fs-uae` (A1200, 1024x768, no sound, AROS boot floppy in DF0:) +- **Startup script**: `~/amiga/start.sh` (launches Xvfb, openbox, FS-UAE) +- **Systemd services**: `amiga.service` + `x11vnc.service` (both enabled) +- **Guacamole**: "Amiga" VNC connection (ID 11) on docker1, granted to kamaji +- **VNC access**: `vncviewer 192.168.128.32:5900` or via Guacamole diff --git a/.gitignore b/.gitignore index db761fe..97b3f84 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,13 @@ .config/ .local/ -# Claude Code files -.claude/ +# Claude Code files (keep memory tracked) +.claude/* +!.claude/projects/ +.claude/projects/* +!.claude/projects/-home-kamaji/ +.claude/projects/-home-kamaji/* +!.claude/projects/-home-kamaji/memory/ .claude.json .claude.json.backup*