Everything here is blocked only by repo access. jbnx/nodedough is private and Cowork containers have no GitHub credentials (gh absent, clone refused, no GitHub connector, no device bridge). So the work is done, not applied.
Stack detected from the live site: Flask / Werkzeug — allow: OPTIONS, GET, HEAD, 405 on POST /healthz, /static/ served under Flask's default convention.
| # | Do | Fixes | Effort |
|---|---|---|---|
| 1 | Copy static/robots.txt, static/sitemap.xml, static/telemetry.js, static/vendor/supabase-2.108.0.js into the repo's static/ | #5, #6, #10 | copy |
| 2 | Apply patches/01_compression_caching_routes.py into the app factory; add flask-compress>=1.14 to requirements.txt | #4, #5, #10 | ~15 lines |
| 3 | Swap the jsDelivr <script> for /static/vendor/supabase-2.108.0.js, tighten CSP to script-src 'self' | #5 | 2 lines |
| 4 | Add <script src="/static/telemetry.js?v={{ asset_version }}"> and the 7 ndTrack() calls listed at the bottom of that file | #6 | 7 lines |
| 5 | Apply patches/02_stripe_webhook.py after the Stripe dashboard prerequisites at the top of that file | #1 | ~80 lines |
Live on production, migrations 0077–0080:
0077 — ops_is_admin() guard on admin_audit and admin_comp_paid_user0078 — scanner denylist on ops_log_404; purged 1,621 bot rows (1,870 → 249)0079 — log_event / log_app_error RPCs, funnel event allowlist, node_shares_granted_by_idx0080 — dropped superseded add_recurring(8-arg) and update_account(5-arg) overloadsThese four are not in the repo. Export them into the migration lineage when the handoff PR lands, or the drift gets worse.
The telemetry server side is already live — static/telemetry.js only calls it.
node_projection is not broken. Its p_months argument has a DEFAULT, so theclient's 1-arg call resolves fine. All 49 RPCs the client calls exist. The recorded "six app features calling non-existent routines" fact was wrong and has been retired.
ops_log_404 was not unrate-limited — it already had a 30/min per-IP cap. The botswere distributed across enough IPs to walk through it, which is why the path denylist was the actual fix.
curl -sI -H 'Accept-Encoding: br,gzip' https://nodedough.com/static/app.js \
| grep -iE 'content-encoding|cache-control'
# expect: content-encoding: br|gzip AND cache-control: public, max-age=31536000, immutable
curl -s -o /dev/null -w '%{http_code}\n' https://nodedough.com/robots.txt # 200
curl -s -o /dev/null -w '%{http_code}\n' https://nodedough.com/sitemap.xml # 200
curl -s https://nodedough.com/ | grep -c jsdelivr # 0
curl -s https://nodedough.com/healthz # billing_configured
Then confirm telemetry is flowing:
select event_name, count(*) from analytics_events
where occurred_at > now() - interval '1 hour' group by 1;
STRIPE_WEBHOOK_SECRET, plus a Supabase secret key on the web service. Details at the top of patches/02_stripe_webhook.py.
web source from main to production (dashboard only; the API refuses source changes). Until then every merge to main is a customer deploy.