# Lightsail Node.js host

This is the API box for `re360muse-backend`. RDS and R2 stay off this
disk. Do **not** use the Bitnami Node.js blueprint — **Ubuntu 24.04**,
same region as RDS (`eu-west-1`).

`scripts/deploy.sh` is the RealCredi-style installer. First run: apt
upgrade, Node 20, Caddy, GitHub deploy key, clone, `openssl rand
-base64 32` for JWT / session / DB password, write `.env`, `migrate
deploy`, systemd. Later releases: push **`main`** (self-hosted GitHub
Actions runner on this box) or `scripts/update.sh`. Existing `.env` is
never overwritten.

## 1. Create the instance

1. Lightsail → **Create instance** → Ubuntu 24.04 → 2–4 GB →
   `re360muse-api`.
2. Attach a **static IP**.
3. Networking: **22** from your IP; **80/443** open; not 4000 or 5432.
4. Account → Advanced → **VPC peering**. RDS SG: 5432 from the
   Lightsail CIDR.

## 2. First boot (from your laptop)

Copy the script, then run it as root on the box. It will print an SSH
public key — add that as a **read-only deploy key** on
`MuseTec/re360muse-backend`, press Enter, and it clones.

```bash
scp scripts/deploy.sh ubuntu@STATIC_IP:/tmp/re360muse-deploy.sh
ssh ubuntu@STATIC_IP
sudo bash /tmp/re360muse-deploy.sh \
  --db-host re360muse.XXXX.eu-west-1.rds.amazonaws.com \
  --s3-endpoint https://ACCOUNT_ID.r2.cloudflarestorage.com \
  --aws-access-key "$AWS_ACCESS_KEY_ID" \
  --aws-secret-key "$AWS_SECRET_ACCESS_KEY" \
  --paystack-secret-key "$PAYSTACK_SECRET_KEY" \
  --rds-admin-user re360muse_admin \
  --rds-admin-password "$RDS_MASTER_PASSWORD" \
  --seed
```

`--db-password` is optional. If omitted, the script generates one with
`openssl rand -base64 32` and builds:

```text
DATABASE_URL=postgresql://re360muse_app:GENERATED@HOST:5432/re360muse?sslmode=require
```

`--rds-admin-password` applies `create-app-user.sql` over SSL. If you
skip it, the SQL file is at `/opt/re360muse/create-app-user.sql` (mode
600) — run that as the master user from Lightsail, then re-run deploy
if migrate failed.

Secrets are written to `/opt/re360muse/api/.env` mode 600 and **not
printed**. Paystack and R2 keys are the flags you passed; JWT and
session are generated.

Cloudflare Origin CA → `/etc/caddy/certs/origin.pem` and `origin.key`.
DNS `api` A record → static IP, proxied, **Full (strict)**, WebSockets
on.

## 3. Later releases

Push or merge to **`main`** on `MuseTec/re360muse-backend`. GitHub
Actions compiles on a hosted runner, then a **self-hosted runner on
this box** runs `scripts/update.sh --branch main`. `develop` does not
deploy. SSH can stay limited to your IP — the runner dials out to
GitHub.

### One-time runner

Mint a registration token (expires in one hour; do not commit it):

```bash
gh api -X POST repos/MuseTec/re360muse-backend/actions/runners/registration-token --jq .token
```

On the box. If `install-github-runner.sh` is not in the clone yet, scp it
first (same pattern as first-boot `deploy.sh`):

```bash
scp scripts/install-github-runner.sh ubuntu@STATIC_IP:/tmp/install-github-runner.sh
ssh ubuntu@STATIC_IP
GH_RUNNER_TOKEN=... sudo bash /tmp/install-github-runner.sh
# later, after main has the script:
# GH_RUNNER_TOKEN=... sudo bash /opt/re360muse/api/scripts/install-github-runner.sh
```

Repo **Settings → Actions → Runners** should show `re360muse-api` idle.
GitHub → **Actions → Deploy Lightsail API → Run workflow** is the
manual trigger. Do not open port 22 to GitHub-hosted IPs.

### Manual fallback

```bash
sudo /opt/re360muse/api/scripts/update.sh
# same as:
sudo /opt/re360muse/api/scripts/deploy.sh
```

`update.sh` always `npm ci` (lockfile). `prisma migrate deploy` — not
`migrate dev` / `db push`. `--upgrade-os` if you also want apt upgrade.
`--rotate-secrets` regenerates JWT/session only.

If git pull has not landed a migration that production already needs
(Prisma `P2021`), copy that folder from the laptop into
`/opt/re360muse/api/prisma/migrations/` then run `npx prisma migrate
deploy` on the box (same pattern as a normal update). Commit the folder
so the next `update.sh` keeps it.

```bash
curl -fsS http://127.0.0.1:4000/api/health
journalctl -u re360muse-api -f
```

Node binds `127.0.0.1:4000` in production (`HOST`). `/api/docs` is not
served on this host. After first boot, tighten the box:

```bash
sudo bash /opt/re360muse/api/scripts/harden-host.sh
```

That enables UFW (22/80/443), fail2ban for sshd, and deletes leftover
`create-app-user.sql`. It does not remove Lightsail’s `ubuntu
NOPASSWD:ALL` — stripping that without an ubuntu password locks sudo.

Paystack webhook:
`https://api.re360muse.com/api/v1/payments/webhooks/paystack`

## 4. Not on this box

Postgres data directory, upload files as source of truth, `.env` in
git, public 5432 or 4000.
