Kabina
A press-kit platform for DJs in Latin America: presskit, booking inbox, visit stats and a mascot called Kabi.

DJs send their press kit as a PDF or a Drive folder that goes out of date the week after. Promoters ask for the same bio, photos, rider and dates over WhatsApp. Kabina gives each DJ one live page, a place to receive booking offers and a way to see who looked. It had to run on free plans, with hard caps, until it has paying users.
Three decisions that shaped it.
- 01
The design handoff is compiled, not retyped
Why. The designs came as 16 HTML prototypes. A small porter (tools/dc2svelte) splits each one into Svelte components, driven by a spec per screen. The dynamic parts live in a view model next to them, so a design change is a regeneration, not a manual diff.
Trade-off. Generated files can't be edited by hand. Fixes go into replace rules in the spec, which is slower for one-off tweaks. Complex new views, like the booking inbox, are written by hand outside the generated folders.
- 02
One build, two backends
Why. With DEMO=1 the same SvelteKit app compiles for Cloudflare Workers and answers API calls from local handlers with sample data. Each visitor gets a session in D1 that expires after three days. People can try the full panel at demo.kabina.online without an account and without touching production.
Trade-off. Every new endpoint needs a demo handler too, or the demo returns an empty response. That is a second implementation to keep in step with the Go API.
- 03
Free tiers with caps enforced in the database
Why. Kabi's AI and the poster generator run on the free Workers AI quota. Each request reserves its cost first with one atomic upsert in Postgres. If the day's cap would be passed, no row comes back and the call never goes out.
Trade-off. The cost is an estimate before the call and is corrected after. Users can hit a daily limit that a paid plan would not have.




Kabina
A press-kit platform for DJs in Latin America: presskit, booking inbox, visit stats and a mascot called Kabi.
- Role
- Product, design implementation and full-stack development
- Stack
- SvelteKit (Svelte 5), TypeScript, Go (chi + Huma), Postgres + sqlc, River, Cloudflare Workers + D1, Clerk

DJs send their press kit as a PDF or a Drive folder that goes out of date the week after. Promoters ask for the same bio, photos, rider and dates over WhatsApp. Kabina gives each DJ one live page, a place to receive booking offers and a way to see who looked. It had to run on free plans, with hard caps, until it has paying users.
Three decisions that shaped it.
The design handoff is compiled, not retyped
Why. The designs came as 16 HTML prototypes. A small porter (tools/dc2svelte) splits each one into Svelte components, driven by a spec per screen. The dynamic parts live in a view model next to them, so a design change is a regeneration, not a manual diff.
Trade-off. Generated files can't be edited by hand. Fixes go into replace rules in the spec, which is slower for one-off tweaks. Complex new views, like the booking inbox, are written by hand outside the generated folders.
One build, two backends
Why. With DEMO=1 the same SvelteKit app compiles for Cloudflare Workers and answers API calls from local handlers with sample data. Each visitor gets a session in D1 that expires after three days. People can try the full panel at demo.kabina.online without an account and without touching production.
Trade-off. Every new endpoint needs a demo handler too, or the demo returns an empty response. That is a second implementation to keep in step with the Go API.
Free tiers with caps enforced in the database
Why. Kabi's AI and the poster generator run on the free Workers AI quota. Each request reserves its cost first with one atomic upsert in Postgres. If the day's cap would be passed, no row comes back and the call never goes out.
Trade-off. The cost is an estimate before the call and is corrected after. Users can hit a daily limit that a paid plan would not have.



