Doc updates
This commit is contained in:
@@ -668,9 +668,9 @@ All six are titled `[Backend] …`, assigned to Vinod, in the **community-rule**
|
||||
|
||||
| # | Linear | Title | Depends on |
|
||||
| - | ------ | ----- | ---------- |
|
||||
| 1 | [CR-96](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names) | `[Backend] Bridge CLOUDRON_* env vars to canonical names` | none — can ship now |
|
||||
| 2 | [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push) | `[Backend] Container image registry: choose, build, push` | registry decision (handoff §5) |
|
||||
| 3 | [CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke) | `[Backend] Cloudron staging install + smoke` | CR-96 + CR-97 + Cloudron CLI access + staging DNS |
|
||||
| 1 | [CR-96](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names) | `[Backend] Cloudron-native env vars` | **Done** — merged |
|
||||
| 2 | [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push) | `[Backend] Container image registry: choose, build, push` | **Done** — first image `0.1.0` verified |
|
||||
| 3 | [CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke) | `[Backend] Cloudron staging install + smoke` | Cloudron CLI token (§2) — **next** |
|
||||
| 4 | [CR-99](https://linear.app/community-rule/issue/CR-99/backend-cloudron-production-install-apex-cutover) | `[Backend] Cloudron production install + apex cutover` | CR-98 green for the agreed overlap window |
|
||||
| 5 | [CR-100](https://linear.app/community-rule/issue/CR-100/backend-steady-state-operator-runbook) | `[Backend] Steady-state operator runbook` | CR-98 (write what we actually did) |
|
||||
| 6 | [CR-101](https://linear.app/community-rule/issue/CR-101/backend-decommission-legacy-communityrule-lamp-app) | `[Backend] Decommission legacy CommunityRule LAMP app` | CR-99 + sign-off window |
|
||||
@@ -682,21 +682,28 @@ All six are titled `[Backend] …`, assigned to Vinod, in the **community-rule**
|
||||
|
||||
| Order | Linear | Repo PR / branch | Kind | Status | Blocked by |
|
||||
| ----- | ------ | ---------------- | ---- | ------ | ---------- |
|
||||
| 1 | [CR-96](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names) | `adilallo/Backend/BridgeCloudronEnv` — *[Backend] Cloudron-native environment variables* | repo | **Open** | — |
|
||||
| 2 | [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push) | TBD — registry choice + build/push (Dockerfile / CI) | repo | **Next** | CR-96 merged + registry decision ([ops-backend-deploy.md](ops-backend-deploy.md) §6) |
|
||||
| 1 | [CR-96](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names) | `adilallo/Backend/BridgeCloudronEnv` — *[Backend] Cloudron-native environment variables* | repo | **Done** | — |
|
||||
| 2 | [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push) | Container registry packaging + `docker-release.sh` | repo | **Done** | — |
|
||||
| — | [CR-102](https://linear.app/community-rule/issue/CR-102/backend-decide-fate-of-legacy-rules-table-read-only-export) | TBD — optional repo PR if export tooling/docs needed | product / repo | **Parallel** | row count from legacy MySQL (pre–CR-99 backup) |
|
||||
| 3 | [CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke) | — (ops checklist; doc tweaks only if smoke finds gaps) | ops | Backlog | CR-96 + CR-97 + Cloudron CLI token + staging DNS |
|
||||
| 3 | [CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke) | — (ops checklist; [ops-backend-deploy.md §10](ops-backend-deploy.md#10-staging-install--smoke-cr-98)) | ops | **Next** | Cloudron CLI token only |
|
||||
| 4 | [CR-100](https://linear.app/community-rule/issue/CR-100/backend-steady-state-operator-runbook) | TBD — `docs/guides/ops-runbook.md` | docs | Backlog | CR-98 (write what we actually did) |
|
||||
| 5 | [CR-99](https://linear.app/community-rule/issue/CR-99/backend-cloudron-production-install-apex-cutover) | — (ops; maintenance window) | ops | Backlog | CR-98 green + CR-102 resolved |
|
||||
| 6 | [CR-101](https://linear.app/community-rule/issue/CR-101/backend-decommission-legacy-communityrule-lamp-app) | — (ops; uninstall LAMP slot) | ops | Backlog | CR-99 + sign-off window |
|
||||
|
||||
**What's next:** merge **CR-96** PR, then open **CR-97** on its own branch. Start **CR-102** product decision in parallel so it is resolved before the CR-99 cutover window.
|
||||
**What's next:** **CR-98** — staging install + smoke at `staging.communityrule.info`
|
||||
([ops-backend-deploy.md §10](ops-backend-deploy.md#10-staging-install--smoke-cr-98)).
|
||||
Start **CR-102** product decision in parallel so it is resolved before the CR-99
|
||||
cutover window.
|
||||
|
||||
**Per-ticket detail:**
|
||||
|
||||
1. **Cloudron-native env vars (CR-96).** **Shipped in repo** on branch `adilallo/Backend/BridgeCloudronEnv` (PR open). App reads `CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*` only (no `DATABASE_URL` / `SMTP_URL` shim). Local dev uses the same names in `.env`. SMTP URL assembled in [`lib/server/env.ts`](../../lib/server/env.ts); mail senders use `getSmtpUrl()`. Acceptance: with only `CLOUDRON_*` set, app connects to DB and sends mail; unit tests in `tests/unit/env.test.ts`.
|
||||
2. **Container image registry: choose, build, push.** Acceptance: `docker pull <registry>/communityrule:<tag>` works from a Cloudron-reachable network. CI builds and pushes on merge to `main` (stretch).
|
||||
3. **Cloudron staging install + smoke.** Acceptance: `curl https://<staging>/api/health` returns `{"ok":true,"database":"connected"}`; magic-link request → click link → `GET /api/auth/session` returns a user; publishing a rule succeeds.
|
||||
1. **Cloudron-native env vars (CR-96).** **Done.** App reads `CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*` only (no `DATABASE_URL` / `SMTP_URL` shim). Prisma datasource uses `CLOUDRON_POSTGRESQL_URL`. Local dev uses the same names in `.env`. SMTP URL assembled in [`lib/server/env.ts`](../../lib/server/env.ts); mail senders use `getSmtpUrl()`. `scripts/start.sh` does not bridge env names.
|
||||
2. **Container image registry (CR-97).** **Done.** Gitea registry on `git.medlab.host`; repo `CommunityRule/community-rule` is **public** (package visibility inherits from repo). Image `git.medlab.host/communityrule/community-rule:0.1.0`, built `linux/amd64` via `./scripts/docker-release.sh`. Anonymous pull verified. CI on merge to `main` deferred (no hosted runners).
|
||||
3. **Cloudron staging install + smoke (CR-98).** **Next.** Full checklist in [ops-backend-deploy.md §10](ops-backend-deploy.md#10-staging-install--smoke-cr-98). Summary:
|
||||
- **Prereqs:** Cloudron CLI token (only outstanding item); CR-96 + CR-97 done.
|
||||
- **Install:** `cloudron install --location staging.communityrule.info` from repo root (manifest supplies `dockerimage`; migrations run in `start.sh`).
|
||||
- **Configure:** `SESSION_SECRET`, `SMTP_FROM`, `NEXT_PUBLIC_ENABLE_BACKEND_SYNC=true`, `UPLOAD_ROOT=/app/data/uploads`.
|
||||
- **Acceptance:** `GET /api/health` → `{"ok":true,"database":"connected"}`; magic-link sign-in end-to-end; publish a rule succeeds.
|
||||
4. **Cloudron production install + DNS cutover.** Acceptance: production subdomain resolves to the new app; old subdomain still works during overlap; sign-in + publish succeed against production; backups confirmed.
|
||||
5. **Steady-state operator runbook.** Lives at `docs/guides/ops-runbook.md` (sibling to the handoff). Covers deploy a new version, rollback, restore drill cadence, multi-instance limitations from [`backend-roadmap.md`](backend-roadmap.md) §5/§7. Acceptance: a fresh reader can deploy + roll back using only this doc.
|
||||
6. **Decommission legacy Express/MySQL backend.** Acceptance: old Cloudron app stopped + uninstalled; old MySQL addon backed up once and removed; legacy Gitea repo README updated to point at this app. Priority: Low.
|
||||
@@ -822,7 +829,7 @@ Tickets **10–11** can be deferred without blocking the core “auth + drafts +
|
||||
|
||||
## Linear (Community-rule team)
|
||||
|
||||
**Main chain (historical):** **CR-72 → CR-83** was the original **strict sequence**; **repo + Linear status today:** **CR-72–CR-79**, **CR-83**, **CR-84**, **CR-85**, **CR-88**, **CR-89** are **Done**; **CR-77** (publish) **Done**; **CR-80–CR-81** remain **Backlog** (web vitals, public rule detail). **CR-82** covered by local `migrate:smoke` (see Ticket 11). **CR-83** (admin handoff) shipped as a narrow handoff sheet; the Cloudron deployment pipeline is split into **`[Backend]` follow-ups CR-96–CR-102** — see [PR plan (CR-96 – CR-102)](#pr-plan-cr-96--cr-102) under Ticket 12 (**CR-96** repo PR open; **CR-97** next). **Parallel (still open):** **CR-86** / Ticket 15 (**Backlog** — publish **not** a blocker); **CR-103** / Ticket 20 (change account email); **CR-93** (**Backlog**); **CR-90** / Ticket 18 (stakeholder invites); **CR-91** / Ticket 19 (`Add` button behavior); **Ticket 21 / [CR-113](https://linear.app/community-rule/issue/CR-113/backend-create-flow-file-uploads-community-photo-custom-method)** (create-flow file uploads).
|
||||
**Main chain (historical):** **CR-72 → CR-83** was the original **strict sequence**; **repo + Linear status today:** **CR-72–CR-79**, **CR-83**, **CR-84**, **CR-85**, **CR-88**, **CR-89** are **Done**; **CR-77** (publish) **Done**; **CR-80–CR-81** remain **Backlog** (web vitals, public rule detail). **CR-82** covered by local `migrate:smoke` (see Ticket 11). **CR-83** (admin handoff) shipped as a narrow handoff sheet; the Cloudron deployment pipeline is split into **`[Backend]` follow-ups CR-96–CR-102** — see [PR plan (CR-96 – CR-102)](#pr-plan-cr-96--cr-102) under Ticket 12 (**CR-96** + **CR-97** **Done**; **CR-98** **next**). **Parallel (still open):** **CR-86** / Ticket 15 (**Backlog** — publish **not** a blocker); **CR-103** / Ticket 20 (change account email); **CR-93** (**Backlog**); **CR-90** / Ticket 18 (stakeholder invites); **CR-91** / Ticket 19 (`Add` button behavior); **Ticket 21 / [CR-113](https://linear.app/community-rule/issue/CR-113/backend-create-flow-file-uploads-community-photo-custom-method)** (create-flow file uploads).
|
||||
|
||||
| Doc ticket | Linear | Title (short) | Deploy PR / tracking |
|
||||
| ---------: | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------------------- |
|
||||
@@ -838,9 +845,9 @@ Tickets **10–11** can be deferred without blocking the core “auth + drafts +
|
||||
| 10 | [CR-81](https://linear.app/community-rule/issue/CR-81/backend-public-rule-detail-page-get-apirulesid-optional) | Public rule detail (optional) | — |
|
||||
| 11 | [CR-82](https://linear.app/community-rule/issue/CR-82/backend-ci-postgres-migration-smoke-optional) | Local migrate smoke (**Done in repo**; optional remote CI) | — |
|
||||
| 12 | [CR-83](https://linear.app/community-rule/issue/CR-83/backend-stagingproduction-runbook-admin-handoff-docsops-backend) | Ops admin handoff (Cloudron) **Done** | — |
|
||||
| 12.1 | [CR-96](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names) | Cloudron-native env vars | **Open** — `adilallo/Backend/BridgeCloudronEnv` |
|
||||
| 12.2 | [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push) | Container image registry + CI | **Next** — own branch after CR-96 |
|
||||
| 12.3 | [CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke) | Cloudron staging install + smoke | Ops — after CR-96 + CR-97 |
|
||||
| 12.1 | [CR-96](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names) | Cloudron-native env vars | **Done** |
|
||||
| 12.2 | [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push) | Container image registry + CI | **Done** — image `0.1.0` |
|
||||
| 12.3 | [CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke) | Cloudron staging install + smoke | **Next** — [ops-backend-deploy.md §10](ops-backend-deploy.md#10-staging-install--smoke-cr-98) |
|
||||
| 12.4 | [CR-99](https://linear.app/community-rule/issue/CR-99/backend-cloudron-production-install-apex-cutover) | Production install + apex cutover | Ops — after CR-98 + CR-102 |
|
||||
| 12.5 | [CR-100](https://linear.app/community-rule/issue/CR-100/backend-steady-state-operator-runbook) | Steady-state operator runbook | Docs PR — after CR-98 |
|
||||
| 12.6 | [CR-101](https://linear.app/community-rule/issue/CR-101/backend-decommission-legacy-communityrule-lamp-app) | Decommission legacy LAMP app | Ops — after CR-99 + sign-off |
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
This doc captures everything needed to deploy the new CommunityRule
|
||||
(Next.js + Postgres) onto MEDLab's Cloudron and replace the legacy
|
||||
LAMP-packaged service at `communityrule.info`. Cloudron admin access
|
||||
has been granted and the container registry is wired up (§6, §9); the
|
||||
remaining gates are CR-96 (env bridging) and CR-98 (staging install).
|
||||
has been granted, CR-96 (Cloudron-native env vars) and CR-97 (container
|
||||
registry + first image push) are done; the remaining gate is
|
||||
[CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke)
|
||||
(staging install + smoke — §10).
|
||||
|
||||
> **For a plain-language summary to hand to MEDLab's Cloudron admin,
|
||||
> see [`../relaunch-brief.md`](../relaunch-brief.md).** This doc is the
|
||||
@@ -76,10 +78,12 @@ per-app in the manifest and provisioned at install time.
|
||||
with the legacy service; SES relay accepts it).
|
||||
- `NEXT_PUBLIC_ENABLE_BACKEND_SYNC=true` — turns on Postgres draft
|
||||
persistence for signed-in users. Required in production.
|
||||
- `UPLOAD_ROOT` — absolute path to a writable directory (typically on the
|
||||
Cloudron **localstorage** mount) for `POST /api/uploads` (community photo +
|
||||
custom-method attachments). When unset, upload routes return
|
||||
`server_misconfigured`. See [CONTRIBUTING.md](../../CONTRIBUTING.md) API table.
|
||||
- `UPLOAD_ROOT` — absolute path to a writable directory on the Cloudron
|
||||
**localstorage** mount for `POST /api/uploads` (community photo +
|
||||
custom-method attachments). Use **`/app/data/uploads`** on Cloudron
|
||||
(`start.sh` chowns `/app/data` for the `node` user). When unset, upload
|
||||
routes return `server_misconfigured`. See [CONTRIBUTING.md](../../CONTRIBUTING.md)
|
||||
API table.
|
||||
|
||||
## 4. Platform settings
|
||||
|
||||
@@ -89,9 +93,10 @@ per-app in the manifest and provisioned at install time.
|
||||
([`app/api/health/route.ts`](../../app/api/health/route.ts) returns
|
||||
`200 {"ok":true,"database":"connected"}` when healthy, `503`
|
||||
otherwise).
|
||||
- Memory limit: start at **512 MiB** (matches what the legacy LAMP
|
||||
app has been running fine on for two years); raise if Next.js
|
||||
standalone OOMs under load.
|
||||
- Memory limit: **768 MiB** in
|
||||
[`CloudronManifest.json`](../../CloudronManifest.json) (`memoryLimit:
|
||||
805306368`). The legacy LAMP app ran at 512 MiB; raise further only if
|
||||
Next.js standalone OOMs under load.
|
||||
- Backups: Cloudron's automatic backups are already on for the host
|
||||
(legacy app shows weekly snapshots ~451 MB each). Same default
|
||||
applies to new apps.
|
||||
@@ -107,10 +112,14 @@ apex.
|
||||
|
||||
### Phases
|
||||
|
||||
1. **Staging install** — `cloudron install --image <our-image>
|
||||
--location staging.communityrule.info`. Set env vars from §3. Run
|
||||
`prisma migrate deploy`. Smoke per
|
||||
[CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke).
|
||||
1. **Staging install** — from a checkout whose
|
||||
[`CloudronManifest.json`](../../CloudronManifest.json) matches the pushed
|
||||
image tag, run `cloudron install --location staging.communityrule.info`.
|
||||
Cloudron reads `dockerimage` from the manifest (no `--image` flag). Set
|
||||
manual env vars from §3. `prisma migrate deploy` runs automatically in
|
||||
[`scripts/start.sh`](../../scripts/start.sh) on container start. Smoke per
|
||||
[CR-98](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke)
|
||||
(§12).
|
||||
2. **Soft launch / acceptance** — share the staging URL with a small
|
||||
group, exercise sign-in + publish + draft sync end-to-end. Hold
|
||||
here until confident.
|
||||
@@ -152,18 +161,24 @@ Product decisions (closed):
|
||||
Infra decision closed:
|
||||
|
||||
3. **Container registry — Gitea Container Registry on `git.medlab.host`.**
|
||||
Same host as Cloudron (`193.46.198.90`); container package is set
|
||||
**public** to sidestep the [same-host docker-login "socket hangup"
|
||||
Same host as Cloudron (`193.46.198.90`). The
|
||||
[`CommunityRule/community-rule`](https://git.medlab.host/CommunityRule/community-rule)
|
||||
repo must be **public** so the container package inherits public visibility
|
||||
(Gitea does not expose per-package visibility toggles — visibility follows
|
||||
the owning repo). Public pull sidesteps the [same-host docker-login
|
||||
"socket hangup"
|
||||
bug](https://forum.cloudron.io/topic/14572/private-docker-registry-in-cloudron),
|
||||
so Cloudron pulls without credentials. Push auth from operator
|
||||
laptops uses a Gitea personal access token (`read:package` +
|
||||
`write:package`). Canonical image ref:
|
||||
`git.medlab.host/communityrule/community-rule:<tag>`. Operator
|
||||
build/push workflow lives in [§9](#9-build-and-push-image-workflow).
|
||||
Tracked in [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push).
|
||||
Fallback if same-host pull ever breaks: install the **Cloudron
|
||||
Container Registry** app and re-tag against its hostname; no other
|
||||
changes required.
|
||||
so Cloudron pulls without credentials. Push auth from operator laptops uses
|
||||
a Gitea personal access token (`read:package` + `write:package`). Canonical
|
||||
image ref: `git.medlab.host/communityrule/community-rule:<tag>`. Images are
|
||||
built **`linux/amd64` only** (Cloudron host is x86_64). Operator build/push
|
||||
workflow lives in [§9](#9-build-and-push-image-workflow). First verified
|
||||
image: `…:0.1.0` (digest
|
||||
`sha256:e652f9f4bfa4154412cc9d8b63d55c94a128e8935579d101b5ab8977e2080e52`).
|
||||
Tracked in [CR-97](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push)
|
||||
(**Done**). Fallback if same-host pull ever breaks: install the **Cloudron
|
||||
Container Registry** app and re-tag against its hostname; no other changes
|
||||
required.
|
||||
|
||||
## 7. Old vs new deltas
|
||||
|
||||
@@ -199,17 +214,16 @@ All filed in Linear, titled `[Backend] …`, assigned to me, in the
|
||||
**Community-rule** team, **Backlog** state.
|
||||
|
||||
1. [**CR-96**](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names)
|
||||
— `[Backend] Cloudron-native env vars` (shipped: app reads
|
||||
— `[Backend] Cloudron-native env vars` (**Done** — app reads
|
||||
`CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*` only).
|
||||
2. [**CR-97**](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push)
|
||||
— `[Backend] Container image registry: choose, build, push`.
|
||||
Registry decided (§6.3); packaging + build/push workflow shipped
|
||||
(§9). Closes after the first verified `docker pull` of the pushed
|
||||
image (no Cloudron-side install required to close this ticket;
|
||||
that's CR-98).
|
||||
— `[Backend] Container image registry: choose, build, push` (**Done**).
|
||||
Registry decided (§6.3); packaging + build/push workflow shipped (§9).
|
||||
First image pushed and verified via anonymous `docker pull` (§9).
|
||||
3. [**CR-98**](https://linear.app/community-rule/issue/CR-98/backend-cloudron-staging-install-smoke)
|
||||
— `[Backend] Cloudron staging install + smoke` at
|
||||
`staging.communityrule.info`. Blocked by CR-96 + CR-97.
|
||||
`staging.communityrule.info`. **Next** — checklist in §10. Requires
|
||||
Cloudron CLI token (§2) only; CR-96 and CR-97 are done.
|
||||
4. [**CR-99**](https://linear.app/community-rule/issue/CR-99/backend-cloudron-production-install-apex-cutover)
|
||||
— `[Backend] Cloudron production install + apex cutover`.
|
||||
Side-by-side cutover at scheduled low-traffic window per §5.
|
||||
@@ -273,16 +287,24 @@ standalone server.
|
||||
Override the tag with `TAG=v0.1.1 ./scripts/docker-release.sh` for
|
||||
semver releases. The script prints the exact `dockerimage` line to
|
||||
paste back into the manifest.
|
||||
3. **First push only:** in Gitea, navigate to the `CommunityRule` org
|
||||
→ Packages → `community-rule` → Settings → set **Visibility: Public**.
|
||||
3. **First push only:** confirm the
|
||||
[`CommunityRule/community-rule`](https://git.medlab.host/CommunityRule/community-rule)
|
||||
repo is **Public** (Settings → General). Gitea inherits container-package
|
||||
visibility from the repo — there is no per-package visibility toggle. Org
|
||||
owners are not required if you have repo-admin rights on this repo.
|
||||
4. **Verify the pull works without credentials** (simulates Cloudron's
|
||||
anonymous pull):
|
||||
|
||||
```bash
|
||||
docker logout git.medlab.host
|
||||
docker pull git.medlab.host/communityrule/community-rule:<tag>
|
||||
# Image is linux/amd64 only. On Apple Silicon, add --platform:
|
||||
docker pull --platform linux/amd64 git.medlab.host/communityrule/community-rule:<tag>
|
||||
```
|
||||
|
||||
A bare `docker pull` on arm64 Macs fails with "no matching manifest for
|
||||
linux/arm64" — that is expected and does **not** indicate an auth problem.
|
||||
Cloudron (x86_64) pulls the amd64 manifest without `--platform`.
|
||||
|
||||
5. **Commit the manifest change** alongside any code changes that
|
||||
shipped in this build, so the manifest and image stay in lockstep.
|
||||
|
||||
@@ -311,13 +333,95 @@ today, and the manual workflow above is acceptable for v1 staging and
|
||||
production. Revisit when runners return or when release cadence
|
||||
justifies the runner cost.
|
||||
|
||||
## 10. Rate limiting (single-instance deploys)
|
||||
## 10. Staging install + smoke (CR-98)
|
||||
|
||||
**Goal:** Install the pushed image at `staging.communityrule.info`, configure
|
||||
production env vars, and verify the vertical slice before apex cutover
|
||||
([CR-99](https://linear.app/community-rule/issue/CR-99/backend-cloudron-production-install-apex-cutover)).
|
||||
|
||||
**Prerequisites (all satisfied unless noted):**
|
||||
|
||||
- [x] **CR-96** — app reads `CLOUDRON_POSTGRESQL_URL` and
|
||||
`CLOUDRON_MAIL_SMTP_*` only ([`lib/server/env.ts`](../../lib/server/env.ts),
|
||||
[`prisma/schema.prisma`](../../prisma/schema.prisma)). No `DATABASE_URL` /
|
||||
`SMTP_URL` shim.
|
||||
- [x] **CR-97** — image pushed to
|
||||
`git.medlab.host/communityrule/community-rule:0.1.0` (or current tag in
|
||||
manifest); repo is **public**; anonymous amd64 pull verified (§9).
|
||||
- [ ] **Cloudron CLI token** — generate at *Profile → API Tokens* on
|
||||
`cloud.medlab.host`; save in 1Password (§2).
|
||||
- [x] **Cloudron admin login** on `cloud.medlab.host` (§2).
|
||||
- [x] **DNS** — `communityrule.info` managed in Cloudron; staging subdomain
|
||||
will be provisioned at install time.
|
||||
|
||||
**Install steps:**
|
||||
|
||||
1. **Checkout** a commit whose [`CloudronManifest.json`](../../CloudronManifest.json)
|
||||
`version`, `dockerimage`, and `memoryLimit` match the image you intend to
|
||||
run (currently `0.1.0` →
|
||||
`git.medlab.host/communityrule/community-rule:0.1.0`).
|
||||
2. **Log in to Cloudron CLI:**
|
||||
```bash
|
||||
cloudron login cloud.medlab.host
|
||||
```
|
||||
3. **Install** from the repo root (manifest is read automatically):
|
||||
```bash
|
||||
cloudron install --location staging.communityrule.info
|
||||
```
|
||||
Cloudron provisions **postgresql**, **sendmail**, and **localstorage**
|
||||
addons from the manifest, pulls the image (no registry credentials needed),
|
||||
and starts the container. `scripts/start.sh` chowns `/app/data`, runs
|
||||
`prisma migrate deploy`, then execs the Next.js server.
|
||||
4. **Set manual env vars** (Cloudron does not inject these):
|
||||
```bash
|
||||
cloudron configure --app <app-id> --set-env \
|
||||
SESSION_SECRET="$(openssl rand -hex 32)" \
|
||||
SMTP_FROM="Community Rule <hello@communityrule.info>" \
|
||||
NEXT_PUBLIC_ENABLE_BACKEND_SYNC=true \
|
||||
UPLOAD_ROOT=/app/data/uploads
|
||||
```
|
||||
Rotating `SESSION_SECRET` logs everyone out. `SMTP_FROM` must be an address
|
||||
SES accepts on `communityrule.info` (platform mail addon is SES-relayed with
|
||||
custom-from allowed — §3).
|
||||
5. **Confirm the app is running** in the Cloudron dashboard (Logs tab). Look
|
||||
for a clean `prisma migrate deploy` and Next.js listening on port 3000.
|
||||
|
||||
**Smoke checklist (acceptance):**
|
||||
|
||||
- [ ] **Health:** `curl -sS https://staging.communityrule.info/api/health`
|
||||
returns `200` with `{"ok":true,"database":"connected"}`.
|
||||
- [ ] **Magic link:** request sign-in from the UI → email arrives at a real
|
||||
inbox → click link → land signed in →
|
||||
`GET /api/auth/session` returns a user. Confirm the link host matches
|
||||
`staging.communityrule.info` (reverse proxy / `Host` alignment).
|
||||
- [ ] **Publish:** complete create flow → publish a rule → public rule detail
|
||||
loads.
|
||||
- [ ] **Draft sync (optional):** signed-in Save & Exit persists to Postgres;
|
||||
resume works after re-login.
|
||||
- [ ] **Upload (optional):** with `UPLOAD_ROOT` set, attach a community photo
|
||||
in create flow and confirm it renders after publish.
|
||||
|
||||
**If something fails:**
|
||||
|
||||
| Symptom | Likely cause | Check |
|
||||
| ------- | ------------ | ----- |
|
||||
| Image pull error on install | Repo still private, or wrong tag in manifest | §6.3; `docker pull --platform linux/amd64 …` from laptop |
|
||||
| Health `503` / `database: disconnected` | Postgres addon not provisioned or URL missing | Cloudron app → Environment; expect `CLOUDRON_POSTGRESQL_URL` |
|
||||
| Magic link not sent | Mail addon or `SMTP_FROM` | Cloudron mail logs; `CLOUDRON_MAIL_SMTP_*` vars |
|
||||
| Upload `server_misconfigured` | `UPLOAD_ROOT` unset | Set to `/app/data/uploads` (§3) |
|
||||
| Container crash on start | Migration failure | App logs around `prisma migrate deploy` |
|
||||
|
||||
**Done when:** all smoke checklist items pass. Then proceed to soft-launch
|
||||
(§5 phase 2) and, when ready, [CR-99](https://linear.app/community-rule/issue/CR-99/backend-cloudron-production-install-apex-cutover)
|
||||
apex cutover.
|
||||
|
||||
## 11. Rate limiting (single-instance deploys)
|
||||
|
||||
The app uses an **in-memory** rate limiter in [`lib/server/rateLimit.ts`](../../lib/server/rateLimit.ts) (magic-link requests, organizer inquiry, etc.). This is sufficient for the current **single Cloudron container** per environment.
|
||||
|
||||
**Before horizontal scale-out** (multiple app instances behind a load balancer), replace or back the limiter with a shared store (e.g. Redis) so per-IP / per-user windows apply across instances. Until then, document expected limits in the steady-state runbook ([CR-100](https://linear.app/community-rule/issue/CR-100/backend-steady-state-operator-runbook)).
|
||||
|
||||
## 11. Related docs
|
||||
## 12. Related docs
|
||||
|
||||
- [`docs/guides/backend-roadmap.md`](backend-roadmap.md) §11
|
||||
(environments) and §8 (Prisma migrations policy).
|
||||
|
||||
@@ -47,9 +47,10 @@ Rollback plan during the window: restore the legacy backup to a scratch Cloudron
|
||||
|
||||
Roughly this order:
|
||||
|
||||
1. **Code prep** — small local change so the app reads Cloudron's injected `CLOUDRON_*` env vars natively. No infra impact.
|
||||
2. **Build and push the app image** to a container registry.
|
||||
3. **Install at staging** subdomain, smoke test, soft launch.
|
||||
1. ~~**Code prep** — Cloudron-native env vars (CR-96).~~ **Done.**
|
||||
2. ~~**Build and push the app image** to Gitea registry (CR-97).~~ **Done**
|
||||
(`git.medlab.host/communityrule/community-rule:0.1.0`).
|
||||
3. **Install at staging** subdomain, smoke test, soft launch (CR-98).
|
||||
4. **Apex cutover window** — the brief downtime above.
|
||||
5. **Uninstall legacy**, archive legacy repos.
|
||||
6. **Write the steady-state runbook** based on what actually worked.
|
||||
|
||||
Reference in New Issue
Block a user