restructure docs: eliminate duplication across md files
This commit is contained in:
+61
-155
@@ -1,193 +1,99 @@
|
||||
# Use Cases
|
||||
|
||||
Expected behaviors for each operation this repo supports. See README.md for
|
||||
instructions, ARCHITECTURE.md for technical details.
|
||||
|
||||
## 0. Initial Bitwarden Secrets Manager setup
|
||||
|
||||
One-time setup before anything else works. Create the secrets project and
|
||||
machine accounts in Bitwarden Secrets Manager.
|
||||
**Trigger:** one-time, before anything else works.
|
||||
|
||||
1. Create a Bitwarden organization (free tier)
|
||||
2. Enable Secrets Manager for the organization
|
||||
3. Create a project named `hantim`
|
||||
4. Create all required secrets (see README.md Secrets table for the full list)
|
||||
5. Create machine accounts:
|
||||
- `hantim-server` — grant access to the `hantim` project
|
||||
- `hantim-ci` — reserved for future CI use
|
||||
6. Generate access tokens for each machine account
|
||||
**Expected outcome:**
|
||||
- Bitwarden organization with Secrets Manager enabled
|
||||
- Project `hantim` with all secrets listed in README.md
|
||||
- Machine accounts `hantim-server` and `hantim-ci` with project access
|
||||
- Access tokens generated for each machine account
|
||||
|
||||
## 1. Provision a new server
|
||||
|
||||
Set up a fresh Rocky Linux 9 server from scratch.
|
||||
**Trigger:** run `bootstrap.sh` on a fresh Rocky Linux 9 install.
|
||||
|
||||
```bash
|
||||
dnf install -y git
|
||||
git clone https://git.timothykim.net/hantim/hantim-server.git /opt/hantim
|
||||
bash /opt/hantim/scripts/bootstrap.sh
|
||||
```
|
||||
**Prerequisites:** DNS pointed to server, bws access token ready.
|
||||
|
||||
**What happens:**
|
||||
- Installs all dependencies (jq, certbot, unzip, Docker)
|
||||
- Installs `bws` CLI, prompts for Bitwarden Secrets Manager access token
|
||||
- Fetches registry token and deploy SSH public key via `bws`
|
||||
- Logs into Docker registry
|
||||
- Creates deploy user with restricted SSH + sudo
|
||||
- Runs `configure.sh`:
|
||||
- Opens firewall ports (HTTP, HTTPS, Garage RPC)
|
||||
- Sets up certbot renewal
|
||||
- Deploys all services via `deploy.sh` (generates `.env` files, issues certs, starts containers)
|
||||
|
||||
**Prerequisites:**
|
||||
- DNS for all configured domains must point to the server
|
||||
- Bitwarden Secrets Manager access token for the `hantim-server` machine account
|
||||
- Docker images must exist in the Gitea registry (or apps will start on next push)
|
||||
**Expected outcome:**
|
||||
- All dependencies installed (Docker, certbot, bws, etc.)
|
||||
- `deploy` user exists with restricted SSH and sudo
|
||||
- Firewall allows HTTP, HTTPS, Garage RPC
|
||||
- Certbot renewal configured
|
||||
- All services running (nginx, apps, Garage)
|
||||
- SSL certs issued for all configured domains
|
||||
- `.env` files generated from bws for services with `.env.keys`
|
||||
|
||||
## 2. Restore from backup
|
||||
|
||||
Identical to provisioning a new server. Run the same three commands on a
|
||||
fresh server.
|
||||
**Trigger:** same three commands as provisioning on a fresh server.
|
||||
|
||||
- `bootstrap.sh` and `configure.sh` are idempotent -- safe to re-run
|
||||
- SSL certs are re-issued automatically (Let's Encrypt)
|
||||
- Docker images are pulled from the Gitea registry
|
||||
- No data migration needed for stateless static sites
|
||||
**Expected outcome:** identical to provisioning. All scripts are idempotent.
|
||||
Certs are re-issued, images pulled from registry. No data migration needed
|
||||
for stateless static sites.
|
||||
|
||||
## 3. Add a new static site
|
||||
|
||||
From your dev machine (single command):
|
||||
**Trigger:** `./tools/new-app.sh example.com` from dev machine.
|
||||
|
||||
```bash
|
||||
./tools/new-app.sh example.com
|
||||
```
|
||||
**Prerequisites:** domain nameservers on Vultr, `bws`/`jq`/`dig` installed.
|
||||
|
||||
**Prerequisites:**
|
||||
- Bitwarden Secrets Manager must contain `hantim-new-app-script`,
|
||||
`hantim-vultr-api-key`, and `hantim-deploy-ssh-private-key`
|
||||
- Domain nameservers must be pointed to Vultr (configured on directnic.com)
|
||||
- Dependencies: `bws`, `jq`, `dig`
|
||||
- `BWS_ACCESS_TOKEN` env var or token saved at `~/.config/hantim/bws-token`
|
||||
|
||||
**What happens (fully automated):**
|
||||
1. Resolves server IP from `hantim.net`
|
||||
2. Fetches Gitea API token, Vultr API key, and deploy SSH key from Bitwarden Secrets Manager
|
||||
3. Creates DNS zone on Vultr (if needed) and A records for bare + www
|
||||
4. Waits for DNS to propagate
|
||||
5. Creates Gitea repo `hantim/example.com` from `static-site-template`
|
||||
6. Creates local files:
|
||||
- `docker/example.com/compose.yml` (container name: `example_com`)
|
||||
- `.gitea/workflows/deploy-example.com.yml`
|
||||
- `docker/nginx/conf.d/example.com.conf` (HTTP->HTTPS, bare->www, proxy)
|
||||
7. SSHes to server as deploy user, issues SSL cert via webroot (zero downtime)
|
||||
8. Creates Garage media bucket via admin API (create, allow media-key, enable website)
|
||||
9. Commits and pushes hantim-server (triggers deploy)
|
||||
10. Triggers initial build of the app repo via Gitea API
|
||||
11. Polls `https://www.example.com` until it responds (up to 3 minutes)
|
||||
|
||||
After the site is live, clone the app repo and customize:
|
||||
|
||||
```bash
|
||||
git clone git@git.timothykim.net:hantim/example.com.git
|
||||
cd example.com
|
||||
# edit static/index.html, etc.
|
||||
git add . && git commit -m "initial content" && git push
|
||||
```
|
||||
**Expected outcome:**
|
||||
- DNS A records created for bare + www
|
||||
- Gitea repo `hantim/example.com` created from template
|
||||
- Config files added: compose, nginx conf, deploy workflow
|
||||
- SSL cert issued (zero downtime)
|
||||
- Garage media bucket created with media-key access
|
||||
- First build triggered
|
||||
- `https://www.example.com` responds within 3 minutes
|
||||
- Site ready for customization via git clone + push
|
||||
|
||||
## 4. Update app code
|
||||
|
||||
Push a change to the app repo (e.g., `example.com`):
|
||||
**Trigger:** push to an app repo.
|
||||
|
||||
```bash
|
||||
cd example.com
|
||||
# make changes
|
||||
git add . && git commit -m "update content" && git push
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Gitea Actions runs `build.yml`: builds Docker image, pushes to registry
|
||||
2. SSHes into server, runs `deploy-example.com`
|
||||
3. `deploy.sh` pulls latest hantim-server, pulls new image, restarts container
|
||||
|
||||
No manual steps needed.
|
||||
**Expected outcome:**
|
||||
- Docker image built and pushed to registry
|
||||
- Container restarted on server with new image
|
||||
- No manual steps
|
||||
|
||||
## 5. Update Docker Compose config
|
||||
|
||||
Edit a compose file in this repo and push:
|
||||
**Trigger:** push compose file change to this repo.
|
||||
|
||||
```bash
|
||||
vim docker/example.com/compose.yml
|
||||
git add docker/example.com/compose.yml
|
||||
git commit -m "update example.com config"
|
||||
git push
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. `deploy-example.com.yml` workflow triggers (path match)
|
||||
2. `deploy.sh` pulls latest code, pulls image, runs `docker compose up -d`
|
||||
**Expected outcome:**
|
||||
- Path-matched workflow triggers
|
||||
- Container restarted with updated config
|
||||
|
||||
## 6. Update nginx config
|
||||
|
||||
Edit an nginx config in this repo and push:
|
||||
**Trigger:** push nginx conf change to this repo.
|
||||
|
||||
```bash
|
||||
vim docker/nginx/conf.d/example.com.conf
|
||||
git add docker/nginx/conf.d/example.com.conf
|
||||
git commit -m "update nginx config"
|
||||
git push
|
||||
```
|
||||
**Expected outcome:**
|
||||
- Config tested with `nginx -t` before applying
|
||||
- If test fails, running nginx is untouched
|
||||
- If new certs needed, issued before applying config
|
||||
- nginx reloaded with new config
|
||||
|
||||
**What happens:**
|
||||
1. `deploy-nginx.yml` workflow triggers (path match)
|
||||
2. `deploy.sh` pulls latest code
|
||||
3. If any conf files reference certs that don't exist yet, nginx is briefly
|
||||
stopped and certs are issued via standalone certbot (auto-restarts on failure)
|
||||
4. Tests nginx config with `nginx -t` -- if it fails, deploy aborts and the
|
||||
running nginx is untouched
|
||||
5. Runs `docker compose up -d` and reloads nginx
|
||||
## 7. Re-run configure
|
||||
|
||||
**Note:** Each conf file must contain server blocks for only one domain. The
|
||||
cert name is derived from the filename (e.g. `example.com.conf` -> cert
|
||||
`example.com`). Redirect-only domains get their own conf file.
|
||||
**Trigger:** push change to `scripts/configure.sh`, or run manually.
|
||||
|
||||
## 7. Re-run configure on existing server
|
||||
|
||||
Safe to do at any time, manually or via CI:
|
||||
|
||||
```bash
|
||||
cd /opt/hantim
|
||||
git pull
|
||||
bash scripts/configure.sh
|
||||
```
|
||||
|
||||
Or push a change to `scripts/configure.sh` — the `provision.yml` workflow
|
||||
triggers automatically.
|
||||
|
||||
- All steps are idempotent
|
||||
- Existing certs are skipped
|
||||
- Brief nginx downtime (seconds) while certbot checks run
|
||||
- Services are restarted
|
||||
**Expected outcome:**
|
||||
- All steps idempotent — safe to run anytime
|
||||
- Existing certs skipped
|
||||
- Services restarted
|
||||
|
||||
## 8. Upload media files
|
||||
|
||||
Media files (images, videos) are stored in Garage and served at `/media/`
|
||||
on each domain.
|
||||
**Trigger:** `aws s3 cp` or `aws s3 sync` to `s3://example.com/`.
|
||||
|
||||
```bash
|
||||
# One-time setup
|
||||
aws configure # key ID + secret from bws, region: garage, output: json
|
||||
# Then add to ~/.aws/config under [default]:
|
||||
# endpoint_url = https://s3.hantim.net
|
||||
|
||||
# Upload a single file
|
||||
aws s3 cp photo.jpg s3://example.com/photo.jpg
|
||||
|
||||
# Sync a directory
|
||||
aws s3 sync static/media/ s3://example.com/
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. File is uploaded to the Garage S3 API via `s3.hantim.net`
|
||||
2. Garage replicates the file to both nodes (hantim + argento)
|
||||
3. File is accessible at `https://www.example.com/media/photo.jpg`
|
||||
|
||||
**Local development:** Store media in `static/media/` (gitignored). The app's
|
||||
nginx serves these locally, matching the `/media/` path used in production.
|
||||
|
||||
**Reference in HTML:** Use absolute paths like `/media/photo.jpg`.
|
||||
**Expected outcome:**
|
||||
- File uploaded via S3 API (`s3.hantim.net`)
|
||||
- Replicated to both Garage nodes (hantim + argento)
|
||||
- Accessible at `https://www.example.com/media/<path>`
|
||||
- Local development: `static/media/` (gitignored) serves at `/media/`
|
||||
|
||||
Reference in New Issue
Block a user