Worker architecture
What this layer does
Phase 4 turns skills.snappy.ai into the only ingress to DO Spaces. The Worker authenticates SNAPPY_MASTER_KEY, derives tenant_id, enforces tenant prefixes on every write, and serves the catalog, detail pages, docs, install one-liner, status page, and changelog. Joe machines never hold DO credentials. KV is a 60s edge cache, not the source of truth.
Files involved
~/projects/snappy-skills/src/index.ts— Worker entry, route table,
scheduled handlers.
~/projects/snappy-skills/src/push.ts—POST /_push.~/projects/snappy-skills/src/pull.ts—GET /_pull.~/projects/snappy-skills/src/status.ts—GET /_status.~/projects/snappy-skills/src/install.ts—/install+ per-tenant
invite + /_install?skills= subset.
~/projects/snappy-skills/src/docs.ts— rendersstate/wiki/*.md.~/projects/snappy-skills/src/changelog.ts— JSON + Atom feeds.~/projects/snappy-skills/src/alert.ts—POST /_alertaggregation.~/projects/snappy-skills/src/quorum.ts— scheduled promotion.~/projects/snappy-skills/src/catalog.ts— scheduled rebuild.~/projects/snappy-skills/src/auth.ts— tenant-aware grants.~/projects/snappy-skills/src/do-spaces.ts— outbound S3 SigV4.~/projects/snappy-skills/wrangler.json— KV bindings + scheduled
triggers.
Route table
| Route | Purpose |
|---|---|
GET / | Catalog grid |
GET /<skill> | Per-skill detail |
GET /docs and /docs/<page> | Wiki rendering |
GET /_status | Public health JSON + HTML |
GET /install | Bare bootstrapper |
GET /install/<inviteCode> | Bootstrapper with key baked in |
GET /_install?skills=foo,bar | Subset install |
GET /_search?q= | Catalog search |
POST /_push | Joe ingress, tenant-prefix enforced |
GET /_pull | Manifest diff + stream |
POST /_alert | Failure tally |
GET /changelog.json and /changelog.atom | Feeds |
GET /.well-known/skills/... | Direct file serve |
Scheduled handlers
async scheduled(controller, env, ctx) {
if (controller.cron === "* * * * *") await runQuorumPromotion(env);
if (controller.cron === "*/5 * * * *") { await rebuildCatalog(env); await syncSkoolThreads(env); }
}
Single writer per task. Per-machine push no longer touches _catalog.json; concurrent pushes from different runtimes can't clobber it.
KV cache strategy
KV bindings: SKILLS_STORE, API_KEYS, SETTINGS_STORE, INVITES, ALERTS, STATUS_LOG. SKILLS_STORE caches DO reads with 60s TTL. On miss the Worker fetches DO Spaces directly and re-populates KV. KV is never the source of truth — losing it triggers a re-population on the next request, no data loss.
DO outbound creds
DO_SPACES_KEY and DO_SPACES_SECRET live as Wrangler secrets, read only inside the Worker process. The 24h grace window for rotation reads DO_SPACES_KEY_NEW first, falls back to the old pair. See secrets-rotation.md.
Operational gotchas
- Tenant prefix is server-derived. Never trust a client-supplied
tenant_id; recompute from sha256(SNAPPY_MASTER_KEY).first(12) on every request.
- Cross-tenant write attempts return HTTP 403 with no body.
- Concurrent shared-prefix writes return HTTP 409 with the expected
SHA. Client must re-pull, re-apply, re-push.
- Bundle hygiene: code only, no embedded skill content.
state/lint/worker-bundle-size.ts warns >500 KB, fails >1 MB.
- Public-tier reads (
/,/<skill>,/docs/*) work without a key.
Auth only on _push, _pull, _alert, and tier-gated detail.
How to verify it's working
curl https://skills.snappy.ai/_statusreturns JSON 200.curl -X POST https://skills.snappy.ai/_pushwith no auth → 401.- Cross-tenant write probe → 403.
wrangler tailshows scheduled handler firing every minute.state/lint/worker-serves-canonical.tsand
state/lint/worker-bundle-size.ts exit 0.