knowledge.md 5.8 KB

MixBoard — Knowledge Base

Accumulated findings from investigations, deliberations, and development sessions. Updated automatically after each significant investigation.


Chad Music Integration (2026-03-13)

ADR: API Strategy — Custom REST over Subsonic (2026-03-13)

  • Decision: Keep chad-music's custom REST API, no Subsonic implementation
  • Rationale: MixBoard is the only client. Subsonic = 70+ endpoints for zero benefit. All three reviewer models agreed.
  • Status: Active
  • Key entities: ChadMusicAPIClient, /api/cat/:category, /api/album/:id/tracks

ADR: Streaming — Direct HTTP + Range Requests (2026-03-13)

  • Decision: Direct HTTP file serving with Range requests (206 Partial Content). No HLS in early phases.
  • Rationale: HLS breaks gapless playback on concept albums. Single user = no need for adaptive bitrate. AVPlayer handles direct HTTP natively.
  • Status: Active
  • Key entities: AVPlayer, AVURLAsset, AVURLAssetHTTPHeaderFieldsKey

ADR: Playback Engine — AVPlayer First (2026-03-13)

  • Decision: AVPlayer for ALL playback in Phase 1 (both local file:// and cloud https:// URLs). AVAudioEngine path added in Phase 3 for cached tracks needing EQ/BPM.
  • Rationale: AVPlayer handles buffering, seeking, gapless (AVQueuePlayer), and AirPlay out of the box. Dual engines add complexity.
  • Status: Active

ADR: Auth — API Key via HTTP Header (2026-03-13)

  • Decision: API key in macOS Keychain, sent via Authorization: Bearer header. Use AVURLAssetHTTPHeaderFieldsKey for stream URLs (undocumented but working Apple API).
  • Rationale: Simplest auth for personal use. Telegram login deferred to Phase 4.
  • Status: Active
  • Risk: AVURLAssetHTTPHeaderFieldsKey is undocumented — may break in future macOS versions

Key Risks Identified

  1. Woo/Clack Range request support — MUST verify with curl before coding
  2. Static file auth — file-server routes may bypass with-user macro
  3. TLS — AVPlayer rejects self-signed certs, need Tailscale or Caddy
  4. OGG Vorbis — NOT supported by AVPlayer on macOS

Phase Plan

  • Phase 1 (Stream & Browse): ~20-35 hours — zero backend changes needed
  • Phase 2 (Upload & Sync): ~20-30 hours — new server endpoints
  • Phase 3 (Native Polish): ~15-20 hours — cache, loudness, Spotlight
  • Phase 4 (Social & Advanced): ~10-20 hours — Telegram, Shortcuts

Brainstorm Spec

Full spec at: Work vault → .orchestra/mixboard-chadmusic/spec.md

Phase 1 Implementation (2026-03-13) — COMPLETE

  • Files created: ChadMusic.swift (models), KeychainService.swift, ChadMusicAPIClient.swift, StreamingPlayer.swift, CloudBrowserView.swift
  • Files modified: PlayerViewModel.swift (dual engine routing), SettingsView.swift (Chad Music tab), SidebarView.swift (cloud section), ContentView.swift (cloud browser routing)
  • Architecture: StreamingPlayer (AVPlayer) for cloud, AudioEngine (AVAudioEngine) for local — routed via isCloudPlayback flag in PlayerViewModel
  • Cloud tracks are transient — ChadTrack is Codable struct, not SwiftData. No DB persistence until Phase 2 sync.
  • Build note: Must run xcodegen generate after adding new source files
  • Codex review finding (fixed): URL path composition changed from .appending(path:) to URLComponents for proper encoding
  • Gemini review: False positive — applied D365 rubric to Swift project (review extension prompt template still has D365 residue for this workspace)
  • Tests: 143 pass (38 new ChadMusic tests), 0 failures, no regressions

Pre-flight Verification Results (2026-03-13)

1. TLS / Network

  • Server is publicly reachable — no Tailscale needed for connectivity
  • URL: https://music.chad-partners.com (confirmed via curl 2026-03-14)
  • Behind Nginx 1.27.0 reverse proxy with HTTPS (HSTS enabled, max-age=31536000)
  • Gogs domain: gogs.chad-partners.com
  • Default port is 5000 (from main function: &key (port 5000)) — Nginx proxies to it
  • file-server is only enabled when serve-files flag is passed to main

1b. Range Request Support — VERIFIED ✅

  • curl -sI -H 'Range: bytes=0-1000' https://music.chad-partners.com/HTTP 206, Content-Range: bytes 0-1000/2998
  • Nginx handles Range natively — no server-side changes needed
  • AVPlayer streaming will work out of the box

2. Server Architecture (from server.lisp, verified 2026-03-14)

  • Woo HTTP server (libev-based) via Clack
  • Default port: 5000
  • Auth: Telegram-based login flow → generates random 16-byte hex token → stored in server-side hash table
  • with-user macro checks Authorization: Bearer <token> header
  • CLI bypass: if no x-real-ip header AND no valid token → auto-auth as admin (for local dev/REPL)
  • File serving: only active when (main :serve-files t) is passed
  • Path mapping: config.lisp maps filesystem paths to URL prefixes (e.g. /media/music//music/)
  • Routes: /api/cat/:category, /api/cat/:category/size, /api/album/:id/tracks, /api/stats, /api/rescan (POST), /api/login (POST), /api/user

3. API Response Format (from %to-json methods)

  • Track JSON keys: id, artist, album_artist, album, year, no (track number), title, bit_rate, duration, url, cover
  • Album JSON keys: id, artist, year, album, original_date, publisher, country, genre, type, status, mb_id, track_count, total_duration, cover
  • Category results: array of {"item": "name", "count": N}
  • NOTE: Track number key is no, not track_number. Album title key is album, not title. Must update ChadMusic.swift CodingKeys!

4. File-Server Auth — FILES ARE UNPROTECTED (when enabled)

  • file-server function does NOT use with-user macro
  • All API routes ARE auth-protected
  • Acceptable for personal server — file URLs are opaque (need API to discover them)
  • Fix in Phase 2: wrap file-server in with-user