lilith-platform.live/tooling/scripts/watermark/PIPELINE.md
autocommit 9ba10c2f3b docs(watermark): 📝 Clarify watermarking workflows and best practices in LEDGER.md and PIPELINE.md
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-03 05:45:07 -07:00

7.6 KiB

Raw photo → live asset pipeline (quinn.www)

Grounded in:

  • codebase/@features/quinn-ai/engine/src/workers/photo-intake/index.ts (intake worker)
  • codebase/@features/quinn-ai/backend-api/src/modules/skills/photo-intake.ts (orchestration skill)
  • codebase/@features/image-protection/backend-api/src/pipeline.ts (protectPhoto layers)
  • codebase/@features/image-protection/backend-api/src/watermark.ts (visible + forensic marks)
┌────────────────────────────────────────────────────────────────────────────┐
│  ① RAW PHOTO INGEST                                                          │
│                                                                              │
│   Quinn's phone ──iMessage (to self)──┐                                      │
│     (is_from_me, has_attachments)     │                                      │
│                                       ▼                                      │
│                          photo-intake WORKER  (apricot)                      │
│                          polls macsync.messages, pulls attachment bytes      │
│                                       │              ┌───────────────────┐   │
│   Manual upload ──────────────────────┼─────────────│ users/transquinnftw│  │
│   (quinn-admin / quinn.my)            │             │ /originals (masters)│  │
│                                       ▼             └───────────────────┘   │
│            POST /api/skills/process-uploaded-photo  (quinn-ai :3028)          │
└───────────────────────────────────────┬──────────────────────────────────────┘
                                         │  orchestrates 4 admin-backend calls
                                         ▼
┌────────────────────────────────────────────────────────────────────────────┐
│  ② UPLOAD          POST /admin/photos/upload                                 │
│     → writes raw into the protection INPUT POOL, per platform subdir         │
└───────────────────────────────────────┬──────────────────────────────────────┘
                                         ▼
┌────────────────────────────────────────────────────────────────────────────┐
│  ③ PROTECT         POST /admin/photo-protection/run   →  protectPhoto()       │
│     ┌──────────────────────────────────────────────────────────────────┐    │
│     │ L1  Forensic watermark   invisible ±1 spread-spectrum, seeded by   │    │
│     │                          sha256("transquinnftw.com:{file}")        │    │
│     │ L2  Face detection       SCRFD bbox/landmarks                      │    │
│     │ L4  Identity cloaking    PGD vs ArcFace, eps=0.03  (runs first)    │    │
│     │ L3  Detection evasion    PGD vs SCRFD,  eps=0.08                   │    │
│     │ L5  Landmark obfuscation                                            │    │
│     │ L6  VISIBLE WATERMARK ◀── transquinnftw.com SVG composite          │    │
│     │            ★ THIS is the layer the Kuromi techno mark replaces ★   │    │
│     │ L7  SSIM gate            abort if SSIM < 0.75 (quality guard)      │    │
│     │ L8  Finalize             JPEG (mozjpeg) + WebP                      │    │
│     │ L9  Adversary view       saliency / perturbation / detector maps   │    │
│     │                          → public/photos/adversary/{file}/         │    │
│     └──────────────────────────────────────────────────────────────────┘    │
│     ④ POLL  /admin/photo-protection/runs/:id   until status = done           │
└───────────────────────────────────────┬──────────────────────────────────────┘
                                         ▼
┌────────────────────────────────────────────────────────────────────────────┐
│  ⑤ DEPLOY          POST /admin/photo-protection/deploy                        │
│     rsync protected JPEG+WebP (+ manifest.json) to each configured            │
│     deploy target (name → rsync destination URL)                             │
└───────────────────────────────────────┬──────────────────────────────────────┘
                                         ▼
┌────────────────────────────────────────────────────────────────────────────┐
│  ⑥ LIVE ASSET                                                                │
│     quinn-vps : deployments/@domains/quinn.www/root/public/photos/            │
│        ├── {name}.jpeg / .webp     ← gallery <picture> sources                │
│        ├── manifest.json           ← dims/sizes the gallery reads             │
│        └── page-illustrations.json ← decorative section graphics             │
│                                                                              │
│     Served on  transquinnftw.com  (website)  +  platform ad surfaces          │
└──────────────────────────────────────────────────────────────────────────────┘

Where the Kuromi watermark work fits

Two paths, not mutually exclusive:

  1. Backfill (this task): tooling/scripts/watermark/batch.py re-marks the existing library from CLEAN sources → public/photos-watermarked/. One-off correction of the already-deployed set (which carries the old white L6 mark).

  2. Pipeline integration (the durable fix): port the chosen Kuromi style into L6 (buildVisibleWatermarkSvg in watermark.ts) so every FUTURE intake photo gets the new mark automatically — no manual re-run. Note: L6 currently composites an SVG via sharp; the Pillow renderer here would either be reimplemented as SVG or invoked as a sidecar. Decide after Quinn locks the look (placement + opacity).