The MachZero site (PESOS X Silicon hackathon, 31 Oct – 1 Nov 2026) built on
eventKit. Its / page is the MachZero hero page, and behind it the app does
everything the hosted eventKit front end does, scoped to this one event:
| Path | What |
|---|---|
/ |
Hero page with the Mach meter, schedule and "Register your team" |
/join |
The event's participant link: start a team (or log in / sign up first) |
/i/:token |
Any other invite link (volunteer, judge, admin) |
/app |
The dashboard: team and registration for participants, scanner, admin section |
It talks only to the public eventKit API, with this event's own API key. It is an installable web app (PWA); the volunteer scanner lives here too.
The event is created and run in eventKit; this site needs three values from it:
- Event ID: the MachZero event's id.
- API key: an admin creates one for the event (
POST /v1/events/{eventId}/api-keys, see eventkit-api'sdocs/usage.md). The full key is shown once. It only says which event's app is calling; permissions still come from each user's role. - Participant link token: from the event's participant invite link
(Admin → Links), the part after
/i/.
Add this site's origin to the API's CORS_ORIGINS (or serve it under the same
domain as the API, with the API at /api).
Start the API first (see eventkit-api's README), then:
cp .env.example .env # fill in VITE_EVENT_ID, VITE_API_KEY, VITE_PARTICIPANT_TOKEN
npm install
npm run dev # http://localhost:5174 (proxies /api to localhost:4000)Types come from the API's OpenAPI spec, never written by hand:
npm run gen:api # reads ../eventkit-api/openapi.json by default
OPENAPI_SPEC=/path/to/openapi.json npm run gen:apisrc/api/client.ts wraps openapi-fetch: every call is typed from the spec and
carries the Bearer token and X-Api-Key.
| Script | What it does |
|---|---|
npm run dev |
Dev server |
npm test / npm run test:coverage |
Vitest + Testing Library + MSW (90% gate) |
npm run lint / npm run typecheck |
oxlint, tsc |
npm run build |
Production build into dist/ |
The image serves dist/ with Caddy. Configuration is read when the container
starts (written to /config.js), so one image works anywhere:
| Variable | Default | Meaning |
|---|---|---|
API_URL |
/api |
Where the API is |
API_KEY |
The MachZero event's API key | |
EVENT_ID |
The MachZero event's id | |
PARTICIPANT_TOKEN |
Token of the event's participant link |
Without PARTICIPANT_TOKEN, /join shows "Page not found".
- Hand-written CSS in
src/theme.css, taken from the MachZero page: Bricolage Grotesque, ink on pale blue-grey, one hot orange accent, light and dark modes (prefers-color-scheme, ordata-themeon<html>). No UI kit. - Components use semantic classes (
.card,.btn,.badge--ok, …);cx()insrc/lib/utils.tsjoins them. - The Mach meter (
src/features/landing/mach.ts) is the page's canvas script as a module: it starts when the hero mounts and stops when it unmounts.
- Times are stored in UTC and always shown in IST (
src/lib/datetime.ts). - The service worker caches only the app shell. API responses are never cached, because scanning is online-only.
- Creating events is left to eventKit; this site serves one event.