Tech debt
This is the structural and correctness debt worth tracking, ordered by priority. None of it is a reason to stop shipping — but each item is a thing a future engineer (or the founder in six months) will need to know.
Priority findings
| # | Finding | Severity | Where |
|---|---|---|---|
| 1 | CORS allows "null" origin with credentials | Review before beta | server.py CORS middleware |
| 2 | Daily try-on limit is 25 in code, docs say 10 | Reconcile | DAILY_TRYON_LIMIT |
| 3 | FLUX.2 routing is effectively dormant | Confirm intent | determine_tryon_model |
| 4 | Body-photo dual-write pattern | Consolidate | analyze + upload paths |
| 5 | Silent pipeline failures return 200 | Wire Sentry | try-on pipeline |
| 6 | Session replay on in production builds | Fix before store | eas.json env |
| 7 | Two .ipa build artifacts committed to git | Untrack | repo root |
| 8 | Marketing bg videos: 84 MB hero, git bloat, third-party IP | Compress / re-host | Drape-Marketing/public/video |
1. CORS allows the null origin
The codebase's own P0 security rules state "do not add null to CORS allowed
origins — it permits file:// requests." The live CORS allow_origins list
includes "null" alongside allow_credentials=True, with an explanatory
comment. This is a direct deviation from the stated invariant and should be
confirmed with the founder: either the rule is wrong, or the code is. With
allow_credentials=True, a null origin broadens the surface for
credentialed cross-origin requests.
2. Daily try-on limit: 25 vs 10
The tech-stack notes describe "10 try-ons/day per user." The code constant
DAILY_TRYON_LIMIT is 25. Neither is wrong to have chosen — but the docs and
code should agree. The code is the source of truth; the docs on this site reflect
25.
3. FLUX.2 is present but dormant
The documented routing says "baggy / wide-leg → FLUX.2 LoRA," but
determine_tryon_model sends baggy garments to Fashn tryon-max by default.
A flux2-lora branch exists, but no default rule selects it — a product must be
explicitly tagged to reach FLUX.2. Decide whether FLUX.2 should be wired back into
default routing or removed from the mental model (and eventually the code path).
See the try-on pipeline.
4. Body-photo dual-write
analyze-body-photos and body-photo/upload both write overlapping body-photo
fields on the user document (a dual-write with a TODO in the code). This is the
kind of overlap that causes "the photo saved but try-on can't find it" bugs.
Consolidating to one writer is deferred but worth doing before scaling past the
current user count.
5. Silent pipeline failures
Several steps can return 200 OK while producing a wrong or missing result:
rembg returning None, body_photo_data never written, Fashn returning a
marketing image, or the analyze pipeline throwing after measurements are written.
is_valid_render catches malformed URLs, but semantic failures pass through. These
need Sentry breadcrumbs so they stop being invisible. Detail on the
try-on pipeline page.
6. Session replay in production
PostHog session replay is gated on EXPO_PUBLIC_APP_ENV === 'beta', and
eas.json sets that env to beta in both the preview and production build
profiles. For a biometric app, replay should be disclosed and ideally off for
production end users — body photos rely on component-level masking only. This is a
15-minute fix (flip the production profile env) and also appears on the
shipping checklist.
7. Repo hygiene
Two compiled .ipa build artifacts (~91 MB total) are committed to git;
.easignore excludes *.png but not *.ipa. Untrack them and add *.ipa to
.gitignore.
8. Marketing video assets & delivery
The marketing site's background loops are plain .mp4 files committed in the
repo under Drape-Marketing/public/video/, served as static assets by AWS
Amplify → CloudFront. The MediaLoop component plays them muted, looping, and
object-cover (poster first, then the video, reduced-motion respected).
Addressed (2026-08-23): the culture.mp4 "BUILT FOR THE CULTURE" loop was a
Vans "Off The Wall" ad (third-party trademark on a commercial site). It was
replaced with PRK Project partner content and converted from HEVC .mov to
web-safe H.264 (Chrome/Firefox can't play HEVC).
Still open:
hero.mp4is ~84 MB (2min 18s). Absurd for a muted background loop — every visitor pulls a huge file. It should be a short (~10–20s), well-compressed loop of a few MB.- Large binaries live in git. The 84 MB + 14 MB videos bloat every clone forever. Move big media out of git (Git LFS, or host on a CDN bucket).
- Third-party IP in the hero.
hero.mp4carries Red Bull branding and a "viemr" stock watermark — stock/competitor footage on a commercial marketing site is a legal risk. Replace with owned or partner-licensed footage.
The delivery layer (CloudFront) is already a CDN — that is not the problem; compression and git bloat are. So a dedicated video host is not required yet:
- Now: compress the loops and keep them on CloudFront; get big media out of git. This fixes most of the issue with no new vendor.
- If a real video pipeline is wanted (adaptive HLS so phones get a lighter
stream, thumbnails, analytics): consolidate on Mux — it is already in the
stack for live streaming (
backend-postgres/helpers/mux_live.py) and does VOD adaptive streaming too. Bunny.net (Bunny Stream) is a viable, cheaper alternative; the main reason to pick it over Mux would be pricing. Adding Bunny means a second video vendor alongside Mux.
server.pyis ~8,900 lines. This is a deliberate solo-founder tradeoff (everything is greppable). It is not a bug, but it raises the cost of onboarding anyone new and makes merge conflicts likely if a second engineer joins. If the team grows, split by router first, not by rewrite.- Stateless auth lookup.
get_current_userhits MongoDB on every request. The upside is immediate revocation; the downside is a DB read per call. Fine at current scale; revisit with caching only if it shows up in profiling. - Dormant white-label layer.
StoreContext,stores.tsx, andstore-adminimplement multi-tenant theming with a purple default theme distinct from the cream design system. It is largely inactive relative to the main consumer flow — document it as "infrastructure, not a live feature" so no one assumes it is wired up. - Static message screens.
messages.tsxandmessage-thread.tsxrender mock data with no backend — a placeholder, not a feature. - Stale audit docs. Two 2026-05-25 dependency audits under
frontend/docs/are stale and inverted on python-jose; correct or archive them.
The root CLAUDE.md tracks a detailed bug list and P0 rules, and most items there
are genuinely fixed in code (the IDOR check, ref-stored AbortControllers,
token-validated-on-load, and the error boundary all verified present). This page
adds the few items that are not yet reflected in that list — chiefly the CORS
finding and the doc/code drifts above.