Commit Graph
179 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 854869ba53 Coordinate Craft App release polling across the fleet through Redis (#77)
New releases took up to ~30 min to reach \`GET
/v1/craft_apps/release_info\` (ArtCraft Launcher 0.2.0 needed 17–29
min), pods disagreed with each other, and every pod polled GitHub on its
own, which uses up the unauthenticated quota (60/h per IP, and 304s
count) and risks backing off for up to 4 h.

The pods now share their results through Redis. There's no lock:
- **One Redis hash per app** (\`craftApps:releases:app:<key>\`, 7 d
TTL). It holds the latest release, the ETag and \`fetched_ms\`. Before
asking GitHub about an app, a pod reads its entry. If it's fresher than
\`interval − 30 s\` (a clock-skew margin), the pod takes it from Redis
and doesn't call GitHub.
- **Last write wins, guarded by timestamp.** A Lua script never lets an
entry go back in time. A 304 moves only \`fetched_ms\`, and only while
the stored ETag still matches.
- **A soft per-app claim** (60 s, no owner, it just lapses), so a fleet
that ticks every 30 s rarely fetches the same app twice.
- **Backoff shared by the whole fleet**
(\`craftApps:releases:backoff\`), so a rate limit slows everyone down.
Per-app failure retry is 60 s, doubling.
- **The endpoint is unchanged.** It still serves each pod's in-memory
copy. If Redis is down, a pod falls back to polling on its own and warns
at most every 10 minutes.

**The GitHub token is optional.** With
\`CRAFT_APPS_RELEASE_GITHUB_TOKEN\` set, the default interval is 120 s
(min 60). Without it (unset or blank), calls go out unauthenticated with
no Authorization header, at 1800 s (min 300), and startup and polling
are never blocked. \`CRAFT_APPS_RELEASE_COORDINATION_ENABLED=false\`
switches back to the old per-pod polling.

**Tests:** \`cargo test -p storyteller-web --bin storyteller-web craft\`
(49 pass) and \`cargo test -p redis_schema craft\` (4 pass). The Redis
tests passed against a local redis-server with
\`CRAFT_APPS_TEST_REDIS_URL=… -- --include-ignored --skip live_round\`
(51 pass).

**Follow-ups after review:**
- **The poller has its own Redis pool** (2 connections; connects within
3 s; every read and write, including the pool's `PING` check, times out
after 5 s). Before this, it shared the handlers' pool, which has no read
timeout, so a Redis that stopped answering could leave the poller
holding every connection while handlers waited 30 s for one. A test
against a fake Redis that goes silent after the handshake fails without
the timeouts and passes with them.
- **When Redis won't run scripts**, writes fall back to
`WATCH`/`MULTI`/`EXEC`: read and decode the guarding fields, decide, and
write in a transaction that Redis drops if another pod wrote in between.
This switch only happens when Redis refuses the script itself. Being
unreachable, busy, out of memory or read-only doesn't trigger it. It was
tested against a real Redis user denied `@scripting`.
- **New pods load from Redis at startup**, before the startup jitter, so
they serve the fleet's releases right away without asking GitHub.
- **`Cache-Control: no-store` until every app has been fetched**
(`last_fetched` set), so Cloudflare never caches a cold pod's "not
fetched yet" answer. The response body is unchanged.

**Tests:** `cargo test -p storyteller-web --bin storyteller-web craft --
--include-ignored --skip live_round` against a local Redis 8.6: 63 pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:05:06 -04:00
Brandon Thomas c3d72fb321 Generate WebP thumbnails for videos instead of Gif (except: MacOS 10.15)
MacOS Catalina 10.15 released October 7, 2019 can't do gifs in
safari/webkit.
2026-09-28 00:52:07 -04:00
Brandon Thomas 55ab800542 more performant list user files endpoint 2026-09-27 15:02:19 -04:00
Brandon Thomas b6a184bcb0 enum variants 2026-09-26 02:36:24 -07:00
Brandon Thomas d0e1d113fe break out queries 2026-09-26 02:13:23 -07:00
Brandon Thomas 1facb312dc tests 2026-09-26 01:57:16 -07:00
Brandon Thomas 4f1c9134ee WIP SSO OTP bridge 2026-09-25 23:23:00 -07:00
Brandon Thomas 3e6d759fe2 fix thumbnail job latency 2026-09-20 10:12:38 -04:00
Brandon Thomas ef2710fcc8 wip 2026-09-14 15:40:33 -04:00
Brandon Thomas cb1bea9d0a remove the desktop app from the repo 2026-09-12 12:23:46 -04:00
Brandon Thomas 7b88737c45 fix tauri for omni apis (#1941) 2026-09-11 01:03:53 -04:00
Brandon ThomasandHanashi 7fb81500a7 rename CLAUDE.md to AGENTS.md (#1940)
Co-authored-by: Hanashi <shared2@storyteller.ai>
2026-09-10 21:55:17 -04:00
hanashi 44ff9ce338 mcp sessions (#1917) 2026-08-27 13:05:55 -04:00
hanashi c62a269638 testing omni gen (#1875) 2026-08-09 22:54:25 -04:00
hanashi a7a32a3862 minimax worker endpoints (#1855) 2026-08-07 01:24:20 -04:00
hanashi 9020f0aa5b minimax turbo (#1852) 2026-08-06 18:21:28 -04:00
hanashi a473be78bd new endpoint for media files by job token (#1846) 2026-08-04 23:13:30 -04:00
hanashi 36cfba0000 media files - record job token (#1845) 2026-08-04 22:09:08 -04:00
hanashi 60b4148179 web + API for omni gen endpoints (#1836) 2026-08-03 16:22:16 -04:00
hanashi 9737f0ef88 rename client (#1824) 2026-07-31 15:10:30 -04:00
hanashi 17c12836ad seedream 5 plus (#1822) 2026-07-30 18:25:19 -04:00
Brandon Thomas 0003cad179 seedream5 (#1784) 2026-07-13 11:10:21 -04:00
Brandon Thomas 348a603cae folderless endpoint (#1779) 2026-07-12 15:23:58 -04:00
Brandon Thomas 7dffc5fb5c list meshes and splats (#1774) 2026-07-12 09:32:35 +07:00
Brandon Thomas 9cc9d4b9cf Fix cover images (#1773) 2026-07-11 15:32:15 -04:00
Brandon Thomas dc5a4f8dd9 backup for cover images when absent 2026-07-10 17:14:13 -04:00
Brandon Thomas cb6d0fe27c new tags system (replacing the old one) (#1770) 2026-07-10 05:21:49 -04:00
Brandon Thomas 698c330734 remove old tags code (#1767) 2026-07-10 02:31:37 -04:00
Brandon Thomas 1dfb62454a 8 new persistence endpoints, 2 for each type (#1766) 2026-07-10 01:32:23 -04:00
Brandon Thomas 87f35f3e6f Remove endpoints for old Rust / Bevy game engine (#1765) 2026-07-09 23:52:16 -04:00
Brandon Thomas df0a82cbdb triposplat support (#1764) 2026-07-09 21:00:32 -04:00
Brandon Thomas c5071d4f2e list project media files endpoint (#1763) 2026-07-09 20:08:59 -04:00
Brandon Thomas 57c77cbbc2 stop writing Dimensional for Mesh/Splat types (#1762) 2026-07-09 17:59:10 -04:00
Brandon Thomas 98c4fad729 add metadata to project files; remove legacy 3d editor endpoint (#1761) 2026-07-09 17:23:09 -04:00
Brandon Thomas 1d5d63fda1 media files: stop reading and writing deprecated fields and values (#1758) 2026-07-08 21:41:45 -04:00
Brandon Thomas 78a5c4ff87 handle many more mesh models (#1755) 2026-07-08 01:02:37 -04:00
Brandon Thomas 5bda2a6cb4 mesh and splat omni endpoints (#1752) 2026-07-07 19:25:49 -04:00
Brandon Thomas 9cf1b8e066 Add omni audio! router, web, etc. - seed audio, suno, etc. (#1747) 2026-07-07 03:32:16 -04:00
Brandon Thomas cec3b50ae4 characters API support (#1738) 2026-07-06 16:12:00 -04:00
Brandon Thomas 4fbe9f54e0 more cleanup (#1734) 2026-07-04 11:53:00 -04:00
Brandon Thomas e850022fe8 further cleanup (#1733) 2026-07-04 11:43:49 -04:00
Brandon Thomas 1a528bba45 remove deprecated endpoints (#1732) 2026-07-04 10:15:59 -04:00
Brandon Thomas 3cc02ae7bf admin dashboard spend (#1729) 2026-07-03 04:52:28 -04:00
Brandon Thomas 2c943b61ab improve media files user list query performance (#1728) 2026-07-03 04:10:13 -04:00
Brandon Thomas c0ca274168 cleanup dead handlers (#1726) 2026-07-03 03:08:59 -04:00
Brandon Thomas 3a2e3b30ec debug logs #2 (#1723) 2026-07-02 19:36:48 -04:00
Brandon Thomas 82c0a7a4d9 debug logs: new field, new queries, new APIs, new dashbord UX (#1722) 2026-07-02 16:43:00 -04:00
Brandon Thomas 35add74498 payment informatics 3 (#1706) 2026-06-30 08:19:52 -04:00
Brandon Thomas d99da56df0 payment informatics #2 (#1705) 2026-06-29 21:21:26 -04:00
Brandon Thomas 3e4f998599 payments informatics (part 1) (#1704) 2026-06-29 17:49:28 -04:00