Adds Monitoring Setup docs and deploy-scp troubleshooting/debug fixes
Continuous Integration / config (pull_request) Successful in 9s
Continuous Integration / backend-build (pull_request) Successful in 4m27s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m10s
Continuous Integration / backend-test (pull_request) Canceled after 0s
Continuous Integration / frontend-build (pull_request) Canceled after 0s
Continuous Integration / frontend-test (pull_request) Canceled after 0s
Continuous Integration / frontend-lint (pull_request) Canceled after 0s
Continuous Integration / publish-test (pull_request) Canceled after 0s
Continuous Integration / publish-production (pull_request) Canceled after 0s
Continuous Integration / deploy-test (pull_request) Canceled after 0s
Continuous Integration / deploy-production (pull_request) Canceled after 0s
Continuous Integration / frontend-prepare (pull_request) Canceled after 50s
Continuous Integration / config (pull_request) Successful in 9s
Continuous Integration / backend-build (pull_request) Successful in 4m27s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m10s
Continuous Integration / backend-test (pull_request) Canceled after 0s
Continuous Integration / frontend-build (pull_request) Canceled after 0s
Continuous Integration / frontend-test (pull_request) Canceled after 0s
Continuous Integration / frontend-lint (pull_request) Canceled after 0s
Continuous Integration / publish-test (pull_request) Canceled after 0s
Continuous Integration / publish-production (pull_request) Canceled after 0s
Continuous Integration / deploy-test (pull_request) Canceled after 0s
Continuous Integration / deploy-production (pull_request) Canceled after 0s
Continuous Integration / frontend-prepare (pull_request) Canceled after 50s
Monitoring Setup: operations/plans/monitoring-setup-plan.md and
operations/monitoring/monitoring-instructions.md, covering Sentry alert
rules on the security_event tag, UptimeRobot's 6 liveness monitors, and
the two new Umami website entries for the admin SPA.
deployment-instructions.md gains a missing Observability__Environment
host var (without it, both environments would tag Sentry events as
"Production"), the nginx client_max_body_size fix for the 413 seen on
publish-test/production artifact uploads, and two troubleshooting notes
on Gitea Actions re-run behaviour: re-running deploy-test/production
alone loses the run's uploaded artifact, and re-running all jobs on an
existing (rather than a brand new) run can replay stale secrets.
deploy-scp.yaml: step names no longer show literal unresolved
${{ inputs.* }} text (Gitea doesn't interpolate that context in step
names), and a temporary debug step logs PI_MAIN_USERNAME/PASSWORD
length plus a username equality check to diagnose a persistent
Permission denied during the SSH steps, without ever logging the
secret values themselves.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FffvxxJp5wG34Ru48GBig
This commit is contained in:
+134
@@ -0,0 +1,134 @@
|
||||
# Monitoring Instructions
|
||||
|
||||
All three services below were built into the application during Construction (U4) — this document
|
||||
only wires them to the real accounts and writes down the configuration that necessarily lives
|
||||
outside this repository (Sentry alert rules, UptimeRobot monitors, Umami website entries).
|
||||
|
||||
**Who does what, where** — reuses the labels from `deployment-instructions.md` § "Who runs what,
|
||||
where", plus the external services themselves:
|
||||
- 👤 **pi-main / `gitea-workflow`** — the environment's `shared/env` file (§ 1.5 of
|
||||
`deployment-instructions.md`)
|
||||
- ☁️ **Gitea web UI** — repository Variables (Settings → Actions → Variables)
|
||||
- ☁️ **Sentry web UI** — project settings and alert rules
|
||||
- ☁️ **UptimeRobot web UI** — monitors and alert contacts
|
||||
- ☁️ **Umami web UI** — website entries
|
||||
|
||||
## 1. Sentry
|
||||
|
||||
One project for the whole CMS (D-19); `Observability__Environment` (host-side, § 1.5) and
|
||||
`VITE_APP_ENV` (build-time) are what tell test and production events apart in that one project —
|
||||
there is no separate DSN per environment.
|
||||
|
||||
### 1.1 Gitea variable
|
||||
|
||||
☁️ **Gitea web UI** — Settings → Actions → Variables:
|
||||
|
||||
| Variable | Value |
|
||||
|---|---|
|
||||
| `VITE_SENTRY_DSN` | the project's DSN (same value used by both `publish-test` and `publish-production`) |
|
||||
|
||||
### 1.2 Host configuration
|
||||
|
||||
Already documented in `deployment-instructions.md` § 1.5 — `Observability__SentryDsn` and
|
||||
`Observability__Environment` in each environment's `shared/env` file. `Observability__Environment`
|
||||
**must** differ between the two files (`test` / `production`); see that section's note on why
|
||||
`ASPNETCORE_ENVIRONMENT` can't be reused for this.
|
||||
|
||||
### 1.3 Alert rules (FR-19)
|
||||
|
||||
The six `security_event` tag values already emitted by `SecurityEvents.cs` — alert rules must filter
|
||||
on the **tag**, not the message text (`nfr-design-patterns.md` Pattern 6):
|
||||
|
||||
| `security_event` tag value | Rule type | Threshold |
|
||||
|---|---|---|
|
||||
| `failed_login` | Count-based | > 20 events in 5 minutes |
|
||||
| `authorization_denied` | Count-based | > 20 events in 5 minutes |
|
||||
| `master_api_key_rejected` | Count-based | > 20 events in 5 minutes |
|
||||
| `admin_bypass_rejected` | Count-based | > 20 events in 5 minutes |
|
||||
| `rate_limit_triggered` | Count-based | > 20 events in 5 minutes |
|
||||
| `migration_failure` | Always-fire | any occurrence (already `Critical` level — the process is exiting) |
|
||||
|
||||
20-in-5-minutes is a starting default (no production traffic history exists yet to tune against) —
|
||||
revisit once real traffic volume is known.
|
||||
|
||||
☁️ **Sentry web UI** — Alerts → Create Alert Rule, once per row above:
|
||||
1. Condition: `tags.security_event equals <value>`
|
||||
2. For the five count-based rules: "when greater than 20 events, in 5 minutes" (or the platform's
|
||||
equivalent rate-window phrasing)
|
||||
3. For `migration_failure`: no count condition — fire on every matching event
|
||||
4. Action: notify via whichever channel/integration alerts should reach (same destination used
|
||||
elsewhere for this project is fine — nothing here requires a dedicated channel)
|
||||
|
||||
**Note (DEV-01, accepted deviation)**: Sentry's plan in use retains events for roughly 30 days,
|
||||
short of the 90-day SECURITY-14 minimum. Already accepted as a cost decision at Requirements
|
||||
Analysis — nothing to configure here, just to be aware of if investigating an old event.
|
||||
|
||||
## 2. UptimeRobot
|
||||
|
||||
Six monitors — `/health`, `/`, `/admin` × test/production (D-23). What each does and does not prove
|
||||
(FR-17):
|
||||
|
||||
| URL | Proves | Does NOT prove |
|
||||
|---|---|---|
|
||||
| `https://test.slpsoftware.nl/health` | The test process is alive (liveness only, D-21) | Database connectivity, migrations applied — no DB check by design (Q17=A) |
|
||||
| `https://test.slpsoftware.nl/` | Proxy Pi → pi-main routing and static hosting work for test | The API or admin SPA work at all |
|
||||
| `https://test.slpsoftware.nl/admin` | The admin SPA is served and its assets aren't blocked by CSP | Login/auth actually succeeds — this only checks the shell loads |
|
||||
| `https://slpsoftware.nl/health` | Same as above, production | Same as above, production |
|
||||
| `https://slpsoftware.nl/` | Same as above, production | Same as above, production |
|
||||
| `https://slpsoftware.nl/admin` | Same as above, production | Same as above, production |
|
||||
|
||||
☁️ **UptimeRobot web UI** — Add New Monitor, once per row above:
|
||||
- Monitor Type: **HTTP(s)** (not Keyword/Port/Ping) — checks only the response status code (2xx/3xx),
|
||||
the same liveness signal `deploy-scp.yaml`'s own `curl -f` health check already relies on
|
||||
- Interval: 5 minutes is enough for all six — `/health` recovers within a process restart
|
||||
(`Restart=on-failure`, `RestartSec=5` in the systemd units), no need for a tighter interval
|
||||
- Alert Contact: the existing contact already configured on this account
|
||||
|
||||
**Free-plan note**: 6 monitors at 5-minute intervals fits comfortably within the free plan's 50-monitor
|
||||
cap and 5-minute minimum interval — no paid tier needed for this feature's monitoring. One gap the
|
||||
free plan leaves open: no SSL-certificate-expiry monitoring. Certbot's systemd timer renews
|
||||
automatically (`deployment-instructions.md` § 1.7.1), so the risk is low, but a silently failed
|
||||
renewal would only be caught reactively — once an HTTP(s) check itself starts failing — rather than
|
||||
warned about in advance. Accepted as a known gap, not built around, consistent with this project's
|
||||
other documented deviations (e.g. DEV-01's log-retention gap).
|
||||
|
||||
## 3. Umami
|
||||
|
||||
Reuses the existing self-hosted instance at `analytics.slpsoftware.nl` (D-24/ASM-05). This section
|
||||
covers only the **admin SPA's** two website entries — the public website's Umami wiring (also
|
||||
required by D-25) belongs to whichever website workspace serves `wwwroot/web/` (FR-09/FR-16), not to
|
||||
this deploy pipeline.
|
||||
|
||||
☁️ **Umami web UI** — Websites → Add website, twice (once per environment), domain set to the
|
||||
admin SPA's real URL (`test.slpsoftware.nl/admin`, `slpsoftware.nl/admin`) — note the resulting
|
||||
website ID for each.
|
||||
|
||||
☁️ **Gitea web UI** — Settings → Actions → Variables:
|
||||
|
||||
| Variable | Value |
|
||||
|---|---|
|
||||
| `VITE_UMAMI_SCRIPT_URL` | `https://analytics.slpsoftware.nl/script.js` (shared — same instance, both environments) |
|
||||
| `VITE_UMAMI_WEBSITE_ID_TEST` | the test website's ID from the step above |
|
||||
| `VITE_UMAMI_WEBSITE_ID_PRODUCTION` | the production website's ID from the step above |
|
||||
|
||||
**These must stay in sync with two other Gitea variables that already exist from U5**
|
||||
(`SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION`) and with the matching
|
||||
`SecurityHeaders__AllowedScriptOrigins__0` / `_AllowedConnectOrigins__0` lines in each environment's
|
||||
host `shared/env` file (`deployment-instructions.md` § 1.5) — all four must name the same
|
||||
`https://analytics.slpsoftware.nl` origin, or either the CI gate (REF-U5-01) fails the build, or the
|
||||
script loads in CI but is silently blocked by CSP on the real host (the gate cannot see the host
|
||||
file, only the Gitea variable side of this).
|
||||
|
||||
## 4. Gitea Variables — Full Summary
|
||||
|
||||
For reference, every Actions variable this document and `deployment-instructions.md` § 1.9 together
|
||||
require:
|
||||
|
||||
| Variable | Scope | Set by |
|
||||
|---|---|---|
|
||||
| `VITE_SENTRY_DSN` | shared | § 1.1 above |
|
||||
| `VITE_UMAMI_SCRIPT_URL` | shared | § 3 above |
|
||||
| `VITE_UMAMI_WEBSITE_ID_TEST` | test | § 3 above |
|
||||
| `VITE_UMAMI_WEBSITE_ID_PRODUCTION` | production | § 3 above |
|
||||
| `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` | per environment | already set at Deployment Setup — must match § 3's Umami origin |
|
||||
| `DEPLOY_PATH_*`, `SERVICE_NAME_*`, `HEALTH_CHECK_URL_*` | per environment | `deployment-instructions.md` § 1.9 (unrelated to monitoring, listed there) |
|
||||
Reference in New Issue
Block a user