{"id":50464624,"url":"https://github.com/overshard/taproot","last_synced_at":"2026-06-01T06:32:51.999Z","repository":{"id":349020714,"uuid":"1200668036","full_name":"overshard/taproot","owner":"overshard","description":"What holds when the surface turns. Dotfiles, containers, and the configs that make a machine mine.","archived":false,"fork":false,"pushed_at":"2026-05-20T01:13:03.000Z","size":128,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-20T04:31:18.191Z","etag":null,"topics":["alpine-linux","caddy","development-environment","docker","dotfiles","homelab","infrastructure","neovim","server-configuration","tmux"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/overshard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-03T17:28:16.000Z","updated_at":"2026-05-20T01:13:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/overshard/taproot","commit_stats":null,"previous_names":["overshard/taproot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/overshard/taproot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Ftaproot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Ftaproot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Ftaproot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Ftaproot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/overshard","download_url":"https://codeload.github.com/overshard/taproot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Ftaproot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33763649,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alpine-linux","caddy","development-environment","docker","dotfiles","homelab","infrastructure","neovim","server-configuration","tmux"],"created_at":"2026-06-01T06:32:50.849Z","updated_at":"2026-06-01T06:32:51.978Z","avatar_url":"https://github.com/overshard.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Taproot\n\nWhat holds when the surface turns.\n\nDotfiles, containers, and the configs that make a machine mine.\n\n## What is this?\n\nThe single deep root beneath everything I work on. Personal infrastructure\nacross every machine I tend — from the development container I write code in\nto the Alpine host that runs in the distance.\n\nThis is not a framework. It's a living configuration. It grows when something\nchanges and stays quiet when nothing needs to.\n\n## Structure\n\n```\ntaproot/\n├── dotfiles/                       the soil — bash, git, neovim, tmux\n│   └── host/                       host-side configs (Zed, Windows ssh-config)\n├── containers/\n│   └── webdev/\n│       ├── Dockerfile              the vessel — Ubuntu 24.04 dev image\n│       ├── bootstrap.ps1           one-shot host setup (Windows)\n│       ├── entrypoint.sh           starts sshd, then exec's CMD\n│       └── scripts/                copied to ~/scripts/ in the container\n│           ├── restic-backup.sh        manual restic snapshot to B2\n│           ├── restic-restore.sh       pull latest snapshot from B2\n│           ├── restic-status.sh        last snapshot per host across repos\n│           ├── code-sync.sh            pull existing repos + clone new ones from GitHub\n│           └── server-health-check.sh  ssh into alpine and run its health check\n└── hosts/\n    └── alpine/\n        ├── quickstart.sh           provision a fresh server\n        ├── etc/caddy/              the single gate — Caddyfile\n        ├── etc/docker/             daemon configuration\n        ├── etc/periodic/           daily backups and upgrades\n        ├── root/                   health checks, restore.sh\n        └── srv/\n            ├── projects.conf       the manifest — every project, port, repo\n            └── bootstrap.sh        clone all repos into a fresh code directory\n```\n\n## The projects it tends\n\nEverything deployed lives in `hosts/alpine/srv/projects.conf`. The Caddyfile,\nport map, and post-receive hooks all grow from that single file.\n\n| Project | Port | What it is |\n|---|---|---|\n| [`analytics`](https://github.com/overshard/analytics) | 8000 | Self-hosted website analytics (Django, SQLite) |\n| [`blog.bythewood.me`](https://github.com/overshard/blog.bythewood.me) | 8100 | Personal blog (Flask, markdown files) |\n| [`timelite`](https://github.com/overshard/timelite) | 8200 | Local-only time tracker (Next.js, IndexedDB) |\n| [`isaacbythewood.com`](https://github.com/overshard/isaacbythewood.com) | 8300 | Personal portfolio (Next.js) |\n| [`status`](https://github.com/overshard/status) | 8400 | Uptime monitor \u0026 status page (Django, SQLite) |\n| [`darkfurrow.com`](https://github.com/overshard/darkfurrow.com) | 8500 | Seasonal almanac (Flask) |\n\n## The container\n\nAn Ubuntu 24.04 development workstation with everything already in the ground:\nPython (uv), Node, Bun, Docker CLI, neovim, tmux, Claude, and Playwright\nChromium (for the Claude playwright MCP). Kept alive with `sleep infinity`.\n\n### Bootstrap on a fresh Windows host\n\nPrereqs: Docker Desktop installed and running, an SSH key at\n`$HOME\\.ssh\\home_key` (and `.pub`) added to GitHub. Nothing else.\n\n```powershell\nirm https://raw.githubusercontent.com/overshard/taproot/master/containers/webdev/bootstrap.ps1 -OutFile bootstrap.ps1\npowershell -ExecutionPolicy Bypass -File .\\bootstrap.ps1 laptop\n```\n\n`-ExecutionPolicy Bypass` is needed because PowerShell blocks scripts pulled\nfrom the internet by default; the flag scopes to that one invocation, no\npersistent system change. Use `desktop` or `laptop` as the first arg to tag\nthis machine's restic snapshots. Re-run any time; every step is idempotent.\n\nBootstrap creates the four `bythewood-*` volumes, clones taproot into\n`bythewood-code` via a throwaway helper container (so the host filesystem stays\nclean), builds the image using `docker.sock` and the volume-resident taproot,\nruns the container, copies your host SSH key into the volume, and prompts for\nrestic credentials. Pass `-Force` to pull the latest taproot, rebuild the\nimage, and recreate the container; pass `-Restore` to also pull data from B2.\n\nThen connect:\n\n```sh\ndocker exec -it bythewood-webdev tmux       # TUI workflow\nssh -p 2222 dev@localhost                   # editor remote-dev (Zed, VS Code, JetBrains)\n```\n\n### Helper scripts inside the container\n\nAll in `~/scripts/` and on `PATH`:\n\n| Command | What it does |\n|---|---|\n| `restic-backup`        | Manual restic backup to B2; snapshot tagged with `$RESTIC_HOST` |\n| `restic-restore`       | Pull latest snapshot from B2; existing data archived first |\n| `restic-status`        | Last snapshot per host across both restic repos, plus repo size |\n| `code-sync`            | `git fetch \u0026\u0026 git pull --ff-only` for every repo under `~/code/`, then clones any non-archived non-fork repos owned by overshard on GitHub that aren't local yet |\n| `server-health-check`  | SSH into alpine and run its `/root/server-health-check.sh`. Override target with `$ALPINE_HOST` |\n\n## The dotfiles\n\nMinimal by intention. I respect defaults and only override what earns it.\nTwo flavors:\n\n- **`dotfiles/`** baked into the container at build time via COPY (bash, git,\n  tmux, neovim).\n- **`dotfiles/host/`** copied by hand on a fresh Windows machine. Bootstrap\n  doesn't manage these to avoid trampling other entries you have:\n  - `dotfiles/host/zed-settings.json` -\u003e `%APPDATA%\\Zed\\settings.json`\n  - `dotfiles/host/ssh-config` -\u003e `~\\.ssh\\config` (merge with existing entries)\n\n## The host\n\nAlpine Linux. Firewall, daily restic backups to Backblaze B2, and quiet daily\nmaintenance. The Caddyfile, port assignments, and post-receive hooks are all\ngenerated from `projects.conf` so the server can be rebuilt from this repo alone.\n\nProvision a fresh server:\n\n```sh\nscp -r hosts/alpine/ root@your-server:/root/alpine\nssh root@your-server \"cd /root/alpine \u0026\u0026 sh quickstart.sh\"\n```\n\nBootstrap a fresh code directory with all repos and server remotes:\n\n```sh\ncd ~/code\nsh taproot/hosts/alpine/srv/bootstrap.sh\n```\n\n## Backups\n\nBoth the webdev container and the alpine host back up to a single Backblaze B2\nbucket (`overshard-backups`) using restic, one repo per kind:\n\n| Repository | What's in it |\n|---|---|\n| `b2:overshard-backups:webdev` | Per-machine snapshots from desktop and laptop (`~/.claude`, `~/code`, `~/.ssh`). Each snapshot tagged with `$RESTIC_HOST` (`desktop` or `laptop`); retention applies per-machine. |\n| `b2:overshard-backups:alpine` | Daily snapshots from the production server (`/srv/git`, `/srv/docker`, `/srv/data`). |\n\nRetention: 7 daily, 4 weekly, 6 monthly per host, pruned after each backup.\nRestic passwords and B2 application keys live in 1Password.\n\n### Webdev credentials\n\nPlaced automatically by `bootstrap.ps1` (it prompts for them and writes into\nthe `bythewood-restic` volume). The `b2-env` file ends up looking like:\n\n```sh\nexport B2_ACCOUNT_ID=\"\u003ckeyID\u003e\"\nexport B2_ACCOUNT_KEY=\"\u003capplicationKey\u003e\"\nexport RESTIC_HOST=\"desktop\"   # or \"laptop\"\n```\n\nOptional: drop the alpine repo password at `~/.restic/alpine-password`\n(prompted for during bootstrap) so `restic-status` can report on the alpine repo too.\n\n### Alpine credentials\n\nPlaced by hand after `quickstart.sh` runs (the same paste-from-1Password\npattern), at `/root/.restic/password` and `/root/.restic/b2-env`. The alpine\n`b2-env` should also have `RESTIC_HOST=\"alpine\"`.\n\n### Daily flow\n\n```sh\nrestic-backup   # take a snapshot from this machine\nrestic-status   # check fleet health (both repos, every host) from anywhere\ncode-sync       # pull every repo under ~/code/ + clone any new ones from GitHub\n```\n\n### Restore\n\nExisting data is moved aside to `~/before-restore-\u003cUTC-ISO\u003e/` (webdev) or\n`/root/before-restore-\u003cUTC-ISO\u003e/srv/` (alpine) before restic writes the\nsnapshot back:\n\n```sh\nrestic-restore                          # webdev (from inside the container)\nssh root@server /root/restore.sh --up   # alpine; --up auto-restarts containers\n```\n\n## Philosophy\n\n- Keep defaults until they fail you.\n- One repo, one root, everything grows from here.\n- If it's not worth tending, remove it.\n\n## License\n\nBSD 2-Clause. See [LICENSE.md](LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovershard%2Ftaproot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fovershard%2Ftaproot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovershard%2Ftaproot/lists"}