☰ MenuStatus

Redpoint Engine — status

Snapshot as of 2026-07-17. Headless multi-tenant middleware for human-edited social-video jobs. Tenant #1: Wakecut (yachting); tenant #2: expocut (pure-API seat, live). See CLAUDE.md, docs/ARCHITECTURE.md, docs/BUILD-PLAN.md.

Editor-program wave (2026-07-17) — closeout batch #65–#79, live DB at 0039

All of #65–#79 merged to main (a1cbd64); migrations 0034–0039 pasted to the live DB and probe-verified. This wave completed Phase 10, delivered the Phase 11 commission dry-run + arming-prep (arming still awaits an operator money ruling), and shipped a self-contained editor-program (client voice notes, editor ratings, the specify-your-editor paid upsell, and a config-gated supervision engine).

Merged this batch (PR → what shipped):

Live vs dormant vs in-PR:

Live DB level with main at 0039 — migrations 0034–0039 pasted and probe-verified this wave. Next free number is 0040.

Phases 1–9 COMPLETE (2026-07-16) — closeout batch #56–#62

All nine build phases are done and live. Batch #56–#62 merged to main (414439c), migrations through 0033 applied to the live DB, and the Phase 9 ffmpeg transcode worker is deployed on Fly for the first time.

Merged this batch:

Live DB level with main at 0033 — probe-verified 2026-07-16: migrations 0027–0033 all applied (objects answer 200); the expocut (tenant #2, pure-API) row is active; anon is 401 on editor_applications and erasure_requests (0005 default-deny holds for the new tables).

Fly — transcode node LIVE (first deploy): redpoint-transcode (ams, performance-1x / 2GB, 60GB encrypted scratch volume, 1 machine, checks 1/1, /healthz 200). Secrets set incl. APP_ORIGIN=https://wakecut.com and a rotated CRON_SECRET (one shared value across Vercel + .env.local + the transcode worker). redpoint-ingest redeployed with the #57 SSRF guard (1 machine, checks 1/1). Config ingest-service/fly.transcode.toml; the app is a sibling of redpoint-ingest — keep both at 1 machine (fly deploy defaults to 2).

Phase 10 — COMPLETE (2026-07-17)

Roadmap: docs/ROADMAP-PHASE-10-PLUS.md (seat-business ordered). All slices merged: 10a-1 (#60), 10b (#61), and 10a-2 (#66) — the review/delivery handle now rides the webhook payloads (data.review / data.delivery, through the same API_JOB_FIELDS allowlist; documented in docs/PARTNER-API.md §04) — plus the client voice-brief (files.kind='voice_note', migration 0034, #65). Phase 11 landed its dry-run + arming-prep (Option A commission, #68/#71, migrations 0035–0036); arming awaits the operator money ruling (below).

Operator-owed (current, 2026-07-17):

Mobile apps (2026-07-11) — installable client + editor iOS apps, LIVE

Both customer-visible workflows ship as installable home-screen web apps (PWA packaging — deliberately no App Store/Capacitor: no Xcode on the build machine, and Apple guideline 3.1.1 makes an in-app Stripe checkout a rejection risk). Merged via the full two-critic pipeline, one PR per invariant:

Operator-owed to finish: (1) {{ .Token }} email template DONE 2026-07-14 and verified live end-to-end: custom SMTP via Resend (smtp.resend.com:465, sender hello@wakecut.com, domain DKIM/SPF verified, Cloudflare auto-configured) + code block in both templates (Magic link or OTP, Confirm signup) — operator signed in with the emailed 8-digit code. This also closes Phase 7's Resend prerequisite (account + verified domain + working SMTP). Note: two Resend API keys transited chat/screenshots during setup — rotate once more and swap into the Supabase SMTP field. Still owed: (2) install both apps from an iPhone (Share → Add to Home Screen on / and on /workspace) and visually QA standalone mode; (3) optional per-tenant app icons via tenants.theme URL keys. Ops note: vercel-secrets.sh now actively unsets the retired NEXT_PUBLIC_BASE_PATH (69b379f) — a re-run would previously have 404'd the live site.

Incident log

Phase status

Phase Scope State
1 Foundation: tenant-aware schema, Supabase SSR auth, tenant resolution, RLS ✅ verified (13/13 RLS + tenant checks)
2 Upload pipeline: Uppy + presigned multipart → R2, resumable, signed download ✅ verified (R2 round-trip 6/6, download gate 4/4, CORS confirmed). Browser multi-GB pause/resume run is the only manual item left
2b Remote ingest (pull from Dropbox/Drive/transfer links → R2) 🚧 in progress — see below
3 Catalogue → Stripe Checkout (manual capture) → verified webhook → NEW job; billing_config dispatch; rush; VAT ✅ verified (13/13 e2e + billing units 8/8)
4 Ops console + transitionJob() ✅ built + reviewed; gate loop automated as 6 UI e2e tests (ops-loop.spec.ts) — operator sign-off run still owed
5 Editor workspace ✅ basic workspace built (/workspace, dark surface) + job board (/board, PR #21): swipe/claim pool jobs, hybrid dispatch (packages.dispatch_mode + jobs.dispatch_override), auto deadline from package turnaround. Migration 0006_job_board.sql applied (live DB is one project serving dev AND prod, now at 0033)
6 Client review, approval, capture, Connect payout, delivery ✅ COMPLETE — built + merged 2026-07-09 late (#23 home / #24 money core / #26 Connect+payout surfaces; #25 review page merged after its CI rate-limit rerun). Migration 0008 APPLIED live. Each PR passed two-critic adversarial review with falsification runs; review caught+fixed: free-finals leak, APPROVED-uncaptured divergence (revert-on-capture-failure), double-pay on retry (classified idempotency), untested CAS lock (+10-min expiry), onboarding double-account race. Operator gate still owed: full NEW→DELIVERED happy path in Stripe test mode incl. a real Connect transfer (editor must onboard via /workspace first). Named residuals: re-authorization flow for expired auths (manual capture ~7-day window — matters if turnaround+review exceeds it), void_failed/capture_failed alerting (Phase 7), e2e session-mint caching (auth 429 pressure), paid-revision billing mechanism
7 Resend notifications + SLA machinery ✅ COMPLETE — notifications (0020/0021) + Web Push third channel (#51) + SLA machinery
8 Headless tenant API + signed webhooks + theming + 2nd tenant ✅ COMPLETE — tenant API keys (0024), /api/v1 orders (0025), signed webhooks (0026), 2nd tenant expocut (0027, active). Formal gate closes when the operator mints the expocut rpk_ key
9 ffmpeg previews, retention cron, right-to-erasure, quality counters ✅ COMPLETE — transcode worker (0029) deployed on Fly (redpoint-transcode), retention cron (0030), right-to-erasure (0031, #56), quality counters (#52)

Phase 2b (remote ingest) — remaining work

Done: migrations 0003_ingest.sql (ingests table, files.ingest_id) and 0004_ingest_source_url.sql (share-token hardening: source_url column-revoked from clients+editors, redacted source_display, admin-only accessor fn); src/lib/ingest.ts (provider detect, classify, redact, SSRF pre-check); ingest API (/api/ingests create+list, /api/ingests/[id]/cancel); Companion sources wired into the uploader (gated by a Companion URL); IngestPanel UI (paste link

Reviewed (branch phase-2b-ingest): two-critic adversarial review, findings verified against HEAD and landed. cd ingest-service && npm test runs 67 gate checks (SSRF probe classes incl. mapped-IPv6 + dns-to-private + redirect-hop re-validation; content-type matrix; size cap; resolver link parsing). Fixes landed: mapped-IPv6 hex-form bypass, SSRF never-retry classification, capped metadata JSON reads, explicit multipart abort, clean-slate multi-file retry, prod fail-fast on missing COMPANION_UPLOAD_URLS, pruning session store.

Known residuals (recorded, not blocking): no automated tests for the Supabase-coupled paths (worker state transitions, cancel race, RLS/redaction, IngestPanel React behaviour) — covered by the post-deploy acceptance gate rather than a local test framework; Tier-2 resolver fixtures are shape-based (unofficial endpoints verified only at the parsing layer until e2e).

Deployed 2026-07-03: Fly app redpoint-ingest (ams, 1 machine — the worker is single-queue by design; fly deploy defaults to 2, scale back down), secrets via ingest-service/scripts/fly-secrets.sh, NEXT_PUBLIC_COMPANION_URL + NEXT_PUBLIC_INGEST_PROVIDERS set in the web app. Note: the app sits in the personal Fly org, not rpv — move/billing TBD.

§6 gate — automatable items PASSED on the deployed node (2026-07-03): SSRF probe classes (API precheck + worker DNS-stage, "never fetched" confirmed in logs, no retry); garbage WeTransfer link → queued_manual; redaction (client denied source_url 42501, sees source_display; admin gets the full URL via ingest_source_url(); non-participant sees zero rows); generic URL ingest e2e (991017-byte mp4 → done, files row with ingest_id, canonical {tenant}/{job}/original/… key = retention parity, signed download served real bytes from EU R2).

The gate also caught a live defect, fixed same day (suite 67 → 71 checks): a dead URL held the single-worker queue 15 min (undici's 300s defaults × 3 attempts). The dispatcher now bounds header wait (30s) and body idle (60s — deliberately not a total cap), env-overridable via INGEST_HEADERS_TIMEOUT_MS / INGEST_BODY_IDLE_TIMEOUT_MS; safeFetch uses undici's own fetch so the SSRF dispatcher can't be silently dropped by Node-version skew (test/stall-gate.mjs, falsification-proven). Deployed to the node 2026-07-04; a live probe against a fast-failing origin confirmed the deployed retry/disposal path (dead URL disposed in 20s).

Tier-2 live e2e PASSED (2026-07-04): real SwissTransfer link (3,145,760 bytes) and real WeTransfer we.tl short link (15,375,308 bytes) both resolved on the deployed node → done, files rows with ingest_id, canonical job-prefix keys. Dropbox OAuth app created (redpoint-ingest, key c0w73ovtywde0wo, read scopes + redirect URI configured; secret push pending — operator).

Remaining (operator) to close the gate:

  1. Push Dropbox app secret: fly secrets set COMPANION_DROPBOX_KEY=… SECRET=… (operator's Dropbox currently over quota — picker test deferred), then Dropbox picker multi-GB ingest.
  2. Google OAuth client (operator-created) + NEXT_PUBLIC_GOOGLE_PICKER_CLIENT_ID; Google Photos picker ingest. Tier-2 resolvers remain unofficial-API best-effort — flag providers off if their endpoints drift.

Testing (2026-07-07)

Two layers, all merged to main via the Opus build→adversarial-review pipeline:

Design (2026-07-08) — IMPLEMENTED

Spec pack: docs/DESIGN-BRIEF.md (system) + docs/design/PAGE-SPECS.md (per-page) + docs/design/mockups/ (Claude Design HTML). Now BUILT into the app via agent packets:

Production (unlisted) — wakecut.com live, verified 2026-07-12

The engine serves at https://wakecut.com (still unlisted: robots disallow-all, nothing links to it). Chain: GoDaddy NS → Cloudflare zone wakecut.comA @ → 76.76.21.21, CNAME www → cname.vercel-dns.com (both DNS-only, NOT proxied) → Vercel project rpv/redpoint-engine (auto-deploys main, no basePath). www.wakecut.com and the legacy wakecut.redpoint-vision.com are 308 redirects to the apex (both live-verified 2026-07-12). The main redpoint-vision.com PHP site stays untouched. Note the Cloudflare account split: the wakecut.com zone is in a separate account from the redpoint-vision account holding R2/Workers (see Operations-Runbook).

Backlog (cross-phase, recorded 2026-07-12)

Infra / operations

Local dev

npm install
npm run dev          # needs .env.local (Supabase + R2 + Stripe test keys)
npm run typecheck
npm run build

Test users (dev), shared password in .env.local context: rp.client@…, rp.client2@… (non-participant), rp.editor@… (assigned). The admin account is the operator's real address: man_on_rock@redpoint-vision.com (renamed from rp.admin@… 2026-07-06; same password).