Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6d15305
phase-0: v2 adapter contract + conformance gate
returnsvoidjanet Jun 21, 2026
2cdcb9d
phase-0: PASETO v4.local grant stream-proxy core (held-cred, m3u8 rew…
returnsvoidjanet Jun 21, 2026
fb0da7a
phase-0: Jellyfin v2 adapter + negotiate/stream-proxy glue + test har…
returnsvoidjanet Jun 21, 2026
039cb13
phase-0(stream): complete copy-paste/identity binding end-to-end
returnsvoidjanet Jun 21, 2026
8c6b303
chore(dev): bypass pnpm verify-deps so vite dev runs the local proxy …
returnsvoidjanet Jun 21, 2026
a1ce335
phase-0(invidious): v2 adapter — Nexus-identity, grant-gated, local=true
returnsvoidjanet Jun 21, 2026
6e872b1
phase-0(stream): fix gzipped-text upstreams (proxy negotiates identity)
returnsvoidjanet Jun 21, 2026
1de4530
phase-0(stream): fail CLOSED on proxy handoff + widen handoff timeout
returnsvoidjanet Jun 21, 2026
ff51287
phase-0(reaping): abandoned-session reaper + force-kill transcode on …
returnsvoidjanet Jun 21, 2026
9bea3d7
phase-0(playback): reactive recovery on mid-stream grant expiry (deci…
returnsvoidjanet Jun 21, 2026
5f4bdf4
phase-0(invidious): adaptive DASH up to 4K through Nexus
returnsvoidjanet Jun 21, 2026
f6f9b38
phase-0(playback): smart bitrate — direct-play first + measured-bandw…
returnsvoidjanet Jun 21, 2026
fe0a9e4
phase-0(playback): mid-stream adaptation watchdog + player buffer/fas…
returnsvoidjanet Jun 21, 2026
551d53a
phase-0(playback): fix probe var collision — mid-stream downshift now…
returnsvoidjanet Jun 21, 2026
79efdfd
phase-0(account): wire v2 playback → continue-watching (progress pers…
returnsvoidjanet Jun 21, 2026
5d7692f
security(proxy): close critical SSRF holes found by adversarial review
returnsvoidjanet Jun 21, 2026
23399dc
security(adversarial): non-waivable conformance + session cap + progr…
returnsvoidjanet Jun 21, 2026
37dd9b1
security(proxy): seam↔proxy shared secret + remove the 'legacy' ident…
returnsvoidjanet Jun 21, 2026
c87bc9e
security(seam): fix allowlist traversal bypass (adversarial review of…
returnsvoidjanet Jun 21, 2026
c39e81c
security(proxy): remove the legacy SSRF routes outright (belt-and-sus…
returnsvoidjanet Jun 21, 2026
4864ecb
phase-0(account): add Better Auth + config foundation (identity layer)
returnsvoidjanet Jun 21, 2026
1e4aa9f
phase-0(account): wire Better Auth live — login/register/logout + ses…
returnsvoidjanet Jun 21, 2026
02f4022
fix: clear pre-existing typecheck errors from the streaming phases
returnsvoidjanet Jun 21, 2026
0612d8b
phase-0(plex): v2 Plex adapter — verified E2E against a live Plex server
returnsvoidjanet Jun 21, 2026
9e17804
phase-0(requests): native request system → Radarr/Sonarr (verified E2E)
returnsvoidjanet Jun 21, 2026
3c00db3
phase-0(cleanup): change-password via BA, decommission v1 Plex login,…
returnsvoidjanet Jun 21, 2026
5b3d7c2
phase-0(tests): backend test suite + extract Plex capability gate
returnsvoidjanet Jun 21, 2026
bf63921
phase-0(auth): OIDC scopes for Authentik login
returnsvoidjanet Jun 21, 2026
93da194
phase-0(auth): OIDC user mapping + account linking — login verified E2E
returnsvoidjanet Jun 21, 2026
784bccc
phase-0(auth): idempotent boot-time credential-account backfill
returnsvoidjanet Jun 22, 2026
0664f43
phase-0(ui): paper+ink Nexus Home preview at /dev + design tokens
returnsvoidjanet Jun 22, 2026
d7888a5
fix(build): approve better-sqlite3/esbuild builds + pin pnpm 11.1.2
returnsvoidjanet Jun 22, 2026
ac30278
fix(build): guard module-load side effects with building flag
returnsvoidjanet Jun 22, 2026
f19ca6e
phase-0: clean re-implementation — strip all legacy code
returnsvoidjanet Jun 22, 2026
34f41eb
fix: restore /api/health endpoint (over-deleted in the strip; healthc…
returnsvoidjanet Jun 22, 2026
4719c2c
phase-0(auth): Authentik SSO passthrough, delete app login/welcome
returnsvoidjanet Jun 22, 2026
32dd518
fix(home): image proxy by service type, smooth rail, wire avatar
returnsvoidjanet Jun 22, 2026
f578224
fix(home): gmail-style rail + don't stream non-video items
returnsvoidjanet Jun 22, 2026
1fd1c9f
fix(home): pin rail icons to constant offset (no horizontal jump)
returnsvoidjanet Jun 22, 2026
6d539d4
feat(home): external art fallback (iTunes) + thin theme-aware scrollbars
returnsvoidjanet Jun 22, 2026
b7de9cf
fix(art): fire iTunes fallback for artless albums (posterItemId fell …
returnsvoidjanet Jun 22, 2026
84f583c
fix(home): graceful onerror on card art (degrade to clean tile)
returnsvoidjanet Jun 22, 2026
99979f5
chore(phase-0): commit deployed work — fake-cleanup, real search, col…
returnsvoidjanet Jun 23, 2026
bd069d9
feat(library): per-type library pages — bold "archive" design, real data
returnsvoidjanet Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,6 @@ stream-proxy/target/
.claude/
sv-agentation
CLAUDE.md

# Runtime data (live DB, backups) — never commit
data/
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
enable-pre-post-scripts=true
verify-deps-before-run=false
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# tracking doesn't always detect source changes when a cached dummy build is
# replaced with real source, producing a binary from the dummy. Accepting a
# longer first-build time in exchange for a correct build every time.
FROM rust:1.85-alpine AS rust-build
FROM rust:1.88-alpine AS rust-build
RUN apk add --no-cache musl-dev pkgconfig openssl-dev openssl-libs-static
WORKDIR /stream-proxy
COPY stream-proxy/Cargo.toml stream-proxy/Cargo.lock ./
Expand All @@ -18,7 +18,7 @@ RUN cargo build --release

FROM node:22-alpine AS deps
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN corepack enable && corepack prepare pnpm@11.1.2 --activate
# python3 + build tools: better-sqlite3 tries prebuild-install first but falls
# back to node-gyp compilation when the prebuilt binary for this exact Node
# version isn't available. Without these, the fallback fails with "gyp ERR!
Expand All @@ -33,14 +33,14 @@ RUN pnpm install --frozen-lockfile

FROM node:22-alpine AS build
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN corepack enable && corepack prepare pnpm@11.1.2 --activate
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build

FROM node:22-alpine AS runtime
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN corepack enable && corepack prepare pnpm@11.1.2 --activate
# Same reason as deps stage — better-sqlite3 may fall back to node-gyp.
RUN apk add --no-cache python3 make g++

Expand Down
15 changes: 5 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,11 @@ Self-hosters running multiple media services who want:

| Service | Status | What it provides |
|---------|--------|-----------------|
| **Jellyfin** | ✅ stable | Media server — movies, shows, music, live TV |
| **Plex** | ✅ beta | Media server — movies, shows, music (playback path stabilized in v0.1.0-beta.2) |
| **Invidious** | ✅ stable | Privacy-respecting YouTube alternative (transcode pipe via Rust stream-proxy) |
| **Calibre-Web** | ✅ beta | Book library — OPDS browse, search, formats, in-browser reader. UI polish ongoing |
| **RomM** | ✅ stable | Retro game ROM management with in-browser emulation |
| **Overseerr / Seerr** | ✅ stable | Media requests and TMDB-powered discovery |
| **Radarr / Sonarr / Lidarr** | ✅ stable | Calendar, queue, quality profiles |
| **Bazarr** | ✅ stable | Subtitle management, sync, translation |
| **Prowlarr** | ✅ stable | Indexer management and stats |
| **StreamyStats** | ✅ stable | ML-powered recommendations and analytics |
| **Jellyfin** | ✅ working | Movies & shows — library, recently-added, and transcoded playback end-to-end via the Rust stream-proxy |
| **Invidious** | ⚠️ playback only | Privacy-respecting YouTube — playback works end-to-end; browse/search not yet wired, so it doesn't surface on the home page yet |
| **Plex** | 🚧 unwired | Adapter is implemented but has no config path in phase-0 (no env/DB/UI to add a Plex server), so it's not reachable yet |

> **Phase-0 is a clean re-implementation, not a port.** It ships exactly these three media-source adapters plus the streaming core, the home feed, and a bare playback test page. The earlier project's broader service list (Overseerr/Seerr, Radarr/Sonarr/Lidarr, Bazarr, Prowlarr, Calibre-Web, RomM, StreamyStats) is **not** part of this build. Those will be reintroduced only when actually built and verified — this table tracks reality, not intent.

New adapters can be added by contributors without modifying any existing code. See [CONTRIBUTING.md](CONTRIBUTING.md) for the adapter development guide.

Expand Down
40 changes: 22 additions & 18 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,29 @@ Updated as work progresses. Each item links to its GitHub issue and/or spec/plan

---

## Milestone 0: Foundation (DONE)
## Phase-0 Rebuild — Actual Status

This is a **clean re-implementation**. The current build ships only what this table
lists. Everything below "Milestone 1" describes the **pre-rebuild project** and is kept
only as historical / feature reference — it does **not** reflect what runs today.

| Status | Item |
|--------|------|
| ✅ done | Jellyfin adapter — library, recently-added, transcoded playback (verified end-to-end against live Jellyfin) |
| ✅ done | Streaming core — negotiate + PASETO grants + Rust stream-proxy (real decodable bytes, SSRF-guarded, fail-closed) |
| ✅ done | Home feed — real Jellyfin content, backend-agnostic rows, real library-type filters (no fake chrome) |
| ✅ done | Auth — Authentik SSO passthrough (no app-owned login screens) |
| ✅ done | Bare playback test page (`/test-play`) — the sanctioned "dumb streaming page" |
| ⚠️ partial | Invidious adapter — playback works end-to-end; browse/search/listing NOT implemented, so it doesn't surface on the home yet |
| 🚧 unwired | Plex adapter — fully implemented but no config path (no env/DB/UI), so it's unreachable |
| ❌ not built | Search — `adapter.search` exists but is unwired; no `/api/search`, no search UI |
| ❌ not built | Subtitle delivery — negotiate advertises track URLs, but `/api/subtitles/*` route is missing (they 404) |
| ❌ not built | Media detail pages; now-playing/sessions UI |
| ❌ not built | Request management / Radarr / Sonarr / Overseerr — **no code path exists; do not advertise as available** |
| ❌ not built | Analytics / stats / admin dashboard |
| ❌ not built | Calibre-Web, RomM, StreamyStats, Bazarr, Prowlarr adapters |

Everything needed to run Nexus as a functional media platform.

| Status | Item | Issue | Spec/Plan |
|--------|------|-------|-----------|
| done | Core adapters (Jellyfin, Radarr, Sonarr, Lidarr, Overseerr, Prowlarr, Bazarr) | — | — |
| done | Per-user auth with service account linking | — | — |
| done | Homepage with personalized recommendations | — | [spec](docs/superpowers/specs/2026-03-11-personalized-homepage-design.md) |
| done | Media detail pages with cast, similar, seasons | — | — |
| done | Search across all services | — | — |
| done | Request management (Overseerr) | — | [spec](docs/superpowers/specs/2026-03-12-requests-page-fixes-design.md) |
| done | Analytics engine + stats | — | [spec](docs/superpowers/specs/2026-03-13-tracking-system-rebuild-design.md) |
| done | Admin dashboard (sessions, health, requests) | — | — |
| done | Invidious adapter (privacy video) | — | — |
| done | Calibre-Web adapter (books) | — | — |
| done | RomM adapter (retro games + EmulatorJS) | — | — |
| done | StreamyStats adapter (ML recommendations) | — | — |
| done | Bazarr adapter (subtitle enrichment) | — | — |
---

## Milestone 1: Polish & Beta (DONE)

Expand Down
Binary file added nexus-collapsed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added nexus-home-live.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@
"tailwindcss": "^4.1.0",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vite-plugin-wasm": "^3.6.0",
"vitest": "^4.1.2"
},
"dependencies": {
"@fontsource-variable/dm-sans": "^5.2.8",
"@fontsource-variable/geist": "^5.2.9",
"@fontsource-variable/geist-mono": "^5.2.8",
"@fontsource-variable/jetbrains-mono": "^5.2.8",
"@fontsource-variable/playfair-display": "^5.2.8",
"better-auth": "^1.6.20",
"better-sqlite3": "^11.10.0",
"chart.js": "^4.5.1",
"dashjs": "^5.1.1",
Expand All @@ -53,6 +57,8 @@
"hls.js": "^1.6.15",
"lru-cache": "^11.3.5",
"lucide-svelte": "0.469.0",
"nucleo-matcher-wasm": "^0.4.0",
"paseto-ts": "^2.0.6",
"pdfjs-dist": "5.5.207",
"undici": "^8.1.0",
"ws": "^8.19.0",
Expand Down
Loading