# Marketplace Architecture

How the letmesee style marketplace actually works — submission, review, the trust-contract enforcement that keeps it honest, versioning, hosting, moderation, discovery, and money. This is the engineering spec, not the marketing pitch (that's [marketplace-catalog.md](./marketplace-catalog.md)).

The marketplace is the product. A camera that transforms photos is a feature; a marketplace of photographic intents that anyone can author, fork, and sell is a platform. This document is about making the platform safe enough to be the default.

---

## 1. The core object

A marketplace entry is a **published `style.md`** plus generated metadata. Nothing else. The format is frozen by [style-format.md](./style-format.md); the marketplace adds an envelope:

```jsonc
{
  "slug": "@username/film-noir",        // namespaced; bundled styles use no namespace
  "version": "1.2.0",                    // semver, immutable once published
  "content_hash": "sha256:…",            // hash of the raw style.md bytes
  "author": { "id": "u_…", "handle": "username", "verified": false },
  "submitted_at": "2026-05-16T…",
  "review": {
    "state": "approved",                 // pending | auto_flagged | in_review | approved | rejected | pulled
    "trust_audit": { … },                // see §4
    "human_reviewer": "r_…",
    "decided_at": "2026-05-16T…"
  },
  "axes": { "aggressiveness": 4, "identity": "soft", … },   // parsed from frontmatter
  "preview_set_id": "pv_…",              // see §3
  "pricing": { "model": "free" },        // free | one_time | sub_only | bundled
  "stats": { "installs": 0, "active": 0, "p50_latency_ms": 0, "report_count": 0 }
}
```

**Immutability rule:** a `(slug, version)` pair is write-once. Fixing a style means publishing `1.2.1`. This is non-negotiable — a user's activated style is part of their photographic identity (see [style-format.md §Versioning](./style-format.md)). We never mutate a version out from under someone.

---

## 2. Catalog model

Three tiers, served from the same schema:

| Tier | Source | Trust | Example |
| --- | --- | --- | --- |
| **Bundled** | Shipped in the app binary | Authored by us, audited at build time | `magazine`, `dreamscape` |
| **Verified** | Marketplace, human-reviewed, author identity-verified | Full trust-contract audit + manual review | `@studio-kane/editorial-93` |
| **Community** | Marketplace, automated review only | Automated audit; lower discovery weight; "unverified" badge | `@someone/my-cool-look` |

The client ships the bundled tier offline. Verified + Community are fetched from the catalog API and cached. A style the user has *installed* is pinned and stored locally — the marketplace going down never breaks someone's camera.

### Catalog API (read path)

```
GET  /v1/catalog?tier=verified&axis.aggressiveness<=3&best_for=portrait&sort=installs
GET  /v1/style/@username/film-noir            → latest approved version envelope
GET  /v1/style/@username/film-noir/1.2.0      → exact version (immutable, CDN-cached forever)
GET  /v1/style/@username/film-noir/1.2.0/raw  → the literal style.md bytes
GET  /v1/preview/pv_…                          → the before/after preview set (see §3)
```

Versioned endpoints are immutable → `Cache-Control: public, max-age=31536000, immutable`, served from CDN edge. The unversioned `latest` pointer is short-TTL (60 s) and the only thing that ever changes.

---

## 3. Preview sets — the honesty layer

A style with no proof is noise. **Every published style must ship a preview set**: the same N source photos run through the style, before/after. The marketplace generates these itself — authors don't upload their own "after" images, because authors lie.

Pipeline on submission:

1. Author submits `style.md`.
2. Marketplace runs the style's prompt against a **fixed, secret evaluation corpus** — ~12 source photos spanning portrait / group / food / landscape / street / low-light / kids / product. Same corpus for every style, so cards are comparable.
3. Outputs stored as the preview set, hashed, pinned to `(slug, version)`.
4. Re-running is deterministic enough for display; we store the actual returned images, not a promise to regenerate.

The eval corpus is **not public** — if authors could see it, they'd overfit prompts to it. It rotates quarterly. Bundled styles use the same pipeline (see `scripts/generate-catalog-samples.py` for the local equivalent).

Cost: ~12 model calls per submission (~$0.05–0.25 depending on model). Charged against the author's account or absorbed for verified authors. Abuse-throttled (see §7).

---

## 4. Trust-contract enforcement

This is the part that makes the marketplace not-a-disaster. The [prompt taxonomy](./prompt-taxonomy.md) defines six axes; a style **declares** its axis scores in frontmatter. The marketplace's job is to verify the style **actually behaves** the way it claims, because the axes are the user's consent contract.

### Automated audit (every submission, blocking)

Run on the preview-set outputs vs. the source corpus:

| Check | Method | Fails if |
| --- | --- | --- |
| **Identity drift** | Face embedding distance (ArcFace) source vs. output, per detected face | Declares `identity: strict` but mean cosine distance > τ_strict; or `soft` but > τ_soft |
| **Person count** | On-device-equivalent detector on source vs. output | A person appears or vanishes and `subject` ≠ `restaged` |
| **Geometry** | Aspect ratio + homography estimate | Declares `geometry: locked` but aspect/crop changed > 2% |
| **Environment** | Background segmentation IoU source vs. output | Declares `environment: cleaned` but background IoU < 0.8 |
| **Aggressiveness floor/ceiling** | Composite of the above into a 1–5 estimate | Estimated aggressiveness deviates from declared by > 1 |
| **Hard safety** | Classifiers (see §5) | Any hard-safety hit → immediate `rejected`, no appeal queue |

A style that **under-claims** (declares aggressiveness 5 but behaves like a 2) is allowed — that's just a conservative author. A style that **over-claims** (declares `identity: strict` but swaps faces) is **rejected**. Asymmetry is the point: the contract protects the user, so erring toward "less than promised" is safe, "more than promised" is a violation.

### Human review (verified tier + anything auto-flagged)

A reviewer sees: the style.md, the preview set, the automated audit scorecard, and a diff against the closest existing style (fork detection). They check the things classifiers can't: is the *intent* honestly described? Does the tagline oversell? Is this a thin wrapper around a bundled style with the axes relabeled to dodge the audit?

Reviewer decisions are logged with the `style.md` content hash so a later "why was this approved" is auditable.

### Continuous re-audit

Models drift. A style approved on model X behaves differently when the user's BYOM is model Y. Mitigations:

- The audit runs per **declared `preferred_model`**. A style is certified *for the models it was audited on*. The card shows "audited on gemini-2.5-flash-image".
- If a user runs a style on an un-audited model, the UI shows a one-time "this style hasn't been verified on your model" notice. Their call — it's their key, their model.
- Spot re-audits run weekly on a random sample of installed styles. A style that starts failing (model update changed its behavior) gets flagged and the author notified before it's pulled.

---

## 5. Safety & policy

The hard line, enforced by classifiers at submission **and** a kill-switch at runtime.

**Categorically rejected, no appeal:**

- Any output that sexualizes minors, or any prompt that plausibly produces it. Zero tolerance, reported per law.
- Identity swap / deepfake of a *specific real person* (public figure likeness, "make anyone look like {celebrity}").
- De-aging/age-up of detected minors, or prompts targeting children's appearance beyond [restage.md](../styles/restage.md)-level pose cleanup.
- Removal of religious dress, or alteration of protected characteristics (ethnicity, perceived disability).
- Non-consensual intimate imagery vectors (clothing removal, body reshaping toward nudity).
- Watermark/provenance removal, including stripping our own `letmesee.style` EXIF tag.

**Allowed but constrained:**

- Aggressive restaging (clothes, pose, environment) is fine — that's the product thesis — *as long as identity is preserved and the axes declare it honestly*. `Wardrobe` and `Dreamscape` are the reference ceiling.
- `Dreamscape`-class environmental replacement must EXIF-tag output `letmesee.style=<slug>` regardless of user watermark settings. Non-negotiable, enforced engine-side, not style-side.

**Provenance:** every output carries C2PA-style content credentials when the platform key is used. BYOM outputs carry the EXIF style tag at minimum. We are betting on making AI photography acceptable *as the default* — that bet only holds if it's also always *disclosable*.

---

## 6. Discovery & ranking

The marketplace browse is the screen most users see. Ranking signals, in rough weight order:

1. **Trust tier** — verified outranks community at equal relevance. Unverified is never an Editor's Pick.
2. **Retained installs** — installs that are still active after 14 days, not raw installs. Kills install-farming.
3. **Axis-honesty margin** — how far *inside* its declared contract the style behaves. Honest-conservative styles get a quiet boost; this is a deliberate thumb on the scale toward trust.
4. **Preview-set quality** — human-rated once at review; not user-gameable.
5. **Recency / freshness** decay so the catalog doesn't ossify.
6. **Personalization** — "you use a lot of aggressiveness-2 portrait styles" → surface similar. On-device; the ranking features never leave the phone unprofiled.

Filters are the taxonomy, 1:1: aggressiveness range, identity preservation, geometry, best-for. The single most important filter — **"identity: strict only"** — is one tap, because that's the cohort that needs the trust contract most.

Editor's Picks are human-curated, rotate weekly, and disclose *why* ("picked for honest skin retouching").

---

## 7. Abuse & rate limiting

- **Submission throttle:** N submissions / author / day; preview-set generation cost is the natural economic limiter. Verified authors get higher limits.
- **Sybil resistance:** community publishing requires a verified email + a small one-time friction (not payment — friction). Verified tier requires identity verification.
- **Fork-spam:** near-duplicate detection (style.md content-hash + prompt embedding similarity). A fork is fine; 400 forks with one word changed is not — collapsed under the original.
- **Report pipeline:** any user can report an installed style. Reports route by category; hard-safety reports page a human immediately and auto-suspend the style pending review. Non-safety reports batch.
- **Runtime kill-switch:** a pulled style's slug is pushed to a denylist the client checks on style activation. Already-installed copies of a *safety-pulled* style are disabled on next launch (the only case where we touch a user's pinned style — and we tell them why).

---

## 8. Money

The marketplace has to pay creators or it won't have creators. Models a style can declare:

| Model | Mechanics | Cut |
| --- | --- | --- |
| `free` | Default. No payment. | — |
| `one_time` | Pay once, yours forever (pinned, works offline, even if later pulled for non-safety reasons) | 70 / 30 to creator |
| `sub_only` | Included with **letmesee Studio** (the hosted-model subscription) | Revenue pool split by retained active-install share |
| `bundled` | We licensed it for the default set | Flat licensing deal |

**letmesee Studio** is the hosted option (referenced in [the Settings mockup](../mockups/05-settings.html)): we pick/operate the model, absorb failures, and the user doesn't bring a key. Studio subscribers get the `sub_only` catalog. BYOM users can still buy `one_time` styles and run them on their own key — the purchase is the *style*, not the inference.

Creator payouts are gated on the trust audit staying green. A style that gets pulled for over-claiming stops earning at the pull; safety-pulled styles claw back nothing already paid out to users but are removed from sale.

---

## 9. Client sync

```
On launch:        GET /v1/catalog/delta?since=<cursor>   (cheap, mostly 304)
On install:       GET /v1/style/<slug>/<ver>/raw         → store in local pinned dir
On activate:      check denylist; if pulled-for-safety → disable + explain
Background weekly: re-audit sample, refresh "latest" pointers, surface updates
                   (notify, never auto-upgrade — see Versioning)
```

The pinned-local store means: airplane mode, marketplace outage, or letmesee-the-company disappearing entirely does not break a camera someone relies on. That property is load-bearing for trust and is a hard design constraint, not a nice-to-have.

---

## 10. Authoring path

Two front doors into "make your own", both producing the same validated `style.md`:

1. **In-app composer** (mockup: `mockups/08-compose.html`) — guided. Set the six axes with controls, describe intent in plain English, the app assembles a spec-valid style.md and prompt. For people who will never open a text editor.
2. **Web style editor** (`mockups/style-editor.html`) — real tool. Edit the style.md directly, run it against a sample photo through your own model key (BYOM, same as the app), see the actual before/after, iterate, export. For authors who want to tune the prompt and *see it work* before they ship.

Both paths feed §1 submission. The web editor is also how *we* author bundled styles — it's the same pipeline, no privileged path.

---

## 11. Open questions

- **Eval corpus governance.** Who curates it, how is consent obtained for the source faces, how often does it rotate without breaking comparability across submission cohorts.
- **Model-specific certification UX.** "Audited on Gemini, you're running OpenAI" — how loud should that warning be without being annoying enough that people stop reading it.
- **Creator identity vs. pseudonymity.** Verified tier needs real identity; a lot of good style authors will want to stay pseudonymous. Where's the line.
- **Cross-model determinism.** Preview sets are model-specific. Do we show the user previews from *their* model (expensive, N× storage) or from the audited model with a disclaimer (cheaper, slightly dishonest).
- **The de-platform problem.** If letmesee-the-company is acquired/shut down, the pinned-local store protects installed styles but the marketplace dies. Is there an export/federation story (signed style bundles, a portable catalog format) so the format outlives the company. The format is just Markdown — that was always partly the point.
