1. System shape
This is a pnpm and Vite+ monorepo. apps/web is both the browser application and the backend; apps/mobile and apps/cli are authenticated consumers of its HTTP API; MCP clients use the same backend through a stateless Streamable HTTP endpoint. packages/shared carries the primary analytics response types and formatting helpers.
| Layer | What lives here | Primary responsibility |
|---|---|---|
| Clients | SvelteKit web, Expo mobile, Node CLI, MCP clients | Present analytics and initiate authorization |
| Cloudflare edge | SvelteKit Worker, server routes, remote functions, static assets | Authenticate requests, validate inputs, and expose product interfaces |
| Application services | connections.ts, dashboard.ts, MCP tool adapter | Resolve the active channel and orchestrate use cases |
| State and policy | Five SQLite Durable Object classes | Own authorization, credentials, tokens, preferences, and cached channel data |
| External systems | Clerk and three YouTube APIs | Identity, channel metadata, analytics, comments, and reach reports |
The architectural sentence
A request proves a Clerk session or a channel-pinned machine credential, asks the organization catalog which Google grant may access the channel, obtains a short-lived token from that grant object, and then asks the channel object for cached or fresh analytics.
2. Request walkthrough
Browser dashboard
hooks.server.tsauthenticates the request with Clerk and records user, session, organization, and admin status in SvelteKit locals.- The page calls SvelteKit remote queries in
data.remote.ts. Those queries delegate to the dashboard application service instead of talking to Google directly. resolveEffectiveChannelreads the organization catalog fromOrgDOand the browser-session preference fromUserDO. A preference is scoped by session and organization, so separate browser sessions can select different channels.resolveAuthorizedChannelasksOrgDOfor active grant sources, then asks the chosenGoogleGrantDOfor a usable access token.dashboard.tscalls the per-channelChannelDO. Fresh cache entries return immediately; stale or missing entries trigger YouTube API work.- The response can contain both data and an error. That is intentional: known-good stale data remains visible during a Google outage.
Connecting Google
- An organization admin starts OAuth. The route creates encrypted state, PKCE material, a same-session nonce cookie, and an exact callback URL.
- The callback verifies user, session, organization, nonce, and return path before exchanging the authorization code.
- The app fetches the Google profile and all channels owned by that Google or Brand Account identity.
- A new
GoogleGrantDOreceives encrypted token material;OrgDOupdates the grant-to-channel catalog; each discoveredChannelDOactivates that organization and grant as a source. - Reconnecting the same Google subject supersedes older active grants and deactivates their channel sources.
CLI, mobile, and MCP
Machine credentials use the routable dyt_v2 envelope. The locator identifies the owning organization but grants no authority: OrgDO stores only a hash of the full credential and rechecks kind, revocation, scope, and current channel membership on every request.
- CLI: starts a ten-minute device flow, opens an approval page, polls, and stores the one-time returned credential in a mode-600 config file.
- Mobile: uses a browser authorization handoff and returns a channel-pinned credential through an allowed deep-link scheme; the app stores it in SecureStore.
- MCP: admins mint and revoke channel-pinned keys in
/mcp/setup. The endpoint is stateless JSON-RPC over POST, with independent IP and locator rate limits.
3. State ownership
Durable Object identity is part of the authorization model, not just a storage choice. Each object validates that its stable name matches the entity it claims to represent.
| Object | Identity key | Owns | Important invariant |
|---|---|---|---|
| UserDO | Clerk user | Per-session, per-organization selected channel | Preference expires and never becomes a global user setting |
| OrgDO | Clerk organization | Grant catalog, channels, credentials, audit events | This is the authorization boundary for every channel and machine credential |
| GoogleGrantDO | Organization + grant | Encrypted access/refresh tokens and discovered channel ids | Long-lived Google secrets do not enter ChannelDO |
| ChannelDO | YouTube channel | Metadata, videos, reports, comments, reach, refresh coordination | Every RPC proves an active organization/grant source before data access |
| DeviceFlowDO | Ephemeral flow id | Approval state and encrypted one-time credential handoff | A credential can be claimed once and the object cleans itself up by alarm |
The channel data is canonical and shareable, while channel_orgs and refresh_sources record which organization/grant pairs may use it. The caller supplies only a short-lived access token after OrgDO and GoogleGrantDO have approved the path.
4. Data lifecycle and freshness
- Read-through cache: dashboard, summary, top-video, video, recent-video, and chronological comment snapshots have a 15-minute freshness window.
- Stale-good behavior: an upstream failure returns cached data with an in-band warning whenever possible.
- Deduplication: concurrent requests for the same report share one in-flight promise inside the object instance.
- Generation ordering: refresh generations prevent a slower, older request from overwriting newer data.
- Manual refresh: a 30-second durable cooldown is recorded before upstream I/O; complete snapshots are promoted atomically.
- Background refresh: alarms refresh full-history data about every 45 minutes with jitter, and stop after repeated failures until an interactive request re-arms the loop.
- Reach: YouTube Reporting jobs and CSV reports are ingested separately. Reach history is durable, can lag, and is explicitly not presented as YouTube Studio parity.
ChannelDO is roughly 1,700 lines because it combines its RPC surface, authorization assertions, cache policy, refresh coordination, Google fan-out, persistence, reach ingestion, comments, alarms, and serialization guards. The object boundary is good; the implementation boundary is ready to be split.
5. Product surfaces
| Surface | Authentication | Primary capabilities | Current edge |
|---|---|---|---|
| Web | Clerk session + organization | Connection management, channel switching, dashboard, reach, video analytics, MCP key setup | Full control plane |
| Expo mobile | Admin-approved mobile credential in SecureStore | Dashboard, date ranges, video analytics, native sharing | Focused native reader |
| Node CLI | Device-flow credential in local config | Dashboard and video reports, human or JSON output | Automation-friendly |
| MCP | Admin-minted, pinned MCP key | Six read-only analytics and comment tools | Agent interface |
The web app uses shared application services directly through SvelteKit remote functions. Mobile and CLI use REST routes. MCP adapts the same dashboard service into tool results. This convergence is a real strength: differences between surfaces are mostly transport and presentation, not separate data implementations.
6. Development and delivery
Local topology
Production exports all five Durable Object classes from the SvelteKit Worker. Local Vite development cannot host those classes through getPlatformProxy, so the repo starts a second Wrangler worker named byts-do on port 8788 and points the web bindings at it through Wrangler's dev registry. The repo scripts own startup, port checks, protocol choice, and cleanup.
New worktrees run pnpm setup:worktree to copy ignored local configuration safely, install dependencies, and verify generated bindings. Vite+ orchestrates formatting, checking, linting, and tests across packages, while the Expo app keeps its own Jest, ESLint, Metro, and TypeScript compatibility tooling.
Deployment topology
- A pull request runs install, check, lint, and test on Depot infrastructure.
- A push to
mainrepeats the quality gate. - The pipeline builds and deploys the staging Worker, then smoke-checks its homepage.
- Only after staging responds does it build and deploy the production Worker and smoke-check the custom domain.
- Production runtime
- Cloudflare Workers + Static Assets + SQLite Durable Objects
- Production domain
byts.davis7.space- Staging runtime
- Separate
byts-stagingWorker and object namespaces - Observability
- Worker logs sampled at 100%; traces sampled at 10%
7. Recommended polish agenda
The sequence below favors visible coherence first, then reduces architectural friction without disturbing the state model.
| Priority | Workstream | Why now | Concrete outcome |
|---|---|---|---|
| 1 | Choose one product identity | The repo simultaneously says Better YouTube Studio, byts, and Deep YT Data. | One naming system for app titles, package/bin names, OAuth copy, mobile metadata, MCP server info, and docs. |
| 2 | Make the API contract executable | Mobile imports shared TypeScript types, but the CLI accepts unknown and reparses responses ad hoc. | Shared Valibot schemas plus a small typed API client used by mobile and CLI; transport responses validated at runtime. |
| 3 | Decompose ChannelDO internally | The correct object boundary now hides too many implementation concerns in one file. | Keep one exported Durable Object facade, but extract cache repository, refresh coordinator, reach importer, comments service, and RPC result mappers. |
| 4 | Define cross-surface parity | Web, mobile, CLI, and MCP expose different subsets of reach, retention, comments, and credential management. | A deliberate capability matrix that labels each gap as product choice, backlog, or unsupported. |
| 5 | Remove config drift paths | wrangler.dev.jsonc must be manually mirrored from production, and client base URLs are declared in more than one place. | Generate or validate the dev config from a canonical binding manifest and centralize public origins per environment. |
| 6 | Make freshness observable | The code has thoughtful cache, retry, and alarm behavior but operators cannot see the health model at a glance. | Structured counters and dashboards for cache hits, stale serves, refresh age, Google quota/error classes, alarm failures, reach lag, and credential use. |
| 7 | Strengthen deployment smoke tests | The pipeline currently proves that homepages respond, not that critical public contracts are wired. | Unauthenticated checks for /api/viewer, MCP method handling, assets, headers, and environment-specific Durable Object bindings. |
| 8 | Unify error and freshness language | Stale-good behavior is excellent but each surface explains it differently. | One shared vocabulary for fresh, cached, stale-with-warning, rebuilding, disconnected, unauthorized, and revoked states. |
Start with product identity + shared response schemas + a capability matrix. That work makes every later UI and backend polish decision easier, produces visible coherence quickly, and does not require a risky storage migration.
What to preserve
- The separation between organization authority, Google secret custody, and channel data.
- Channel-pinned machine credentials and reauthorization on every request.
- Stale-good responses with explicit warnings.
- Atomic promotion and generation ordering for refreshes.
- The shared dashboard application service used by web, REST, and MCP.
- Staging-before-production deployment and worktree-safe local setup.
8. Evidence map
This snapshot describes main at commit cfea7eb. The fastest files for re-orienting after future changes are:
README.md, rootpackage.json, andpnpm-workspace.yamlfor system intent and workspace orchestration.apps/web/wrangler.jsonc,wrangler.dev.jsonc, andwrangler.channel-do.jsoncfor production and local topology.apps/web/src/hooks.server.ts,lib/server/connections.ts, andlib/server/dashboard.tsfor the request path.apps/web/src/lib/server/*-do/for state ownership and RPC contracts.apps/web/src/routes/data.remote.ts,routes/api/, androutes/mcp/for the three server-facing transports.packages/shared/src/types.ts,apps/mobile/lib/api.ts, andapps/cli/src/api.tsfor client contracts..depot/workflows/ci.ymland.depot/workflows/deploy.ymlfor delivery.