diff --git a/CHANGELOG.md b/CHANGELOG.md index 003d448..f9a1956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 2026-07-03 — docs: REUSE.md introduced + +Cross-repo reuse-map rollout (docs-only). New `REUSE.md`: catalog conventions verified against all +53 apps — the canonical example app (paperless-ngx), `.felhom.yml` required fields, healthcheck +family per image type (BusyBox wget / curl / Node / Python / DB sidecars), memory-limit convention, +new-app checklist, and traps (gokapi entrypoint hack, legacy templates.json). Known README drift +recorded in §6 (NOT fixed). Also a minimal `CLAUDE.md` carrying the REUSE.md pointer + maintenance +rule (full CLAUDE.md is a separate task). + ## 2026-06-29 — App-email: calcom + nextcloud (tls_mode=plaintext :2526 + nextcloud split-From) - **nextcloud** — `smtp_mapping` with `tls_mode: plaintext` (controller injects port 2526, the plaintext-only listener) + **split From** (`from_var=MAIL_FROM_ADDRESS` + `from_domain_var=MAIL_DOMAIN` → nextcloud@felhom.eu). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e9b8c30 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,8 @@ +# CLAUDE.md — `app-catalog-felhom.eu` + +> Minimal for now (created with the REUSE.md rollout; a full CLAUDE.md refresh is a separate task). +> Cross-repo orientation lives in the workspace-root `e:\git\CLAUDE.md`; catalog format spec in `README.md`. + +- See `REUSE.md` before adding or editing an app (conventions, healthcheck families, traps). +- Update `REUSE.md` if you added/changed/deprecated a catalog-wide convention (same commit). +- Update `CHANGELOG.md` (newest on top) and overwrite `REPORT.md` with every pushed change, per workspace convention. diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..90a8055 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,5 @@ +# CONTEXT — app-catalog-felhom.eu working state + +> Created with the REUSE.md rollout (2026-07-03). History: `CHANGELOG.md`; format spec: `README.md`. + +- **2026-07-03 — `REUSE.md` exists at the repo root** (catalog conventions, healthcheck families, canonical example app = paperless-ngx, traps); maintenance rule active: update it in the same commit that changes a catalog-wide convention. diff --git a/REPORT.md b/REPORT.md index 8dec0e7..e73a38a 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,39 +1,34 @@ -# REPORT — App-email gap closure: calcom + nextcloud wired (plaintext :2526 + split-From) +# REPORT — docs: REUSE.md introduced (cross-repo reuse map) -**Date:** 2026-06-29 -**Task:** Wire cal.com + nextcloud now that the controller (v0.89.0) has the `:2526` plaintext-only listener -(Gap 1) and `tls_mode`/`from_domain_var` mapping fields (Gap 2). Catalog-only; live send-tests. +**Date:** 2026-07-03 · **Class:** docs-only (no template change, no deploy effect) +**Baseline:** `main` @ `bb81cd414afcb89e28ac60f405133f40195e6c43` -## Mappings added -- **nextcloud** (`tls_mode: plaintext`, split From): `host_var=SMTP_HOST`, `port_var=SMTP_PORT`, - `from_var=MAIL_FROM_ADDRESS`, `from_domain_var=MAIL_DOMAIN`, `from_local=nextcloud`; no `security_var` - (`SMTP_SECURE` stays empty = no TLS). Compose references the injected `${SMTP_*}` + `${MAIL_*}`. - **Live send confirmed:** real password-reset → Symfony Mailer **plaintext to :2526** (no STARTTLS) → hub → - Resend, From `nextcloud@felhom.eu` (split From), shim `hub=200`. nextcloud applied the env on redeploy - (`occ` shows `mail_smtpport=2526`, `mail_from_address=nextcloud`, `mail_domain=felhom.eu`) — every-boot - `getenv`, **no occ step needed (§8)**. -- **cal.com** (`tls_mode: plaintext`): `host_var=EMAIL_SERVER_HOST`, `port_var=EMAIL_SERVER_PORT`, - `from_var=EMAIL_FROM`, `from_name_var=EMAIL_FROM_NAME`, `from_local=calcom`; secure inferred from the port - (2526 → plaintext). Compose references the injected `${EMAIL_*}`. Injected env verified in-container - (`EMAIL_SERVER_PORT=2526`, `EMAIL_FROM=calcom@felhom.eu`). **Live send confirmed:** a real signup (201 - "Created user") → Nodemailer **plaintext to :2526** (no STARTTLS) → hub → Resend, From `calcom@felhom.eu`, - shim `hub=200`. +## What was done -## Pre-existing cal.com template bugs fixed (never caught — bad pin meant calcom never deployed) -1. **Image pin `calcom/cal.com:v4.8.7` doesn't exist** as a published image (manifest unknown) → `v4.6.9` - (nearest valid 4.x; same `EMAIL_SERVER_*` behaviour). -2. **Missing `DATABASE_DIRECT_URL`** — cal.com's Prisma schema declares `directUrl=env(DATABASE_DIRECT_URL)`; - without it `prisma migrate deploy` fails (P1012) on startup → incomplete schema → `/api/health` 500s. Added - (same direct Postgres URL). -3. **Healthcheck path `/api/health` is 404 in v4.x** → container stayed unhealthy → Traefik wouldn't route it. - Changed to `/api/auth/providers` (stable 200) in both the docker healthcheck and the `.felhom.yml` probe. +- **`REUSE.md`** created at the repo root (58 lines, light §2/§5-focused variant per spec): + the canonical example app (**paperless-ngx** — copy its structure), `.felhom.yml` required-field + set (verified across all 53 apps), deploy_fields conventions, the five Docker-healthcheck families + (BusyBox wget / curl / Node / Python / DB sidecars) each with a cited example, the memory + request/limit convention (`mem_limit` = sum of compose limits; limits enforced, no reservations), + the new-app checklist, and traps (gokapi entrypoint hack; legacy `templates.json` + + `scripts/generate-customer.sh`; uptime-kuma's image-specific healthcheck binary). +- **`CLAUDE.md`** created (minimal — this repo had none): the REUSE.md pointer + same-commit + maintenance rule + the workspace CHANGELOG/REPORT convention. A full CLAUDE.md is a separate task. +- `CHANGELOG.md` entry + `CONTEXT.md` created (one-liner). -## Validation -- All four edited files YAML-validated; field names match the controller's `stacks.SMTPMapping` tags. -- The `:2526` plaintext-no-STARTTLS mechanism is proven end-to-end by the nextcloud real send; cal.com uses the - identical mechanism (Nodemailer) with its env verified. +## Verification -## Notes -- **immich still unwired by decision** (Gap 3 — no SMTP env; needs config-file injection). -- Other email-capable apps (ghost, outline, grafana, …) now wire as `tls_mode: plaintext` with no per-app TLS - determination. No secrets in any committed file. +`python felhom.eu/scripts/reuse_refs_check.py e:/git/app-catalog-felhom.eu` → +`OK [app-catalog-felhom.eu]: 10 cited paths, all exist`. Sweep: 53 apps scanned (all 53 descriptors +share the same top-level key set; optional extras verified per file). + +## Observations (NOT acted on) — README drift, recorded in REUSE.md §6 + +- README lists `templates/filebrowser/` (doesn't exist); omits `templates/recipe-importer/` (does). +- README field-type table omits `subdomain` (all 53 apps use it) and `secret_input` (romm); lists + `boolean` which nothing uses. +- README says smtp_mapping = "Vaultwarden, Mealie"; six apps carry it (calcom, gitea, mealie, + nextcloud, rallly, vaultwarden). +- Healthcheck URL style drift (localhost vs 127.0.0.1, `curl -f` vs `-sf`, two Node one-liner styles). + +Cross-repo summary + full sweep counts: `felhom.eu/REPORT.md`. diff --git a/REUSE.md b/REUSE.md new file mode 100644 index 0000000..565c5c2 --- /dev/null +++ b/REUSE.md @@ -0,0 +1,58 @@ +# REUSE.md — app-catalog-felhom.eu + +> Before adding or editing an app, check here. Conventions to copy, traps to avoid. +> Maintenance: update in the SAME commit that changes a catalog-wide convention. +> Entries cite real files. Line numbers are landmarks only — reconfirm before editing. + +## 1. Canonical helpers + +None — this repo is templates/config, not code. See §2/§5. + +## 2. Canonical patterns (copy structure from THE named file) + +| Pattern | Canonical file | Key traits | +|---|---|---| +| **The one canonical example app** | `templates/paperless-ngx/` (both files) | Multi-container (app + postgres + redis), HDD + userdata mounts, full deploy_fields spectrum (domain/subdomain/secret/password/text/path/select). Copy this structure for any new app. | +| `.felhom.yml` required fields | `templates/paperless-ngx/.felhom.yml` | All 53 apps: `display_name`, `description` (Hungarian), `category`, `subdomain`, `slug`, `resources{mem_request, mem_limit, pi_compatible, needs_hdd}`, `deploy_fields`, `app_info{tagline, use_cases, first_steps, ...}`, `healthcheck`. Optional: `smtp_mapping` (email-capable apps), `open_path` (non-root landing page, e.g. ghost). | +| deploy_fields conventions | `templates/paperless-ngx/.felhom.yml` (`deploy_fields:` block) | Every app starts with `DOMAIN` (type `domain`) + `SUBDOMAIN` (type `subdomain`, `locked_after_deploy: true`). Secrets: `type: secret` + `generate:` — dominant generators `password:24` (DB passwords) and `hex:32` (app secret keys); `password:16` for shown admin passwords (`type: password`). HDD apps add `HDD_PATH` (`type: path`, placeholder `/mnt/felhom-drives/hdd_1`, locked). Labels/descriptions in Hungarian. | +| Controller-side health probe | `templates/vaultwarden/.felhom.yml` (`healthcheck:` block) | `healthcheck.checks[]` with `type: http` (port only), `type: api` (port + `path` + `expect.status: 200`), or `type: tcp` (port only — mealie, crafty-controller). Prefer `api` with a real health path when the app has one. | +| Docker healthcheck — BusyBox/wget images | `templates/vaultwarden/docker-compose.yml` (~L49) | `test: ["CMD", "wget", "--spider", "-q", "http://localhost:/"]`. Most common family (~20 apps, e.g. homebox, glance). | +| Docker healthcheck — curl-capable images | `templates/paperless-ngx/docker-compose.yml` (~L76) | `test: ["CMD", "curl", "-f", "http://localhost:/"]` (~18 apps: jellyfin, immich, sonarr…). | +| Docker healthcheck — Node images (no wget/curl) | `templates/rallly/docker-compose.yml` (~L49) | `test: ["CMD", "node", "-e", "require('http').get(...)"]` — used when the image lacks wget (that was rallly's actual bug). | +| Docker healthcheck — Python images | `templates/mealie/docker-compose.yml` (~L47) | `test: ["CMD-SHELL", "python3 -c \"import socket; s=socket.create_connection(('localhost',),2); s.close()\""]` (mealie, crafty-controller). tandoor/wger use `urllib.request` variants for real HTTP checks. | +| Docker healthcheck — DB/Redis sidecars | `templates/paperless-ngx/docker-compose.yml` (~L107, L129) | postgres: `pg_isready -U -d `; mariadb: `healthcheck.sh --connect --innodb_initialized`; redis: `redis-cli ping`. App container gets `depends_on: : condition: service_healthy`. | +| Memory convention | `templates/paperless-ngx/docker-compose.yml` (~L71) + `.felhom.yml resources:` | EVERY service has `deploy.resources.limits.memory` (compose is the enforcement). NO `reservations` anywhere. `.felhom.yml mem_limit` = SUM of all containers' limits (see paperless header comment: 768+256+128=1152M); `mem_request` = expected steady-state usage, display-only. | +| Compose file skeleton | `templates/paperless-ngx/docker-compose.yml` (header) | Header comment (app, domain, DB type, RAM math, Pi), `restart: unless-stopped`, `TZ=Europe/Budapest`, explicit `container_name`, `traefik-public` external network + `-internal` for DBs, Traefik labels with ``Host(`${SUBDOMAIN}.${DOMAIN}`)``, named volumes for DB/config (NVMe), `${HDD_PATH}/appdata//...` for bulk data, `${USERDATA_PATH}/...` for customer-browsable content. | +| App-email (SMTP shim) opt-in | `templates/vaultwarden/.felhom.yml` (`smtp_mapping:`) + README.md §smtp_mapping | `smtp_mapping` maps shim host/port/security/from to the app's own env names; compose MUST reference the mapped `${VAR:-}` keys with empty defaults. STARTTLS if the app can accept self-signed certs, else `security_value: "NONE"` plaintext (or the :2526 plaintext listener for STARTTLS-insistent clients — see calcom/nextcloud). | + +## 3. Dangerous lookalikes — do NOT copy + +| Trap | Why it bites | Use instead | +|---|---|---| +| `templates/gokapi/docker-compose.yml` custom `entrypoint` seeding `config.json` + `--deployment-password` | One-off hack because Gokapi has no env-var headless setup (pinned to ConfigVersion 21 / v1.9.6). Copying this entrypoint pattern to another app will break on image updates. | Normal env-var config via `deploy_fields`; entrypoint-seeding only as last resort. | +| `templates.json` + `scripts/generate-customer.sh` | LEGACY Portainer-only mechanism (marked so in README.md). New apps do NOT need entries here; the controller syncs `templates//` directly. | Just `templates//{docker-compose.yml,.felhom.yml}`. | +| `templates/uptime-kuma/docker-compose.yml` `test: ["CMD", "extra/healthcheck"]` | Image-provided binary, unique to this image — not a portable pattern. | Pick the wget/curl/node/python family matching your image (§2). | + +## 4. Seams & interfaces (cross-repo) + +- Controller pulls this repo (default 15m) and copies `templates//docker-compose.yml` + `.felhom.yml` to `/opt/docker/stacks//`; it NEVER overwrites deployed `app.yaml` / `.env`; SHA-256 change detection. Contract description: `README.md` §"How It Works". +- `.felhom.yml` is the contract surface consumed by felhom-controller (repo `felhom-controller/`): `deploy_fields` drive the deploy wizard, `resources` the deploy screen hints, `healthcheck.checks` the controller-side probe, `smtp_mapping` the app-email injection at deploy/redeploy. +- Assets are NOT in this repo: logo/screenshots resolve from felhom.eu via `slug` (`{assets.base_url}/assets/{slug}-logo.webp` — see comment block in `templates/paperless-ngx/.felhom.yml`). +- Commit+push to main IS the deploy: the controller picks changes up on next sync. + +## 5. Extension points (adding a new app) + +1. `templates//docker-compose.yml` — copy `templates/paperless-ngx/docker-compose.yml` skeleton; every service needs `container_name`, `restart: unless-stopped`, `TZ=Europe/Budapest`, `deploy.resources.limits.memory`, a healthcheck (family per §2), Traefik labels on the web service, `traefik-public` external + `-internal` network if it has a DB. +2. `templates//.felhom.yml` — copy `templates/paperless-ngx/.felhom.yml`; required keys per §2; DOMAIN + SUBDOMAIN fields always; `mem_limit` = sum of compose limits; Hungarian user-facing text; `healthcheck.checks` probe. +3. Update `README.md` App Catalog + Variable-types tables (convention — every existing app is listed). +4. Skip `templates.json` / `generate-customer.sh` (legacy, §3). +5. Email-capable app: add `smtp_mapping` + matching `${VAR:-}` compose lines (§2 last row). + +## 6. Known inconsistencies (observed — NOT fixed) + +- `README.md` lists `templates/filebrowser/` in the tree, but no such directory exists; conversely `templates/recipe-importer/` exists but is absent from README's tree and both catalog tables. +- `README.md` field-type table (§".felhom.yml Format") omits `subdomain` (used by all 53 apps) and `secret_input` (`templates/romm/.felhom.yml`), and lists `boolean` which no descriptor uses. +- `README.md` says `smtp_mapping` is "Currently mapped: Vaultwarden, Mealie" — six descriptors now carry it (calcom, gitea, mealie, nextcloud, rallly, vaultwarden). +- Healthcheck URL style drifts: `localhost` vs `127.0.0.1`, trailing-slash vs none, `curl -f` vs `curl -sf`, and two distinct Node one-liner styles (compact rallly vs verbose docmost). +- One unquoted generator value (`generate: password:24`) among otherwise-quoted `generate: "..."` values. +- The in-file comment block in `.felhom.yml` headers (e.g. `templates/paperless-ngx/.felhom.yml` "Generator types") omits `base64key:N`, which `README.md` documents and one app uses.