podcast manager
1#+TITLE: skypod
2#+AUTHOR: Jonathan Raphaelson
3#+EMAIL: jon@accidental.cc
4
5an offline-first RSS & podcast PWA application
6
7* what is skypod?
8
9- rss/podcast subscription management
10- offline first; cache audio locally, and p2p sync between devices
11- podcast playback with nice features; skip silence, volume
12 correction, etc.
13- fun to hack on
14
15* how does it work?
16
17- PWA stores and displays cached feeds, subscription, and listening
18 history data locally in IndexedDB
19- WebSocket signaling server for WebRTC peering, devices sync cached
20 data and play state using a hybrid logical clock for causal ordering
21- feed proxy server provides a nice API for fetching feeds and
22 extracting metadata
23
24* getting started with devenv
25
26this project uses [[https://devenv.sh][devenv.sh]] for reproducible development environments.
27
28if you have devenv installed:
29
30#+BEGIN_SRC bash
31 $ devenv shell
32 $ npm install
33 $ npm run dev
34#+END_SRC
35
36if you don't have devenv, you can install it from [[https://devenv.sh][devenv.sh]] or just
37use node v24+ directly.
38
39* development
40
41run the pwa+server with:
42
43#+BEGIN_SRC bash
44 $ npm install
45 $ npm run dev # lots of stuff concurrently with wireit
46#+END_SRC
47
48this starts:
49- vite dev server at ~http://127.0.0.1:4000~ (frontend)
50- backend server at ~http://127.0.0.1:4001~ (WebSocket + API)
51- live type-checking and linting in watch mode
52
53** technology stack
54
55*** common
56- typescript with strict mode
57- [[https://github.com/panva/jose][~jose~]] for cross-platform webcrypto and JWT management
58- [[https://zod.dev/][Zod]] describes schema and builds transformation pipelines
59
60*** backend
61- [[https://expressjs.com/][Express]] for HTTP and WebSocket servers
62- [[https://github.com/Level/level][Level]] for persistent realm kv storage
63
64*** frontend
65- [[https://vite.dev/][Vite]] for builds
66- [[https://preactjs.com/][Preact]] for UI
67- [[https://dexie.org/][Dexie]] for IndexedDB storage
68- [[https://github.com/feross/simple-peer][simple-peer]] for WebRTC
69
70*** build & dx
71- typescript for type-checking
72- [[https://github.com/google/wireit][Wireit]] does script dependencies and services
73- [[https://eslint.org][ESLint]] and prettier keep the code nice
74
75** scripts
76
77- ~npm run dev~ :: alias for ~npm run start:dev~
78- ~npm run lint~ :: runs ~eslint~
79- ~npm run types~ :: runs ~tsc~ (no emitting, just typechecking)
80- ~npm run build~ :: build production frontend
81- ~npm run test~ :: runs ~jest~ as a one-off
82- ~npm run start:tests~ :: runs ~jest~ in watch mode
83- ~npm run start:dev~ :: runs BE/FE with live type-checking and linting
84- ~npm run start:prod~ :: builds and runs everything in production mode
85
86** running tests
87
88There's not much here yet, I want to figure it out first.
89
90#+BEGIN_SRC bash
91 $ npm run test # run all tests once
92 $ npm run start:tests # run tests in watch mode
93#+END_SRC
94
95to run a single test file:
96
97#+BEGIN_SRC bash
98 $ npx jest src/path/to/file.spec.ts
99#+END_SRC
100
101** git hooks
102
103pre-commit hook runs type-checking and linting automatically. enable with:
104
105#+BEGIN_SRC bash
106 $ git config core.hooksPath .githooks
107#+END_SRC
108
109* architecture
110
111the codebase is organized into modules with path aliases:
112
113- ~#client/*~ (~src/client/~) - preact frontend application
114- ~#server/*~ (~src/server/~) - node.js express backend
115- ~#common/*~ (~src/common/~) - shared code (protocol, crypto, utilities)
116- ~#realm/*~ (~src/realm/~) - p2p connection and sync protocol
117- ~#skypod/*~ (~src/skypod/~) - domain-specific schemas and actions
118
119** key architectural components
120
121*** p2p synchronization
122
123uses a hybrid logical clock (HLC) for causal ordering of events across
124distributed peers. clients PULL complete action history when catching
125up, and PUSH tailored updates to each peer based on knowledge vectors.
126
127*** offline-first
128
129all user data lives in IndexedDB via dexie. the server is stateless
130regarding user data (only maintains ephemeral realm/peer state).
131
132*** webrtc for p2p
133
134peers communicate directly via WebRTC data channels. the WebSocket
135connection to the server is only used for signaling and as a fallback
136broadcast mechanism.
137
138*** realm system
139
140a realm is a collection of verified identities that can communicate
141securely. realms are not publicly routable; access requires the realm
142id and an invitation from an existing member.
143
144see [[./docs/readme-brainstorm.org]] for detailed architecture thoughts
145and connection flow diagrams.
146
147* contributing
148
149- run ~git config core.hooksPath .githooks~ to install pre-commit
150 hooks for lint/fmt
151- honestly, not really open to contributions right now, come back
152 later.
153
154* license & copyright
155
156copyright (C) 2025 jonathan raphaelson
157
158this program is free software: you can redistribute it and/or modify
159it under the terms of the **affero general public license version 3 or
160later** (AGPLv3+).
161
162see [[./readme-license.txt]] for a copy of the full license.