repos

docs: move project guidance to memory project, human-focus README

9458b44a by Isaac Bythewood · 18 days ago

deleted CLAUDE.md
@@ -1,81 +0,0 @@# CLAUDE.mdThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.## What This IsTaproot is a personal infrastructure repository containing dotfiles, a Docker development container, and Alpine Linux server provisioning. Everything grows from one root: dev environment config, deployment automation, and server management.## Key Commands**Set up the dev container on a Windows host (idempotent, safe to re-run):**```powershellirm https://raw.githubusercontent.com/overshard/taproot/main/containers/webdev/bootstrap.ps1 -OutFile bootstrap.ps1powershell -ExecutionPolicy Bypass -File .\bootstrap.ps1 laptop      # or "desktop"powershell -ExecutionPolicy Bypass -File .\bootstrap.ps1 laptop -Restore   # also pulls B2 snapshot```The `-ExecutionPolicy Bypass` flag scopes to that one invocation; nothing on your system changes persistently.**Build the dev container manually (bootstrap.ps1 does this for you):**```shdocker build --tag overshard/webdev:latest -f containers/webdev/Dockerfile .```**Provision a fresh Alpine server:**```shscp -r hosts/alpine/ root@server:/root/alpine && ssh root@server "sh /root/alpine/quickstart.sh"```**Bootstrap project repos on server:**```shsh hosts/alpine/srv/bootstrap.sh```There are no tests, linters, or build steps in this repo — it is pure configuration.## Helper Scripts (inside the webdev container, in PATH at `~/scripts/`)| Command | What it does ||---|---|| `restic-backup` | Manual restic backup to B2 (tags snapshot with `$RESTIC_HOST` from `~/.restic/b2-env`) || `restic-restore` | Pull latest snapshot from B2 into volumes; existing data archived first || `restic-status` | Last snapshot per host across both webdev and alpine restic repos, plus repo size || `code-sync` | `git fetch` + `git pull --ff-only` for every repo under `~/code/` (skips dirty/divergent), then clones any non-archived non-fork repos owned by overshard on GitHub that aren't local yet || `server-health-check` | SSH into alpine and run its `/root/server-health-check.sh` (apk log, restic stats, free, df, docker stats). Override target with `$ALPINE_HOST` |## Architecture- **`dotfiles/`** — Terminal and editor config (bash, git, tmux, neovim). Neovim config is Lua-based with a custom statusline. These get COPYed into the container at build time.- **`containers/webdev/`** — Ubuntu 24.04 dev container with Node (apt, only for `npx playwright install`), Python 3 (pip + uv), Bun, Rust (rustup-managed stable toolchain with rust-analyzer, clippy, rustfmt — for the axum projects and the Claude Code rust-analyzer LSP plugin), Docker CLI, Playwright Chromium (under `/opt/playwright-browsers`, for the Claude playwright MCP), and standard dev tools (neovim, tmux, git, rsync, htop, nmap, unzip, etc.). Stays alive via `sleep infinity`; entered through `docker exec -it bythewood-webdev tmux` for the TUI workflow. `openssh-client` and the baked-in `~/.ssh/config` plus the volume-resident `home_key` exist only for outbound SSH (git over SSH, `server-health-check` into alpine); there is no sshd, so nothing connects into the container. Started with `docker run --init` so PID 1 reaps zombies left behind when tmux children exit. Helper scripts (`restic-backup`, `restic-restore`, `restic-status`, `code-sync`, `server-health-check`) are baked in at `/home/dev/scripts/` and on PATH. Host setup is automated by `bootstrap.ps1`.- **`hosts/alpine/`** — Production server setup: Caddy (in Docker, auto HTTPS) on the shared `bythewood-edge` network for reverse-proxying, Docker Compose for services, restic backups to Backblaze B2, UFW firewall, push-to-deploy via git hooks.## Deployed Projects`hosts/alpine/srv/projects.conf` is the single source of truth. Format per line:`name|github_repo|branch|has_data_dir|runs_migrations`. Current manifest:| Project | Stack | Data dir | Migrations ||---|---|---|---|| `analytics` | Rust (axum) + Vite (Bun) + SQLite | yes | no || `blog.bythewood.me` | Rust (axum) + Vite (Bun) | no | no || `timelite` | Next.js + Bun (local-only, no backend) | no | no || `isaacbythewood.com` | Next.js + Bun (Pages Router, plain JS) | no | no || `status` | Rust (axum) + Vite (Bun) + SQLite | yes | no || `darkfurrow.com` | Rust (axum) + Vite (Bun) | no | no || `repos` | Rust (axum) + Vite (Bun) | no | no || `finance` | Rust (axum) + Vite (Bun) + SQLite | yes | no |Every container listens on 8000 internally; Caddy reaches them by container name on the shared `bythewood-edge` Docker network, so there's no per-project port. Update repos or flags by editing `projects.conf` and re-running the relevant provisioning step. Post-receive hooks and the bootstrap script are generated from this file; the Caddyfile is hand-maintained at `srv/caddy/Caddyfile`.## How Deployment Works`quickstart.sh` reads `projects.conf` and generates one bare repo under `/srv/git/<name>.git/` per project with a post-receive hook. Pushing to that remote triggers: `git pull`, `docker compose up --build --detach`, then `docker network connect bythewood-edge <cid>` for every container ID returned by `docker compose ps -q` (resolved that way, not by name, so the attach works regardless of compose service name or `container_name`; idempotent because `compose up` recreates containers and drops external attachments), optional `manage.py migrate`, and `docker system prune`. Caddy itself runs as a container under `/srv/docker/caddy/` and is brought up once by `quickstart.sh`; ACME certs and account keys persist at `/srv/data/caddy/` (so they're covered by the daily restic snapshot), and Caddyfile edits get picked up via `docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile`.## Conventions- Commit messages are poetic and lowercase, reflecting the "taproot" metaphor- Shell scripts target POSIX sh for Alpine compatibility- Git is configured for rebase-on-pull- Container volumes use `bythewood-*` prefix- Only Caddy publishes host ports (80, 443, 443/udp). Every other container is reached through the shared `bythewood-edge` Docker network. UFW manages the host firewall; do not add `ports:` to a project compose file or it will be exposed publicly via Docker's iptables NAT
modified README.md
@@ -7,7 +7,7 @@ Dotfiles, containers, and the configs that make a machine mine.## What is this?The single deep root beneath everything I work on. Personal infrastructureacross every machine I tend — from the development container I write code inacross every machine I tend, from the development container I write code into the Alpine host that runs in the distance.This is not a framework. It's a living configuration. It grows when something
@@ -17,10 +17,10 @@ changes and stays quiet when nothing needs to.```taproot/├── dotfiles/                       the soil — bash, git, neovim, tmux├── dotfiles/                       the soil: bash, git, neovim, tmux├── containers/│   └── webdev/│       ├── Dockerfile              the vessel — Ubuntu 24.04 dev image│       ├── Dockerfile              the vessel: Ubuntu 24.04 dev image│       ├── bootstrap.ps1           one-shot host setup (Windows)│       └── scripts/                copied to ~/scripts/ in the container│           ├── restic-backup.sh        manual restic snapshot to B2
@@ -31,28 +31,33 @@ taproot/└── hosts/    └── alpine/        ├── quickstart.sh           provision a fresh server        ├── etc/caddy/              the single gate — Caddyfile        ├── etc/docker/             daemon configuration        ├── etc/periodic/           daily backups and upgrades        ├── root/                   health checks, restore.sh        ├── etc/apk/                package repositories        ├── etc/periodic/daily/     restic autobackup, apk autoupgrade        ├── root/                   health check, restore.sh        └── srv/            ├── projects.conf       the manifest — every project, port, repo            ├── projects.conf       the manifest, every project and repo            ├── caddy/Caddyfile     the single gate, reverse proxy            └── bootstrap.sh        clone all repos into a fresh code directory```## The projects it tendsEverything deployed lives in `hosts/alpine/srv/projects.conf`. The Caddyfile,port map, and post-receive hooks all grow from that single file.Everything deployed lives in `hosts/alpine/srv/projects.conf`, one line perproject: `name|github_repo|branch|has_data_dir|runs_migrations`. The Caddyfile,post-receive hooks, and bootstrap script all grow from that single file. Everycontainer listens on 8000 internally; Caddy reaches each one by container name onthe shared `bythewood-edge` network, so there is no per-project port to track.| Project | Port | What it is ||---|---|---|| [`analytics`](https://github.com/overshard/analytics) | 8000 | Self-hosted website analytics (Django, SQLite) || [`blog.bythewood.me`](https://github.com/overshard/blog.bythewood.me) | 8100 | Personal blog (Flask, markdown files) || [`timelite`](https://github.com/overshard/timelite) | 8200 | Local-only time tracker (Next.js, IndexedDB) || [`isaacbythewood.com`](https://github.com/overshard/isaacbythewood.com) | 8300 | Personal portfolio (Next.js) || [`status`](https://github.com/overshard/status) | 8400 | Uptime monitor & status page (Django, SQLite) || [`darkfurrow.com`](https://github.com/overshard/darkfurrow.com) | 8500 | Seasonal almanac (Flask) || Project | What it is ||---|---|| [`analytics`](https://github.com/overshard/analytics) | Self-hosted website analytics (Rust axum, SQLite) || [`status`](https://github.com/overshard/status) | Uptime monitor & status page (Rust axum, SQLite) || [`finance`](https://github.com/overshard/finance) | Self-hosted market watcher (Rust axum, SQLite) || [`blog.bythewood.me`](https://github.com/overshard/blog.bythewood.me) | Personal blog (Rust axum, markdown files) || [`darkfurrow.com`](https://github.com/overshard/darkfurrow.com) | Seasonal almanac (Rust axum) || [`repos`](https://github.com/overshard/repos) | Minimal git repo browser (Rust axum) || [`isaacbythewood.com`](https://github.com/overshard/isaacbythewood.com) | Personal portfolio (Next.js) || [`timelite`](https://github.com/overshard/timelite) | Local-only time tracker (Next.js) |## The container
@@ -109,8 +114,9 @@ COPY (bash, git, tmux, neovim).## The hostAlpine Linux. Firewall, daily restic backups to Backblaze B2, and quiet dailymaintenance. The Caddyfile, port assignments, and post-receive hooks are allgenerated from `projects.conf` so the server can be rebuilt from this repo alone.maintenance. The bare repos and post-receive hooks are generated from`projects.conf` (the Caddyfile is hand-maintained) so the server can be rebuiltfrom this repo alone.Provision a fresh server: