Hosted Capture Pipeline — Email + Voice + Push

Status: design. Target: ADR-0029.

This is the hosted-only continuation of Phase 10. Local self-host already ships the synchronous POST /api/inbox endpoint (ADR-0024). What this doc covers: inbound email, voice transcription, and web push notifications — all of which need hosted infrastructure.

Inbound email (Postmark)

Address shape

Each tenant gets inbox+<slug>@in.Lycato. Configurable forwarder so founder can use a friendlier address ([email protected] → +<slug>@in.Lycato).

Webhook

Reply-by-email

Out of scope for v0.4.0. Reply path is "founder edits in cockpit."

Spam

Postmark's built-in spam filter + sender allowlist on the tenant side (set in Settings).

Voice capture (Whisper)

Flow

  1. PWA "record" button → captures audio as audio/webm (Opus).
  2. POSTs to /api/inbox/voice with the audio blob (≤2 minutes, ≤5 MB).
  3. Server stores raw audio under inbox/attachments/<id>/audio.webm.
  4. Server calls Whisper (BYO OpenAI key from the tenant secret store) with the audio.
  5. Transcript written to inbox/<id>.md as the body. Title is the first sentence (capped 80 chars).
  6. Original audio retained for 30 days then deleted (founder can opt-in to keep longer).

Why Whisper not local

Local Whisper.cpp would require shipping a 200MB+ model per container. Cost asymmetry: a paid Whisper API call is ~$0.006/min of audio; voice capture is rare; the per-tenant amortised cost is negligible.

Cost guardrails

Web push notifications

Use cases

  1. High-risk approval pending for >2 hours.
  2. Daily-digest agent failed during scheduled run.
  3. Cofounder approved a branch (J2 closes faster).

Stack

Opt-in

Per-user, per-kind. Founder can subscribe to "approval pending" but mute "daily-digest failed" if it's noisy. Default = all off; opt-in on first cockpit visit after enabling notifications.

Privacy

No content in payloads. Subscription endpoints are encrypted with the tenant DEK (treated as a key-equivalent because they can be abused to send push to the founder's device).

Local self-host

Self-host installs do NOT get any of this. Workarounds:

Self-host parity for these is not a v0.4.0 goal; if a self-host founder pushes for it, the SDK already provides the surface to build it.

What's intentionally absent