0ae59f3550
Introduces RECAP_MODE=multi alongside single-mode self-host: - Tenant auth + accounts (magic-link via System SMTP), per-tenant credit pool, anonymous trial minting with per-IP/-64 caps - Self-serve Pro/Max purchase: inline Lightning (BTCPay) + card (Zaprite), prepaid 30-day periods, expiry-reminder emails - Core-decoupling: relay owns cloud tier/expiry keyed by Recaps user-id - SQLite (better-sqlite3) schema for multi-mode; filesystem unchanged for single - StartOS actions/versions through 0.2.155
283 lines
12 KiB
Markdown
283 lines
12 KiB
Markdown
# Path 2B + Path 1 Interweave Plan
|
|
|
|
Companion doc to `architecture-simplification-plan.md` (Path 1) and the
|
|
chat thread that proposed Path 2A (relay-only upload, ship first).
|
|
|
|
This doc covers:
|
|
|
|
1. **Path 2B** — bringing internal-meeting analysis into the Recaps
|
|
cloud frontend as a first-class feature alongside YouTube/podcast
|
|
summaries.
|
|
2. **How Path 2B depends on Path 1** — what Path 1 unlocks vs. what
|
|
could be partially built without it.
|
|
3. **Migration path** — how Path 2A's relay-only upload data flows
|
|
forward into Path 2B's cloud-side library.
|
|
|
|
---
|
|
|
|
## Context
|
|
|
|
Recap Relay (the operator-side backend) is now generic enough to
|
|
analyze ANY audio — not just YouTube/podcasts. The download step is
|
|
the only YouTube-specific code; everything downstream (transcribe →
|
|
diarize → cluster → analyze → polish) applies cleanly to arbitrary
|
|
audio. Path 2A exposes this via a relay-admin-only upload UI so
|
|
operator Grant can run internal meeting analysis on his own hardware
|
|
TODAY without waiting on Recaps multi-tenant work.
|
|
|
|
Path 2B is the longer arc: same capability surfaced in the cloud
|
|
Recaps app, so signed-in users can submit private meeting audio,
|
|
manage it alongside their other content, and (optionally) share with
|
|
colleagues.
|
|
|
|
---
|
|
|
|
## Path 1 (recap) state
|
|
|
|
`architecture-simplification-plan.md` defines:
|
|
|
|
- One Recaps binary, two modes via `RECAP_MODE=single|multi` env var
|
|
- Magic-link + optional password auth via StartOS SMTP
|
|
- Per-user library at `/data/history/<userId>/<sessionId>.json`
|
|
- Per-user keysat license (mintable via Keysat admin API)
|
|
- BTCPay subscription + one-time credit-purchase flows
|
|
- Lite-settings UI for non-operator cloud users
|
|
- Self-hosted operator stays single-tenant by default; the .s9pk
|
|
ships free + open
|
|
|
|
Status: written but not built. The relay-side work has continued in
|
|
parallel (FIFO queue, clustering suppression, polish pass) which
|
|
strengthens the case for Path 1 — the cloud user experience benefits
|
|
from all of it, and the keysat-license layer is increasingly
|
|
friction-without-value for cloud users who already have email-verified
|
|
accounts.
|
|
|
|
---
|
|
|
|
## Path 2B — internal meetings in cloud Recaps
|
|
|
|
### What it looks like
|
|
|
|
A signed-in Recaps user gets a second submission affordance alongside
|
|
the existing "Paste a YouTube/podcast link" input:
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Submit content │
|
|
│ ○ Paste a YouTube/podcast link │
|
|
│ ○ Upload audio file (private) [Choose file…] │
|
|
│ │
|
|
│ Title: [_____________________________] │
|
|
│ Participants: [_____________________________] (opt) │
|
|
│ Meeting type: [▼ default / 1:1 / all-hands / …] │
|
|
│ │
|
|
│ [ Summarize ] │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
The submission flows the same way YouTube submissions do:
|
|
Recaps-app → Relay (`/relay/v1/summarize-upload` for files,
|
|
existing `/relay/v1/summarize-url` for URLs). The relay handles both
|
|
via the same pipeline — only the input step differs (download vs.
|
|
multipart receive).
|
|
|
|
### Library + rendering
|
|
|
|
Each saved session in the cloud user's library has a `type` field:
|
|
|
|
- `"youtube"` — existing rendering (video player + topics + transcript chips)
|
|
- `"podcast"` — existing podcast rendering (audio player + topics + transcript)
|
|
- `"meeting"` — new rendering: no media player; topics + transcript chips
|
|
expandable below each topic card; PLUS a "Meeting analysis" block at
|
|
the top with Decisions / Action Items / Open Questions / Key Quotes
|
|
(the structured-extras pass from Phase 2 of Path 2A)
|
|
|
|
Library list view shows a small icon distinguishing meeting items so
|
|
the user can filter at a glance.
|
|
|
|
### Privacy + sharing
|
|
|
|
Meetings are PRIVATE by default — visible only to the submitting user.
|
|
Two later features:
|
|
|
|
- **Share with team** — a meeting can be optionally shared with N
|
|
named cloud-Recaps users (each looks up by email). Other users see
|
|
it in a "Shared with me" library section.
|
|
- **Export to markdown / PDF** — already exists for YouTube content;
|
|
add the meeting-extras blocks to the export.
|
|
|
|
### Audio handling
|
|
|
|
- Uploaded audio goes from browser → Recaps-app (Node Express,
|
|
multipart middleware) → Recap Relay (forward as multipart to
|
|
`/relay/v1/summarize-upload`).
|
|
- Recaps-app's tmp file is deleted immediately after the relay
|
|
acknowledges receipt.
|
|
- Relay's tmp file is deleted after the pipeline completes.
|
|
- NEITHER side keeps the audio. The TRANSCRIPT + analysis are saved.
|
|
If the user wants to re-process (different prompt set), they'd
|
|
re-upload the audio.
|
|
- The transcript stays in the user's per-user library at
|
|
`/data/history/<userId>/<sessionId>.json`, scoped + isolated.
|
|
|
|
---
|
|
|
|
## Path 2B's hard prerequisite: Path 1
|
|
|
|
Path 2B fundamentally requires multi-tenant auth in Recaps. Without
|
|
Path 1:
|
|
|
|
- No per-user library separation
|
|
- No way to know whether the meeting audio is private to a user vs.
|
|
visible to everyone running this Recaps instance
|
|
- No way to share with named other users
|
|
- No way to bill upload-heavy users differently from URL-only users
|
|
(uploads might warrant a different price tier given the storage
|
|
cost)
|
|
|
|
You COULD build a stripped-down Path 2B on single-tenant Recaps —
|
|
operator uploads audio, it's saved to the operator's library, no
|
|
sharing. But that's roughly equivalent to Path 2A with a fancier UI,
|
|
just on the wrong side of the codebase split (Recaps-app vs. relay).
|
|
Not worth the duplication.
|
|
|
|
So: **ship Path 2A as the immediate beachhead, do Path 1 next, then
|
|
Path 2B on top.**
|
|
|
|
### What Path 1 unlocks (relevant to 2B)
|
|
|
|
The Path 1 doc already covers most of what 2B needs:
|
|
- Per-user library at `/data/history/<userId>/*.json`
|
|
- Auth-aware request scoping (`req.userId`)
|
|
- Per-user keysat license OR the simplified user-id + tier headers
|
|
- BTCPay subscription tracking (for billing upload-heavy use)
|
|
|
|
Path 1's "Lite-settings panel for cloud users" already imagines a
|
|
post-auth Recaps UI without operator config noise. The submission
|
|
input would extend in 2B to add the upload option.
|
|
|
|
The relay-side header migration Path 1 proposes (`X-Recap-User-Id` +
|
|
`X-Recap-User-Tier` replacing the bearer license token) is also
|
|
beneficial for 2B — uploads from a single user with N concurrent
|
|
browsers all carry the same user-id, so credit accounting is per-user
|
|
not per-install.
|
|
|
|
---
|
|
|
|
## How Path 2A's data flows into Path 2B
|
|
|
|
When Path 2A ships (relay-only upload, results saved to
|
|
`/data/internal-meetings/<id>.json` on the relay), those summaries
|
|
are tied to the OPERATOR (Grant) — there's no cloud-user concept yet.
|
|
|
|
When Path 2B lands:
|
|
|
|
1. **Migration script** — walks `/data/internal-meetings/*.json` and
|
|
re-homes each entry under the operator's cloud user account
|
|
(`/data/history/owner/*.json` initially, then `/data/history/
|
|
<operator-user-id>/*.json` after Path 1's owner→admin rename).
|
|
2. **Same JSON shape** — Path 2A should save in a shape compatible
|
|
with Path 2B's expected library shape (chunks + entries + speakers
|
|
+ meeting-extras). One way to guarantee this: design the Phase 1
|
|
save shape now to match what Recaps' `saveToHistory` produces for
|
|
`type=meeting`, even though no Recaps UI consumes it yet.
|
|
3. **No re-processing needed** — transcripts and analysis transfer
|
|
verbatim. The user just sees them appear in their cloud library.
|
|
|
|
The relay-side upload endpoint (`/relay/v1/summarize-upload`) is the
|
|
same in both worlds. Path 2A calls it via the operator dashboard's
|
|
admin auth; Path 2B calls it via Recaps-app server proxying a
|
|
signed-in cloud user's POST.
|
|
|
|
So the relay code path is built once and serves both.
|
|
|
|
---
|
|
|
|
## Operator-editable prompt sets (Phase 3 in 2A; carries to 2B)
|
|
|
|
The "meeting type" dropdown is operator-editable. Each set has:
|
|
- Name (e.g. "1:1 with direct report")
|
|
- Topic-analysis prompt template (replaces the YouTube/podcast version)
|
|
- Meeting-extras prompt template (Decisions / Action Items / ...)
|
|
- Optional metadata schema overrides (e.g. "this meeting type always
|
|
expects 2 participants")
|
|
|
|
Stored in `relay_meeting_prompt_sets_json` config field. Operator
|
|
edits via the dashboard (similar to existing prompts panel). Cloud
|
|
Recaps users pick a set at submission time; the relay applies it
|
|
during analyze + polish.
|
|
|
|
Default sets ship built-in:
|
|
- `default` — neutral meeting prompt, all sections enabled
|
|
- `1on1` — emphasizes Action Items + Open Questions; light on Decisions
|
|
- `all-hands` — emphasizes Decisions + Key Quotes; less actionable
|
|
- `customer-interview` — emphasizes Key Quotes + Open Questions; light
|
|
on Decisions
|
|
- `standup` — short-form; Action Items + Open Questions only
|
|
|
|
---
|
|
|
|
## Suggested order of operations
|
|
|
|
1. **Path 2A Phase 1** — relay-only upload, no extras, basic
|
|
topic+transcript rendering. ~2-3 days.
|
|
2. **Path 2A Phase 2** — meeting-extras analysis pass (Decisions /
|
|
Action Items / Open Questions / Key Quotes). ~1-2 days.
|
|
3. **Path 2A Phase 3** — prompt sets dropdown. ~1 day.
|
|
4. (Use Path 2A in production for some weeks; gather feedback on
|
|
prompts, output quality, UX.)
|
|
5. **Path 1** — multi-tenant Recaps. ~3-4 weeks per the existing
|
|
architecture-simplification doc, modulo amendments.
|
|
6. **Path 2B** — surface internal meetings in cloud Recaps. ~1.5-2
|
|
weeks given Path 1 has shipped. Migrates the Path 2A artifacts
|
|
into the new per-user library.
|
|
|
|
Total wall time: ~6-8 weeks for the full arc. Path 2A capability
|
|
available to operator after step 1 (~2-3 days). Cloud users get
|
|
meetings after step 6.
|
|
|
|
---
|
|
|
|
## Open questions
|
|
|
|
These should be settled BEFORE Path 2B build starts:
|
|
|
|
1. **Pricing for uploads.** Does an upload count the same as a URL
|
|
submission against a user's monthly credit cap? Or is it priced
|
|
differently to reflect the upload bandwidth + storage cost? My
|
|
default: same price (1 credit per submission) — bandwidth cost is
|
|
trivial, storage is just JSON.
|
|
|
|
2. **Audio retention.** Default: never retain. Optional per-user
|
|
setting "keep audio for 7/30/90 days so I can re-process with a
|
|
different prompt set"? Adds operator storage cost; only worth it
|
|
if users actually want it.
|
|
|
|
3. **Sharing model.** Path 2B Phase 1 = no sharing, private only.
|
|
Phase 2 = shared with N named users. Phase 3 = optional public
|
|
share URL. Each phase adds auth/permission complexity. Worth
|
|
designing the data model now (`{ ownerId, sharedWith: [userId] }`)
|
|
even if only ownerId is populated in Phase 1.
|
|
|
|
4. **Speaker name persistence.** If a user identifies "Speaker_A" as
|
|
"Matt Hill" in one meeting, should that name auto-suggest in the
|
|
next meeting if a fingerprint match is found? Requires storing
|
|
fingerprints per-user across meetings. Big privacy + product
|
|
decision. My instinct: opt-in toggle in user settings, default
|
|
off.
|
|
|
|
5. **Meeting type defaults.** Should Recaps' submission flow have a
|
|
default meeting type, or force the user to pick? My instinct:
|
|
default to "default" set; let users pick if they want.
|
|
|
|
---
|
|
|
|
## Decision points for Grant
|
|
|
|
- Confirm the phasing above (2A → 1 → 2B) is the right order
|
|
- Pre-commit to the "uploaded audio is never retained" default — it's
|
|
the privacy-safer choice and aligns with how YouTube downloads
|
|
already work
|
|
- Pick a side on the open questions above before Path 2B starts, OR
|
|
defer them as Phase 2/3 of Path 2B
|