Tunrooms free architecture
AI search widget on tunrooms.com. Infrastructure runs on free plans. The client pays OpenAI on its own account. Blue shows the request path. Orange shows the data path.
Legend
- Request path · numbered circles
- Data path · lettered squares
- Dashed: later, after the client API exists
- Shaded area: owner / plan
What is free / who pays
| Service | Cost |
|---|---|
| Cloudflare Worker + Cron | Free |
| Durable Object (SQLite) | Free |
| R2 bucket | Free |
GitHub Actions, org TunroomsAI | Free |
| OpenAI (intent + embeddings) | Client pays |
Search pipeline: CPU budget per chat
Runs inside the Worker on every chat (request steps 2–4). Bars use one 0–3 ms scale. The light part shows the top of a range. Network wait is not CPU.
- 1Body + state
state.tsvalidation0.5 ms - 2Admissionone DO call · network~0 ms
- 3IntentOpenAI fetch + validation1 ms
- 4Snapshotcold load, single-flight2–3 ms cold · 0 warm
- 5Hard filterscity, type, guests, blocked days0.5 ms
- 6Quotes + budgetweekend rule, overrides1–2 ms
- 7Embed + cosine619 × 15361.5 ms
- 8Top 12 + JSONtemplates0.5 ms
Total target < 8 msp99, cold and warm, 90-night searches
0
8 ms target
10 ms free limit
Request path
- 1Widget sends
POST /api/chatto the Worker. - 2One Durable Object call checks the per-IP limit and the app AI budget.
- 3OpenAI returns the intent and the query embedding.
- 4The Worker searches the snapshot in memory: filters, quotes, cosine, top 12.
- 5Later: live check of the shown cards on
/v1/availability/check. - 6Cards go back to the widget.
Data path
- ADaily GitHub Actions job crawls the catalog (37 pages × 3.5 MB).
- BIt embeds only the changed listings with OpenAI.
- CIt writes immutable
snapshots/<rev>/, then swapscurrent.json. - DWorker Cron, every minute: ~45 calendars, ≤4 at a time, then an If-Match write of
calendars.bin. All ~660 listings refresh about every 15 min. - EThe Worker loads the snapshot from R2 into memory.