Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
09c4562
docs(live): add go live spaces design spec
rabble Apr 6, 2026
35ef957
docs(live): add go live implementation plan
rabble Apr 6, 2026
8f1551d
feat(live): add go live feature flags and media dependency
rabble Apr 6, 2026
85eb8ea
feat(live): add live room domain models and Nostr codec
rabble Apr 6, 2026
4a3722c
feat(live): add backend and LiveKit service boundaries
rabble Apr 6, 2026
e2eac31
feat(live): add repositories and dependency wiring
rabble Apr 6, 2026
6afc226
feat(live): add discovery, room, chat, and host blocs
rabble Apr 6, 2026
e5f2548
fix(live): satisfy analyzer import ordering
rabble Apr 6, 2026
442268f
fix(live): collapse livestream rollout to one flag
rabble Apr 6, 2026
22c7e22
feat(live): add public room discovery and host beta flow
rabble Apr 6, 2026
aef9f51
feat(live): harden mobile hosting and add replay handoff
rabble Apr 6, 2026
1fac6ab
build(live): sync provider outputs
rabble Apr 6, 2026
1a96323
feat(live): flesh out live moderation and discovery surfaces
rabble Apr 7, 2026
12c8279
test(live): relax audience moderation assertion
rabble Apr 7, 2026
ed197d0
fix(live): remove zap action from room view
rabble Apr 7, 2026
af1d1f8
docs(live): add divine live server design
rabble Apr 7, 2026
4d04d7c
docs(live): add divine live server implementation plan
rabble Apr 7, 2026
fb17e64
docs(live): sync live server design and plan
rabble Apr 8, 2026
a4c8f57
fix(live): stabilize golden tests in ci
rabble Apr 8, 2026
908c165
feat(live): wire mobile client to dedicated live api
rabble Apr 8, 2026
908b962
feat(live): make livestream a first-class explore tab
rabble Apr 8, 2026
2bdb345
fix(live): preserve go live route normalization
rabble Apr 8, 2026
68c60c8
fix(live): defer host media capture until user action
rabble Apr 8, 2026
8778a49
docs(live): add go live prefill design
rabble Apr 8, 2026
ce268ad
feat(live): prefill go live setup from host profile
rabble Apr 8, 2026
71f5d01
fix(live): disable fast-connect capture on room join
rabble Apr 8, 2026
5030d22
docs(live): add live room usability design
rabble Apr 9, 2026
f358ba4
fix(live): add back affordances to live flows
rabble Apr 9, 2026
86ebff2
fix(live): scope chat to active session
rabble Apr 9, 2026
32cbf27
fix(live): tighten live room chat and stage ui
rabble Apr 9, 2026
b7b77d3
fix(tests): stub live widget nip05 verification
rabble Apr 9, 2026
f8e9756
test(live): refresh live goldens for profile-backed ui
rabble Apr 9, 2026
8af23f6
test(live): stabilize live room widget coverage
rabble Apr 9, 2026
7a7c450
fix(live): show local host state before publishing
rabble Apr 9, 2026
5d92ecd
fix(live): echo chat and clarify host media controls
rabble Apr 9, 2026
2b46bf9
test(live): refresh host room golden
rabble Apr 9, 2026
7554762
docs(live): add single active host live design
rabble Apr 9, 2026
a6accb8
fix(live): resume existing active host sessions
rabble Apr 9, 2026
6093072
fix(live): gate host camera publish behind permissions
rabble Apr 9, 2026
d51705b
fix(macos): keep debug signing enabled
rabble Apr 9, 2026
232a73c
fix(live): defer macos camera prompts to camera stack
rabble Apr 9, 2026
ab2af39
fix(live): make host media state truthful
rabble Apr 9, 2026
6d11275
fix(live): request macos media permissions before publish
rabble Apr 9, 2026
a747b83
fix(macos): harden media permission prompts
rabble Apr 9, 2026
263c424
style(divine_ui): format vine theme
rabble Apr 9, 2026
5a26798
fix(live): update rebase wiring and goldens
May 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
910 changes: 910 additions & 0 deletions docs/superpowers/plans/2026-04-06-go-live-spaces.md

Large diffs are not rendered by default.

136 changes: 136 additions & 0 deletions docs/superpowers/plans/2026-04-08-divine-live-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Divine Live Server Implementation Plan

> **For agentic workers:** REQUIRED: use `superpowers:subagent-driven-development` or `superpowers:executing-plans` when continuing implementation. This document is now a progress tracker synced to the real backend repo, not the original greenfield checklist.

Status: In Progress
Last synced against: `../divine-live-server` `main` at `64a497c` on 2026-04-08.

**Goal:** Finish the dedicated Rust/Axum service in `../divine-live-server` on `live.api.divine.video` that verifies NIP-98 auth, persists live room/session state, mints LiveKit Cloud tokens, and handles replay webhooks for the mobile live feature.

**Architecture:** Standalone live control-plane service with an Axum router, Postgres persistence through `sqlx`, a NIP-98 verifier for caller identity, a LiveKit Cloud integration layer for token minting and webhook verification, and route handlers that keep Nostr as the public source of truth while enforcing server-authoritative media permissions.

**Tech Stack:** Rust, Axum, Tokio, SQLx + Postgres, Serde, Chrono, JsonWebToken, HMAC/SHA256, Tracing, Tower HTTP, Docker, and GKE manifests.

---

## Current Status

- Hosted LiveKit Cloud is authenticated locally for the `divine` project
- the backend repo already exists at `/Users/rabble/code/divine/divine-live-server`
- runtime startup in `src/main.rs` loads config, connects to Postgres, applies migrations, and serves the Axum app
- the router in `src/app.rs` exposes:
- `GET /health`
- `POST /v1/live/rooms`
- `POST /v1/live/rooms/:roomId/sessions`
- `POST /v1/live/rooms/:roomId/sessions/:sessionId/end`
- `POST /v1/live/rooms/:roomId/join`
- `PUT /v1/live/rooms/:roomId/participants/:pubkey/role`
- `GET /v1/live/rooms/:roomId/recording`
- `POST /v1/live/webhooks/livekit`
- deployment packaging exists in `Dockerfile` and `k8s/`
- the repo is on `main` and recent progress has already landed there

## Progress By Task

### Task 1: Scaffold The New Repo

- [x] Repo scaffolded in `d9dda0f chore(live): scaffold divine live server`
- [x] Health route and smoke tests exist in `src/routes/health.rs` and `tests/health_routes.rs`

### Task 2: Add Typed Config, Shared State, And Error Responses

- [x] Config, error, and shared state foundation landed in `6038f42 feat(live): add config and app state foundation`
- [x] Health output includes safe service metadata

### Task 3: Implement NIP-98 Verification

- [x] NIP-98 verification landed in `bc461ce feat(live): add NIP-98 request verification`
- [x] Coverage exists for valid auth, stale events, wrong URL, wrong method, wrong payload, and malformed base64 in `tests/nip98_auth.rs`

### Task 4: Add Postgres Schema And Repository Layer

- [x] Initial schema exists in `migrations/0001_init.sql`
- [x] Postgres models and repositories exist under `src/db/` and `src/repos/`
- [x] Production runtime uses `AppState::from_pg_pool(...)`
- [ ] Add sqlx-backed integration tests that exercise the Postgres path directly instead of relying mostly on the in-memory test backend
- [ ] Decide whether the in-memory backend should remain test-only or be moved behind a narrower test helper boundary

### Task 5: Implement Room Draft, Session Start, And Session End Routes

- [x] Lifecycle routes landed in `2025fe0 feat(live): add control plane routes and gke packaging`
- [x] Coverage exists in `tests/live_rooms_routes.rs`

### Task 6: Add LiveKit Token Minting And Join Authorization

- [x] Join token minting exists in `src/livekit/tokens.rs`
- [x] Join authorization exists in `src/routes/live_rooms.rs`
- [x] Coverage exists in `tests/live_join_routes.rs`

### Task 7: Add Speaker Role Grant Route

- [x] `PUT /v1/live/rooms/:roomId/participants/:pubkey/role` is implemented
- [x] Host-only authority and speaker/audience role semantics are covered in `tests/live_join_routes.rs`

### Task 8: Add Replay Lookup And LiveKit Webhook Handling

- [x] Replay lookup route and webhook route are implemented
- [x] Recording-ready handling exists
- [x] Webhook authorization was tightened in `9fb99dd fix(live): verify livekit webhook authorization`
- [ ] Reconcile the configured `LIVEKIT_WEBHOOK_SECRET` with the actual verification code, or remove the unused setting if it is not needed
- [ ] Decide whether additional LiveKit webhook event types should update room/session state in v1

### Task 9: Add Final Service Hardening And Developer Docs

- [x] `README.md` and `.env.example` exist
- [x] Container packaging exists and was hardened in:
- `2025fe0 feat(live): add control plane routes and gke packaging`
- `84391ce build(live): include lockfile in container build`
- `64a497c build(live): use rust 1.93 in docker image`
- [ ] Expand local development docs with example NIP-98 request flows and Postgres bootstrap steps
- [ ] Add structured request ids, metrics, and audit-friendly logs
- [ ] Add CI or release automation if we want reproducible deploys beyond local `docker build`

## Remaining Execution Slices

### Slice 1: Persistence And Verification Hardening

- [ ] Add Postgres-backed integration tests for migrations, repositories, and route handlers
- [ ] Verify create/start/join/end/recording flows against the real database path
- [ ] Tighten conflict/error mapping so Postgres constraint failures return stable API errors

### Slice 2: Webhook And Replay Cleanup

- [ ] Settle the canonical LiveKit webhook verification approach
- [ ] Either wire `LIVEKIT_WEBHOOK_SECRET` into code or remove it from config, docs, and manifests
- [ ] Decide whether replay URLs should be rewritten behind a Divine domain or stored exactly as provided by LiveKit

### Slice 3: Observability And Ops

- [ ] Add structured request ids to request logs
- [ ] Add counters for auth failures, token issuance, session starts, session ends, and webhook failures
- [ ] Document GKE deploy expectations more concretely, including secret names and rollout checks

### Slice 4: Mobile Integration Follow-Up

- [ ] Add a dedicated `LIVE_API_URL` to the mobile client instead of routing live traffic through the generic backend base URL
- [ ] Call the participant role endpoint when hosts promote or demote speakers
- [ ] Verify end-to-end mobile start/join/replay against the deployed live server

## Suggested Verification Commands

Run from `/Users/rabble/code/divine/divine-live-server`:

```bash
cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets
```

If Postgres-backed tests are added, include the DB boot or test harness command in this section when that slice lands.

## Out Of Scope For This Plan

- mobile discovery and room UI work
- `divine-funnelcake` changes
- custom SFU or self-hosted LiveKit work
- paywalls, private rooms, or non-Nostr guest access
76 changes: 76 additions & 0 deletions docs/superpowers/plans/2026-04-09-go-live-prefill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Go Live Prefill Implementation Plan

> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Prefill the livestream `Go live` form from the host's cached profile and show the host avatar as the default cover preview.

**Architecture:** Keep the change local to the live host flow. Resolve the cached profile in `GoLivePage`, derive initial form defaults there, pass them into `GoLiveCubit`, and let `GoLiveView` seed its controllers from state once while rendering a lightweight cover preview from the same image URL.

**Tech Stack:** Flutter, flutter_bloc, Riverpod provider wiring, existing profile repository, widget tests

---

## Chunk 1: Prefill State Wiring

### Task 1: Capture the desired prefill behavior in tests

**Files:**
- Modify: `mobile/test/screens/live/go_live_page_test.dart`
- Reference: `mobile/lib/screens/live/go_live_page.dart`
- Reference: `mobile/lib/screens/live/go_live_view.dart`

- [ ] **Step 1: Write the failing tests**

Add widget coverage that verifies:
- a cached profile prefills title, summary, and cover image URL
- the form shows a visible avatar/cover preview
- no cached profile keeps the form blank

- [ ] **Step 2: Run the targeted test to verify it fails**

Run: `flutter test test/screens/live/go_live_page_test.dart`
Expected: FAIL because the page currently renders blank controllers and no preview.

- [ ] **Step 3: Write the minimal implementation**

Update the page, cubit, state, and view so:
- `GoLivePage` loads the cached profile for the current host
- it derives initial defaults from `bestDisplayName` and `picture`
- `GoLiveCubit` starts from those values
- `GoLiveView` seeds its controllers once from bloc state and renders the cover preview

- [ ] **Step 4: Run the targeted test to verify it passes**

Run: `flutter test test/screens/live/go_live_page_test.dart`
Expected: PASS

- [ ] **Step 5: Commit**

```bash
git add mobile/lib/blocs/go_live/go_live_cubit.dart mobile/lib/blocs/go_live/go_live_state.dart mobile/lib/screens/live/go_live_page.dart mobile/lib/screens/live/go_live_view.dart mobile/test/screens/live/go_live_page_test.dart docs/superpowers/specs/2026-04-09-go-live-prefill-design.md docs/superpowers/plans/2026-04-09-go-live-prefill.md
git commit -m "feat(live): prefill go live setup from host profile"
```

## Chunk 2: Focused Verification

### Task 2: Verify the prefill UX and live host flow stay healthy

**Files:**
- Reference: `mobile/lib/screens/live/go_live_page.dart`
- Reference: `mobile/lib/screens/live/go_live_view.dart`
- Reference: `mobile/test/screens/live/go_live_page_test.dart`

- [ ] **Step 1: Run focused widget verification**

Run: `flutter test test/screens/live/go_live_page_test.dart test/screens/live/live_room_page_test.dart`
Expected: PASS

- [ ] **Step 2: Run focused analyze verification**

Run: `flutter analyze lib/blocs/go_live lib/screens/live test/screens/live/go_live_page_test.dart test/screens/live/live_room_page_test.dart`
Expected: PASS

- [ ] **Step 3: Review the diff**

Run: `git diff --stat`
Expected: only live host UI/state/docs coverage changed
74 changes: 74 additions & 0 deletions docs/superpowers/plans/2026-04-09-live-explore-tab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Live Explore Tab Implementation Plan

> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Make livestream discovery a first-class Explore tab instead of a promo card above the tab strip.

**Architecture:** Keep the change local to `ExploreScreen`: extend the existing tab bookkeeping with a gated `live` entry, remove the promo-card rendering path, and mount the existing embedded live discovery UI inside the `TabBarView`. Preserve the existing standalone live routes for direct navigation and deep links.

**Tech Stack:** Flutter, Riverpod compatibility glue, existing live screens/widgets, Flutter widget tests

---

## Chunk 1: Explore Tab Wiring

### Task 1: Capture the failing Explore behavior in tests

**Files:**
- Modify: `mobile/test/screens/explore_screen_test.dart`
- Reference: `mobile/lib/screens/explore_screen.dart`

- [ ] **Step 1: Write the failing tests**

Add tests that verify:
- when `FeatureFlag.livestreamingBeta` is enabled, Explore shows a `Live` tab label
- the old `LiveExploreEntryCard.entryKey` is absent

- [ ] **Step 2: Run the targeted test to verify it fails**

Run: `flutter test test/screens/explore_screen_test.dart`
Expected: FAIL because Explore still renders the promo card and has no `Live` tab.

- [ ] **Step 3: Write the minimal implementation**

Update `mobile/lib/screens/explore_screen.dart` so:
- `_tabCount` includes the gated live tab
- `_tabNames` includes `live`
- the `TabBar` adds a `Tab(text: 'Live')`
- the `TabBarView` adds the embedded live discovery content
- the promo-card branch is removed

- [ ] **Step 4: Run the targeted test to verify it passes**

Run: `flutter test test/screens/explore_screen_test.dart`
Expected: PASS

- [ ] **Step 5: Commit**

```bash
git add mobile/lib/screens/explore_screen.dart mobile/test/screens/explore_screen_test.dart docs/superpowers/specs/2026-04-09-live-explore-tab-design.md docs/superpowers/plans/2026-04-09-live-explore-tab.md
git commit -m "feat(live): make livestream a first-class explore tab"
```

## Chunk 2: Verification

### Task 2: Verify the live Explore UX did not regress

**Files:**
- Reference: `mobile/lib/screens/explore_screen.dart`
- Reference: `mobile/lib/screens/live/live_discovery_view.dart`

- [ ] **Step 1: Run focused widget verification**

Run: `flutter test test/screens/explore_screen_test.dart test/screens/live/live_discovery_page_test.dart`
Expected: PASS

- [ ] **Step 2: Run focused analyze verification**

Run: `flutter analyze lib/screens/explore_screen.dart lib/screens/live test/screens/explore_screen_test.dart test/screens/live/live_discovery_page_test.dart`
Expected: PASS

- [ ] **Step 3: Review git diff for unintended churn**

Run: `git diff --stat`
Expected: only Explore/live UI docs and tests changed
Loading
Loading