Frontend
Layer 1 of six (the map) — and where the app is created, so start here. Got the four prerequisites in place? Then run along.
The baseline. veni create gives you a React app that federates into the Shell and is signed in for free — no login code, no second URL. Everything else in this section is a capability you add on top with veni enable.
veni create calendar # UI-only by default — API and database come laterPeople open the Shell and find Calendar waiting there — already signed in, no second login, no separate URL to remember.
Resolving it. Unlike cloning an existing app, there's no cp .env.example .env here — veni create writes ui/.env for you, pointed at a local Shell:
# calendar/ui/.env — written by veni create
VITE_API_URL=http://localhost:3014/api # your API (once you `veni enable api`). Must end in /api
VITE_SHELL_API_URL=http://localhost:3000/api
VITE_SHELL_URL=http://localhost:5173 # Shell UI — the "← Shell" link and 401 redirects
VITE_PORT=4177
VITE_SERVICE_ID=calendar # the slug you registerBuilding against Dev instead? Edit these two by hand
veni register rewrites api/.env for whichever Shell you target — but it never touches ui/.env. Point the Shell lines at Dev yourself, or the UI keeps linking back to a localhost:5173 Shell that isn't running:
VITE_SHELL_API_URL=https://shell-api.dev.venizia.ai/api
VITE_SHELL_URL=https://veni.dev.venizia.aiWe'll build it up simplest-first, starting with the month view. Run the UI — how depends on which Shell you picked:
# The local Shell is itself a Vite dev server, so a dev-mode remote loads fine.
# `veni dev` runs from the app root and starts every service present — here, just the UI.
cd calendar && veni install && veni dev # UI → http://localhost:4177# The Dev Shell is a production build — serve one too. Same port, so the
# registered entry is unchanged. Re-run build after every UI change.
cd calendar/ui && bun install && bun run build && bun run preview # http://localhost:4177Against Dev: serve a build, and use Chrome
veni dev serves a dev remote — it carries an HMR shim and imports out of Vite's on-the-fly module graph, which a production host has no business loading. Build and preview instead; ui/.env is baked in at build time, so set it first.
Open the Dev Shell in Chrome: it's HTTPS loading your http://localhost:4177, which Chrome permits as a trusted origin and other browsers can refuse as mixed content — silently. Full detail in Cloud setup.
Register it so it shows up in the Shell (full options + flags). As a developer you register your own copy — from the CLI, or in the UI via Developer mode → My Apps (the developer role doesn't have admin's "Quick Integrate"):
veni register --shell http://localhost:3000 # uses the PAT you installed withveni register --shell https://shell-api.dev.venizia.aiRun it from the app root — veni register reads the slug and ports from the app's env. No PAT saved? Against a local Shell, add --user <you> to sign in with a password instead; Dev is SSO-only, so there a PAT is the only route. The PAT-based command is self-service, so it mounts at /<your-username>-calendar as a private draft you own (why); an admin login (--user <admin>) gives the unprefixed /calendar. Either way — build your calendar grid in ui/src.
Because it runs inside the Shell, it already knows the signed-in user: federation and the Shell's token broadcast are wired for you. So the calendar can greet them by name — and later show their events — with zero auth code. That's SSO, free.
Module Federation rules — do not break these
Expose exactly ./ShellEntry via federation/shell-entry.tsx, bundle your own React (shared: {}) and integrate through the mount pattern. Do not set manualChunks — it breaks the federated production build.
Done when — your calendar (
/<you>-calendar, or/calendarif an admin registered it) loads inside the Shell and greets you by name: signed in, no second login.
Next: the month view needs events — an authenticated API and a database to hold them. Build the backend →
Stuck? See Common snags, or back to Build a satellite.