External edge smoke
Prove the public edge is narrow before inviting testers.
This harness runs from outside the VPS against the final HTTPS origin. It checks that only shallow health/docs are public, private app surfaces stay gated, headers survive the proxy, and request body limits fail closed.
Command
Run from an external machine after HTTPS proxy attachment
scripts/external-edge-smoke.sh https://members.final-domain.example
# Or through make:
NOCTURNE_PUBLIC_ORIGIN=https://members.final-domain.example make external-edge-smoke
The script is intentionally dependency-light: bash, curl, and python3. It refuses local, placeholder, .test, .invalid, and non-HTTPS origins by default.
TLS rule: valid certificate verification is part of the smoke.
NOCTURNE_EDGE_SMOKE_INSECURE=1 exists only for temporary private TLS debugging and should never be treated as launch-ready evidence.Coverage
What the smoke proves
| Check | Expected result | Why it matters |
|---|---|---|
/healthz | 200 with plain ok. | Confirms the public proxy can reach the app while keeping health shallow. |
| Security headers | HSTS, CSP, nosniff, referrer policy, and permissions policy are present. | Confirms app/proxy header posture survived TLS termination. |
/docs/ | 200 with Nocturne docs marker. | Confirms the public documentation surface works. |
| CLI-only diagnostics | /doctor, /production-preflight, /backup-status, /local-smoke, and /metrics are 404 or 405. | Confirms operational detail stays local/operator-only. |
| Member gates | Unauthenticated /library and media file routes return 401 or 403. | Confirms private media is not exposed by the proxy. |
| Private media root | Direct /private_media/... path is not 200. | Confirms the proxy does not serve the filesystem media root. |
/report body cap | Oversized 9 KB report post returns 413. | Confirms the public intake cap is active at proxy or app. |
| Default form cap | Oversized 17 KB join post returns 413. | Confirms the global small-form posture is active at proxy or app. |
Sequence
Recommended VPS validation order
- Replace all placeholders in
.env.productionand the selected proxy config. - Run
vps-bootstrap-plan --env-file .env.production, thenproduction-preflightlocally on the VPS with production env loaded. - Validate the live proxy syntax on the VPS:
caddy validateornginx -t. - Attach/reload the proxy and confirm public DNS/TLS resolves.
- Run
scripts/external-edge-smoke.shfrom outside the VPS network and record adeployment-evidence-record --stage external_edge_smokeentry with an artifact hash. - Run
smoke-member-provision --recovery-key-file ...on the VPS to create a disposable recovery-confirmed member for private external testing. - Run external member smoke from outside the VPS network with that member ID and recovery-key file.
- Run
smoke-member-cleanup --confirm-smoke-member-cleanupafter the authenticated smoke window closes. - Run
production-preflightlocally again after the public and member smokes pass, record sealed backup dump/media hashes withbackup-manifest-record, record encrypted/offsite custody withbackup-custody-record, rundeployment-evidence-checkfor the final origin, then runprivate-vps-readinessbefore widening private testing. - Record the date, origin, proxy type/version, smoke output, and any warnings in the deployment notes.
Failure interpretation
What to fix first
- Health fails: check DNS, TLS, proxy upstream target, Docker network, app health, and firewall rules.
- Header fails: verify
NOCTURNE_PUBLIC_ORIGINis the final HTTPS origin and that proxy header rules were not stripped. - Diagnostics return
200: remove any proxy rule that maps CLI-only commands, local logs, metrics, backup paths, or shell endpoints into public HTTP. - Media gate fails: ensure the proxy routes only to the app and never serves
private_mediaas a static public directory. - Body cap fails: restore the Caddy
request_bodycaps or nginxclient_max_body_sizecaps before external testing continues.
Limits
What this smoke does not prove
- It does not prove payment processor approval, live webhook signatures, tax handling, or subscription sync.
- It does not prove WAF/rate-limit tuning, DDoS resilience, or abusive traffic handling.
- It does not prove backup encryption, offsite retention, or restore drills.
- It does not replace authenticated member smoke or manual browser playback checks with real private video fixtures.
- It does not prove legal/privacy notice readiness or trust/safety escalation procedures.