Skip to content

gl0bal01/devbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DevBox v3.0

Production-hardened provisioning for secure remote development, penetration testing, and AI workstations on Ubuntu 24.04 VPS instances.

v3.0 highlights:

  • Bootstrap-only setup.sh — tracked compose files, helper scripts, Traefik config, and middleware live under services/ and scripts/ in the repo. setup.sh rsyncs them into ~/docker/. No more heredoc sprawl.
  • SHA-pinned upstream images via docker compose config --lock-image-digests + committed docker-compose.lock.yml. Weekly CI refresh + cosign keyless signing + SBOM + SLSA provenance. See ADR-0003.
  • Verified downloads — every curl | sh in the bootstrap is routed through fetch_and_verify with pinned SHA256s in scripts/lib/download-manifest.sh. Emergency override: DEVBOX_ALLOW_UNVERIFIED=1. See ADR-0005.
  • Traefik security middleware — HSTS/frame-deny/CSP/rate-limit/IPAllowList (Tailscale IPv4 CGNAT + IPv6 ULA + loopback) wired at the entryPoint level, not globally. See ADR-0006.
  • Honest privilege modeldev is in the docker group (root-equivalent). Sudoers whitelists only ufw/tailscale/openvpn/specific systemctl. The security boundary is Tailscale ACL + SSH key auth + UFW default-deny. See docs/security.md and ADR-0011.
  • Operator-friendly opsbackup.sh with gpg/age encryption-at-rest, diagnose.sh bundle for incident reports, pre-rsync snapshots in ~/.local/share/devbox/backups/, flock guard against concurrent installs. See docs/ops.md.
  • 55/55 bats unit tests passing. Full verification matrix gated by scripts/ci/ + .github/workflows/.

Table of Contents

Overview

DevBox transforms a fresh Ubuntu 24.04 VPS into a fully configured development environment. The entire stack runs behind Tailscale with no public ports exposed except SSH.

Architecture

Two access paths are supported:

Path A — Tailscale-only (default): All services on port 80, bound to the Tailscale IP. No domain or certificates needed. HTTP over WireGuard is already encrypted.

Path B — Public HTTPS (opt-in): Adds port 443, Let's Encrypt wildcard cert via OVH DNS-01. Requires ENABLE_HTTPS=true, a domain, and ~/.config/devbox/ovh.env with OVH API credentials.

                           Path A (default)          Path B (opt-in)
                           ────────────────          ───────────────
[Your Devices] ──Tailscale──> [Traefik :80]    [Internet] ──> [Traefik :443]
                                    |                                |
                  (Tailscale IP)    |              (0.0.0.0, TLS)    |
                                    v                                v
+---------------------------[VPS]-------------------------------------------+
|                                                                           |
|   [Tailscale] <──> [Your Devices]     [Internet (optional, HTTPS only)]   |
|        |                                                                  |
|        v                                                                  |
|   [Traefik] ──────+──── [Open WebUI]    ai.internal / ai.DOMAIN           |
|        |          |                                                       |
|        |          +──── [Ollama API]    ollama.internal (basicAuth)       |
|        |          |                                                       |
|        |          +──── [Traefik Dashboard] traefik.internal (basicAuth)  |
|        |                                                                  |
|        +──> [Docker Socket Proxy] ──> /var/run/docker.sock                |
|             (internal network, read-only API access)                      |
|                                                                           |
|   [Exegol Container] <──> [HTB/THM VPN]                                   |
|        (noVNC bound via UFW to tailscale0 only — see docs/exegol.md)      |
|                                                                           |
|   [AI Dev Stack] — Claude Code, OpenCode, Goose, LLM, Fabric             |
|        (native CLI tools for AI-assisted development)                     |
|                                                                           |
+---------------------------------------------------------------------------+

Features

Core Components

Component Description
Tailscale Zero-trust mesh VPN for secure access
Traefik Internal reverse proxy with label-based routing + security middleware
Ollama Local LLM inference server (with basicAuth on the external route)
Open WebUI Chat interface for Ollama models
Exegol Penetration testing container with full toolkit (noVNC scoped to Tailscale)

AI Coding Tools (opt-in via install-ai-dev-stack.sh)

Tool Provider Purpose
Claude Code Anthropic AI-assisted coding CLI
OpenCode Open-source Multi-provider AI coding
Goose Block AI coding agent
LLM Datasette CLI for language models
Fabric danielmiessler AI prompts framework

Development Tools

  • mise: Polyglot version manager (Node, Python, Go)
  • lazygit / lazydocker / lazyvim: Terminal UIs
  • Rust + cargo-binstall + zellij: Modern terminal/dev stack
  • Bun: Fast JavaScript runtime
  • Oh-My-Zsh: Shell configuration with DevBox aliases

Security Hardening (v3.0)

  • Secrets stored in .env files with 600 permissions (rendered at install time via whitelisted envsubst, never committed)
  • Docker socket proxy prevents container escape
  • All containers run with no-new-privileges, cap_drop: ALL, explicit cap_add
  • Traefik dashboard protected with basic authentication
  • Ollama external route protected with separate basicAuth (internal openwebui→ollama bypasses auth)
  • Security headers middleware: HSTS (HTTPS mode), X-Frame-Options, nosniff, referrer-policy
  • Rate limiting: 100 req/min average, burst 50
  • IPAllowList: Tailscale CGNAT IPv4 + IPv6 ULA + loopback
  • Per-container resource limits and healthchecks
  • stop_grace_period tuned per service (60s for Ollama to drain in-flight inference)
  • Log rotation via json-file driver (max 10MB × 3 files) — drift-checked by CI
  • umask 077 + flock guard + cleanup trap in setup.sh (partial-install prevention)
  • Pre-rsync snapshot of every overwritten file to ~/.local/share/devbox/backups/<ts>/
  • SSH key-only auth, custom port, UFW default-deny, fail2ban

Requirements

  • Ubuntu 24.04 LTS
  • Docker and Docker Compose plugin (v2.22+) pre-installed
  • Minimum 4GB RAM (24GB+ recommended for Ollama with larger models)
  • Root access for initial setup
  • SSH public key for authentication

Quick Start

# Clone the repository
git clone https://github.com/gl0bal01/devbox.git
cd devbox

# (Optional) Override defaults via env vars
export DEVBOX_USER=dev
export DEVBOX_SSH_PORT=5522
export SSH_PUBLIC_KEY="$(cat ~/.ssh/id_ed25519.pub)"

# (Optional) For HTTPS mode, create the OVH credentials file FIRST:
install -d -m 0700 ~/.config/devbox
cat > ~/.config/devbox/ovh.env <<EOF
OVH_ENDPOINT=ovh-eu
OVH_APPLICATION_KEY=your-key
OVH_APPLICATION_SECRET=your-secret
OVH_CONSUMER_KEY=your-consumer-key
EOF
chmod 600 ~/.config/devbox/ovh.env
export ENABLE_HTTPS=true
export DEVBOX_DOMAIN=example.com

# Run as root
chmod +x setup.sh
sudo ./setup.sh

After setup.sh completes, follow the Post-Installation steps.

Configuration

Configuration via environment variables (overrides the defaults in setup.sh):

Variable Default Purpose
DEVBOX_USER dev Username to create
DEVBOX_EMAIL admin@example.com Email for Let's Encrypt
DEVBOX_SSH_PORT 5522 SSH port
DEVBOX_DOMAIN example.com Domain (HTTPS mode only)
ENABLE_HTTPS false Opt-in to public HTTPS
SSH_PUBLIC_KEY (unset) Your SSH pubkey string
OPENWEBUI_SECRET (auto-generated) Session secret for Open WebUI

For HTTPS mode, create ~/.config/devbox/ovh.env before running setup.sh. The script refuses ENABLE_HTTPS=true if the file is missing. OVH credentials NEVER live in tracked source per ADR-0005.

Default setup (Tailscale-only): Leave ENABLE_HTTPS=false. Services are accessible at .internal hostnames over Tailscale.

Public HTTPS: Set ENABLE_HTTPS=true, DEVBOX_DOMAIN, and create ~/.config/devbox/ovh.env. See docs/https-setup.md.

SSH Public Key (Required)

The script reads your SSH public key from these sources (in order):

  1. Environment variable: SSH_PUBLIC_KEY="ssh-ed25519 AAAA..."
  2. File: ~/.ssh/devbox_authorized_key or /root/.ssh/devbox_authorized_key
  3. Manual: Add key to ~/.ssh/authorized_keys after setup

Post-Installation

1. Verify SSH Access

From a new terminal on your local machine (do NOT close the setup terminal until this works):

ssh -p 5522 dev@YOUR_SERVER_IP

2. Authenticate Tailscale

sudo tailscale up --accept-routes --advertise-tags=tag:devbox

3. Generate Initial Image Digest Lockfiles

Per ADR-0003, lockfiles are generated by running docker compose config --lock-image-digests on the target host. Run it twice to handle the first-generation tag-strip drift (see Critic Issue #2 in the plan):

cd ~/docker/../devbox   # or wherever the repo lives
./scripts/update-images.sh --apply
./scripts/update-images.sh --apply   # second pass stabilizes
git add services/*/docker-compose.lock.yml
git commit -m "lock: initial image digests"

4. Start Services

cd ~/docker
./start-all.sh

5. Configure Local DNS

Add to /etc/hosts on your local machine (replace with your Tailscale IP):

100.X.Y.Z  ai.internal traefik.internal ollama.internal exegol.internal

Get your Tailscale IP with tailscale ip -4.

6. Install AI Coding Tools (Optional)

~/docker/install-ai-dev-stack.sh           # Interactive menu
~/docker/install-ai-dev-stack.sh --all     # Install all tools
~/docker/install-ai-dev-stack.sh --status  # Check installation status
~/docker/install-ai-dev-stack.sh --update  # Update installed tools

After installation:

claude login                     # Authenticate Claude Code
goose configure                  # Configure Goose
llm keys set openai              # Set OpenAI key for LLM
fabric --setup                   # Configure Fabric
export ANTHROPIC_API_KEY=...     # Set key for OpenCode

7. Pull Ollama Models

docker exec -it ollama ollama pull llama3.2
docker exec -it ollama ollama pull codellama

8. Verify Security

~/docker/security-check.sh

Reproducibility & Verification

DevBox ships its release artifact as a cosign-keyless-signed, SBOM-attested, SLSA-provenance-attested tarball via the .github/workflows/weekly-rebuild.yml CI pipeline. Three install modes are documented in docs/updating.md:

Install modes

Mode How Signed? Smoke-tested? Recommended for
latest git pull origin main No No Development only
weekly-YYYYMMDD git checkout weekly-20260420 or download release tarball Yes (cosign keyless) Yes Production
@sha256:<digest> Pin release tarball by SHA256 Yes Yes Maximum paranoia

latest is NOT recommended for any environment where compromise has consequences. It tracks main HEAD with no signature, no smoke test, no provenance. Use weekly-YYYYMMDD for any persistent install.

Verifying a weekly release

# Download the release tarball + signature + certificate
gh release download weekly-20260420 \
  -p devbox.tar -p devbox.tar.sha256 \
  -p devbox.tar.sig -p devbox.tar.pem

# Verify SHA256 first
sha256sum -c devbox.tar.sha256

# Verify cosign keyless signature
cosign verify-blob devbox.tar \
  --signature devbox.tar.sig \
  --certificate devbox.tar.pem \
  --certificate-identity-regexp '^https://github.com/gl0bal01/devbox/.*' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com'

Verify both --certificate-identity and --certificate-oidc-issuer — without them, any cosign keyless signature from any GitHub Actions workflow would be accepted.

Multi-arch caveat

Committed docker-compose.lock.yml files target x86_64 (the CI runner architecture). Operators on arm64 (e.g. Apple Silicon Linux VMs) must regenerate lockfiles locally with ./scripts/update-images.sh --apply on their own host before start-all.sh. Documented in ADR-0003.

Sigstore unavailability fallback

If Sigstore (Rekor/Fulcio) is down, CI fails closed and no weekly-* tag is cut. Emergency path: git checkout main && sudo ./setup.sh — treat as untrusted and re-verify by running weekly-rebuild manually as soon as Sigstore recovers. See docs/updating.md.

Usage

Service Management

start-all        # Start all services (uses --wait --wait-timeout 120)
stop-all         # Stop all services (reverse order)
status           # Show service status + Tailscale info + COMPOSE_FILE in use
security-check   # Verify security hardening (10 checks)
devbox-backup    # Encrypted backup of volumes + configs (gpg/age)
devbox-diagnose  # Bundle diagnostic info for incident reports

Optional Hardening Modules

Three opt-in operator scripts under scripts/host/ add per-target hardening on top of the baseline. Each is dry-run by default; --apply is required to mutate. See docs/harden-modules.md for full caveats.

# Scope Docker DNAT rules to a CIDR (default Tailscale CGNAT 100.64.0.0/10)
sudo scripts/host/harden-dnat-scope.sh             # dry-run
sudo scripts/host/harden-dnat-scope.sh --apply

# fail2ban traefik-auth + recidive jails (only installs when Traefik is publicly bound)
sudo scripts/host/harden-fail2ban.sh               # dry-run
sudo scripts/host/harden-fail2ban.sh --apply

# Generic age-encrypted, systemd-timed backup pipeline (per service tag)
sudo scripts/host/harden-backup-skeleton.sh \
  --tag myapp --path /home/myapp --path /var/lib/myapp   # dry-run
sudo scripts/host/harden-backup-skeleton.sh \
  --tag myapp --path /home/myapp --path /var/lib/myapp --apply

These modules support the mixed Tailscale + public topology — they act per target, not globally. A host can run some services bound to the Tailscale IP and others bound to 0.0.0.0; harden-fail2ban.sh strict- detects each Traefik container and refuses to install jails on the Tailscale-only shape (use Tailscale ACLs instead). harden-dnat-scope.sh accepts --port to scope its action to one published port.

Accessing Services

Service URL (default) URL (ENABLE_HTTPS=true)
Open WebUI http://ai.internal https://ai.DOMAIN
Traefik Dashboard http://traefik.internal http://traefik.internal
Ollama API http://ollama.internal http://ollama.internal

All .internal URLs require Tailscale. Add to /etc/hosts on your laptop:

100.X.Y.Z  ai.internal traefik.internal ollama.internal exegol.internal

Remote IDE Integration

# Set your server's Tailscale IP
export OLLAMA_SERVER_IP="100.x.x.x"

# Configure shell functions for quick AI queries (jq-safe, idempotent)
bash scripts/laptop/ollama-setup.sh

# Configure Zed editor (jq deep-merge — preserves existing settings)
bash scripts/laptop/zed-setup.sh

See docs/remote-ide-setup.md for manual SSH tunnel setup.

Penetration Testing Workflow

# Connect to HTB VPN (PID-file based, no global pkill)
htb-vpn ~/htb/lab.ovpn

# Check status
htb-vpn status

# Start Exegol with desktop (random per-container password)
exegol                             # Default: exegol-htb on port 45377
exegol osint-box --port 45378      # Multiple containers on different ports

# Rotate VNC password for a running container
exegol-reset ~/docker/exegol-reset-vnc.sh exegol-htb

# List containers
exegol-list

# Access at: http://exegol.internal:45377/vnc.html
# Username: root, password: printed once at container start
# UFW restricts the port to the tailscale0 interface only — not publicly reachable

See docs/exegol.md for the full workflow.

Shell Aliases

Defined in scripts/install/dev-zshrc and installed to ~/.zshrc. See that file for the full list — highlights:

  • Docker: dps, dpsa, dlog NAME, dex NAME bash, dc up -d, dprune
  • Git: lg (lazygit), gs, gp, gP
  • Tailscale: tsip, tsstatus, tsup, tsdown
  • DevBox: start-all, stop-all, status, security-check, exegol, exegol-reset, htb-vpn, devbox-backup, devbox-diagnose

Security

Network Exposure

  • Only SSH (port 5522) is exposed to the public internet
  • All other services are accessible only via Tailscale (or optional HTTPS via configured domain)
  • UFW firewall configured with default deny incoming
  • Traefik ports 80/443 bound to Tailscale IP — inaccessible from LAN by design
  • Exegol noVNC port: container-internal 0.0.0.0, host-side UFW rule scoped to tailscale0 interface

Honest privilege model

Per docs/security.md and ADR-0011:

dev is in the docker group. This is equivalent to root for anyone who compromises the dev account (docker socket → bind-mount /). The security boundary is the Tailscale ACL + SSH key auth + UFW default-deny, not local privilege separation.

Sudoers whitelist (only commands NOT handled by the docker boundary):

dev ALL=(root) NOPASSWD: /usr/sbin/ufw, /usr/bin/tailscale, /usr/sbin/openvpn
dev ALL=(root) NOPASSWD: /bin/systemctl restart docker, /bin/systemctl reload ufw
dev ALL=(root) PASSWD: ALL

Container Security

Measure Implementation
Secrets Management .env files with 600 perms, rendered at install time from .env.template
Docker Socket Protection Traefik uses docker-socket-proxy (read-only API subset)
Privilege Escalation Prevention All containers: no-new-privileges: true
Capability Dropping All containers: cap_drop: ALL + explicit cap_add whitelist
Resource Limits Memory, CPU, and PID limits on every container
Log Rotation json-file driver, 10MB × 3 files, drift-checked by CI
Grace Period stop_grace_period tuned per service (60s for Ollama)

Authentication

Service Method
SSH Key-based only (password disabled, root disabled)
Open WebUI Application-level (disable signup after admin creation)
Traefik Dashboard Basic Auth (dashboard-auth middleware)
Ollama external route Basic Auth (ollama-auth middleware)

Credential handling

  • ~/.devbox-credentials is written via umask 077 + install -m 0600 atomic permission drop
  • If gpg --list-keys returns a key at install time, the credentials file is GPG-encrypted and the plaintext is shred-ed
  • trap registered to shred the plaintext on SIGINT (Scenario E mitigation)

Recommendations

  1. Save credentials from ~/.devbox-credentials (or .devbox-credentials.gpg) to a password manager, then shred -u the file
  2. Disable Open WebUI signup after creating admin account
  3. Run security-check periodically
  4. Enable MagicDNS in Tailscale admin console
  5. Configure Tailscale ACLs for multi-device access control
  6. Use weekly-YYYYMMDD installs in production; verify the cosign signature before promotion

Troubleshooting

SSH Connection Refused

sudo systemctl status ssh      # Verify SSH is running
sudo ss -tlnp | grep ssh       # Check listening port
sudo ufw status                # Verify firewall

Services Not Accessible

docker ps                      # Check containers are running
docker logs traefik            # Check Traefik logs
tailscale status               # Verify Tailscale connection
status                         # DevBox status dashboard
devbox-diagnose                # Bundle full diagnostic tarball

Tailscale Authentication Failed

sudo tailscale logout
sudo tailscale up --accept-routes

Concurrent setup.sh blocked

If you see "Another devbox setup.sh is already running" — check for stale lock:

sudo ls -la /var/lock/devbox-setup.lock
sudo rm /var/lock/devbox-setup.lock   # only if no real setup is running

For more scenarios, see docs/troubleshooting.md and docs/ops.md.

Documentation

Operator docs

Document Description
docs/security.md Trust model, privilege boundaries, honest disclosure
docs/harden-modules.md Opt-in harden-*.sh modules: DNAT scoping, fail2ban, encrypted backups
docs/updating.md Digest refresh, cosign verify, install modes
docs/ops.md Backup/restore, DR runbook, 6 pre-mortem scenarios
docs/quick-reference.md Command cheat sheet
docs/https-setup.md Enable public HTTPS with OVH DNS-01
docs/exegol.md Multi-container pentest workflows
docs/ollama-optimization.md Performance tuning for Ollama
docs/remote-ide-setup.md Configure local IDE with remote Ollama
docs/troubleshooting.md Common issues and solutions
services/README.md services/ tree conventions + rendering table
scripts/laptop/README.md Configure laptop for remote Ollama
CONTRIBUTING.md How to contribute

Architectural Decision Records

All 13 ADRs are in docs/adr/:

ADR Title
0001 Extract heredoc-generated configs into a tracked services/ tree
0002 HTTPS via Compose override file
0003 SHA pinning via docker compose config --lock-image-digests
0004 setup.sh becomes a host-bootstrap-only installer
0005 Replace curl|sh with fetch_and_verify + pinned SHA256s
0006 Traefik security middleware wired at entryPoint level
0007 Per-file duplicated x-logging anchor with CI drift check
0008 Minor-pinned tag strategy with weekly refresh
0009 Template rendering with whitelisted envsubst
0010 TLS at registry + cosign keyless for release artifact
0011 Accept docker-group = root-equivalent
0012 Exegol image left unpinned (operator-controlled risk)
0013 No rollback machinery; snapshot-based recovery

Directory Structure

Repository structure (v3.0):

devbox/
├── setup.sh                            # Host bootstrap (963 lines, 4 heredocs)
├── services/                           # Tracked compose + Traefik configs
│   ├── README.md                       # services/ conventions + rendering table
│   ├── traefik/
│   │   ├── docker-compose.yml          # HTTP base with x-logging anchor
│   │   ├── docker-compose.https.yml    # HTTPS override (port 443, OVH env)
│   │   ├── docker-compose.lock.yml     # Generated by update-images.sh, committed
│   │   ├── traefik.yml                 # HTTP-only static config
│   │   ├── traefik.https.yml.template  # HTTPS variant (envsubst $USER_EMAIL)
│   │   ├── .env.template               # OVH credentials template
│   │   └── dynamic/
│   │       ├── dashboard-auth.yml.template
│   │       ├── ollama-auth.yml.template
│   │       ├── middlewares-base.yml    # frame-deny, nosniff, referrer
│   │       ├── middlewares-https.yml   # HSTS (HTTPS mode only)
│   │       ├── middlewares-rate.yml    # rate limit
│   │       └── middlewares-allowlist.yml # IPAllowList
│   └── ollama-openwebui/
│       ├── docker-compose.yml
│       ├── docker-compose.https.yml
│       ├── docker-compose.lock.yml
│       └── .env.template
├── scripts/
│   ├── host/                           # Runtime helpers (rsynced to ~/docker/)
│   │   ├── start-all.sh                # Uses --wait --wait-timeout 120
│   │   ├── stop-all.sh
│   │   ├── status.sh
│   │   ├── security-check.sh           # 10 checks
│   │   ├── exegol-start.sh             # Random password + UFW tailscale0 rule
│   │   ├── exegol-reset-vnc.sh         # Password rotation helper
│   │   ├── htb-vpn.sh                  # PID-file based, no global pkill
│   │   ├── backup.sh                   # gpg/age encryption-at-rest
│   │   ├── diagnose.sh                 # Incident-report bundle
│   │   ├── install-ai-dev-stack.sh     # User-invoked AI tools installer
│   │   ├── harden-dnat-scope.sh        # Scope Docker DNAT to a CIDR (opt-in)
│   │   ├── harden-fail2ban.sh          # traefik-auth + recidive jails (opt-in)
│   │   └── harden-backup-skeleton.sh   # Generic age-encrypted backup (opt-in)
│   ├── install/                        # Install-time tracked files
│   │   ├── dev-zshrc                   # The canonical .zshrc
│   │   └── mise-profile.sh             # /etc/profile.d/mise.sh
│   ├── laptop/                         # Run on your laptop
│   │   ├── ollama-setup.sh             # jq-safe JSON, idempotent
│   │   ├── zed-setup.sh                # jq deep-merge
│   │   ├── shell-config.txt
│   │   └── README.md
│   ├── lib/                            # Verified download infrastructure
│   │   ├── fetch-verify.sh             # fetch_and_verify function
│   │   ├── download-manifest.sh        # Pinned URL + SHA256 manifest
│   │   └── sources/                    # Per-upstream version refresh handlers
│   │       ├── bun.sh
│   │       ├── claude.sh
│   │       ├── fabric.sh
│   │       ├── goose.sh
│   │       ├── lazydocker.sh
│   │       ├── lazygit.sh
│   │       ├── neovim.sh
│   │       ├── opencode.sh
│   │       ├── rustup.sh
│   │       └── tailscale.sh
│   ├── ci/                             # CI helper scripts
│   │   ├── lint.sh                     # shellcheck + bash -n + bats
│   │   ├── check-compose-config.sh     # HTTP + HTTPS compose validation
│   │   ├── check-anchor-consistency.sh # x-logging drift check
│   │   ├── smoke-test.sh               # docker compose up + healthcheck
│   │   ├── sbom-targets.sh             # Enumerate locked image digests
│   │   └── release-notes.sh            # Weekly release note generator
│   ├── update-images.sh                # Wraps compose --lock-image-digests
│   └── update-manifest.sh              # Refreshes download-manifest.sh
├── tests/
│   ├── README.md
│   ├── lib/test-helpers.bash
│   └── unit/                           # 55/55 bats tests passing
│       ├── anchor-consistency.bats     # 8 tests
│       ├── fetch-verify.bats           # 10 tests
│       ├── htb-vpn-pidfile.bats        # 16 tests
│       ├── parse-image-line.bats       # 12 tests
│       ├── render-env.bats             # 9 tests
│       └── fixtures/bin/               # Mock openvpn, pkill, ip, sudo
├── docs/
│   ├── adr/                            # 13 ADRs
│   ├── security.md                     # Honest privilege model
│   ├── updating.md                     # Digest refresh + cosign verify
│   ├── ops.md                          # DR runbook, 6 scenarios
│   ├── exegol.md
│   ├── https-setup.md
│   ├── ollama-optimization.md
│   ├── quick-reference.md
│   ├── remote-ide-setup.md
│   └── troubleshooting.md
├── .github/
│   └── workflows/
│       ├── weekly-rebuild.yml          # cosign keyless + SBOM + SLSA provenance
│       └── pr-validate.yml             # lint + compose-config + bats
├── CONTRIBUTING.md
├── LICENSE
└── README.md

Server directory structure (after running setup.sh):

~/
├── .devbox-credentials[.gpg]           # Generated credentials (DELETE after saving)
├── .config/devbox/
│   ├── config.env                      # Install-level config (ENABLE_HTTPS, TAILSCALE_IP, COMPOSE_FILE)
│   └── ovh.env                         # OVH credentials (HTTPS mode; 600)
├── .local/share/devbox/backups/        # Pre-rsync snapshots (Scenario B)
├── docker/                             # rsync'd from services/ + scripts/host/
│   ├── traefik/                        # HTTP + HTTPS compose + dynamic middleware
│   ├── ollama-openwebui/
│   ├── exegol-workspace/
│   ├── start-all.sh, stop-all.sh, status.sh, security-check.sh
│   ├── exegol-start.sh, exegol-reset-vnc.sh, htb-vpn.sh
│   ├── backup.sh, diagnose.sh, install-ai-dev-stack.sh
│   └── (per-service .env files, rendered from .env.template at install time)
├── projects/                           # Your code projects
└── htb/                                # HTB .ovpn files

Tested Environments

Provider Instance Type Status
Hostinger KVM 8 (32GB RAM, 8 vCPU) Verified
Hetzner CX31 Compatible
DigitalOcean Droplet Compatible
AWS EC2 t3.medium+ Compatible

License

MIT License. See LICENSE for details.

References


Last updated: 2026-04-13 (v3.0 — production-readiness refactor: SHA pinning, cosign keyless, honest privilege model, 55/55 tests, 13 ADRs)