Shell
The host app — authentication, routing, the app registry, and the launchpad every remote app federates into. It owns identity for the whole platform: users, organizations, roles, and the auth providers people sign in with. Repo veni-shell · Make-driven, with a Bun API and a Vite UI.
What the Shell does
Four responsibilities, and everything else in this section serves one of them:
- Identity — it owns the users, organizations, and roles for the whole platform, in
shell_db. A satellite has its own users table, provisioned from the Shell on first sign-in — it never readsshell_db. - Sign-in — people authenticate here, against whatever providers you configure. The Shell is provider-agnostic; the app never sees the provider.
- Token issuance — on sign-in the Shell mints an RS256 JWT and publishes its public keys at the JWKS endpoint. A satellite exchanges that token for its own and verifies it against those keys — no shared secret.
- The launchpad — it keeps the app registry and federates each satellite's UI in at
/<slug>, so signed-in users move between apps without re-authenticating.
So the mental model is a hub: the Shell holds identity and hands out verifiable tokens; satellites are self-contained systems that federate into its UI and trust its tokens, but keep their own database and deployment. The platform architecture has the full picture; Build a satellite is the other side of this handshake.
The moving parts
| What it is | Runs at | |
|---|---|---|
| API | Auth, routing, the app registry, and RS256 token issuance | :3000 |
| UI | The launchpad; every remote federates into it | :5173 |
| Database | shell_db — users, orgs, roles, providers, the registry | :15433 |
Architecture
The Shell API is a Bun + Hono + Ignis app — the same framework a satellite uses: ShellApplication extends BaseApplication wires everything in api/src/application.ts. Every caller hits one request pipeline, and satellites also verify its keys and consume its events:
The Shell owns its data — shell_db, its Redis cache, its MinIO bucket. In local dev these run from the Shell's own compose; in production they're shared platform infrastructure each app connects to (the SCS split). Beside them sit two shared, self-hosted services: Keycloak (SSO) and the event bus on Redis Streams, which the Shell publishes to and every app consumes into a local read model. Google, SMTP and Stripe are external third-party, all optional. Satellites also verify the Shell's JWKS keys.
The codebase
Two trees — a Bun API and a Vite UI.
API — api/src/
The request path, then the cross-cutting pieces:
api/src/
├─ application.ts # ShellApplication — the composition root
├─ controllers/ # HTTP in, @authorize-gated (one per REST group)
├─ services/ # business logic (~50): auth, RBAC, orgs, billing
│ └─ authz/ # Casbin adapter — the Shell's own RBAC
├─ repositories/ # data access over Drizzle
├─ models/ # the Drizzle schema → Postgres
├─ strategies/ # request auth: jwt (Shell tokens) · pat
├─ grpc/ # Connect services: auth-gateway · rbac · quota
├─ events/ # Redis Streams publisher: shell:stream:*
└─ workers/ # background jobs (e.g. rag-indexer)UI — ui/src/
A React app. Beyond its own screens it hosts each satellite's UI — the Federation section walks that:
ui/src/
├─ main.tsx → bootstrap.tsx # deferred entry (Module Federation inits first)
├─ federation/ # host — loadRemote mounts ./ShellEntry at /<slug>
├─ pages/ # route views: sign-in, launchpad, admin, org settings
├─ layout/ # the shell chrome: nav rail, launchpad, top bar
├─ components/ # shared UI primitives and widgets
├─ hooks/ # data hooks over the API
├─ api/ · services/ # the Shell API client
├─ context/ · providers/ # React context: auth/session, theme
└─ themes/ · i18n/ # theming and localisationWhere to start reading
Read these before you extend the Shell:
- Sign-in —
api/src/services/auth.service.tsand the provider services resolve a user and mint the RS256 token. - Token exchange —
api/src/grpc/auth-gatewaybrokers a Shell token into a satellite's service token. - Federation —
ui/src/federation/loadRemote.tsmounts a satellite's./ShellEntryat/<slug>.
Identity, RBAC, orgs and the registry all hang off the first two.
Federation
The UI's defining job is to render each satellite's own UI inside the Shell — so a signed-in user moves between apps with no reload — using Module Federation. The Shell is the host; each satellite is a remote exposing one entry, ./ShellEntry, which the Shell discovers from the registry, loads, and mounts at /<slug>. How federation works → walks the full runtime path, the ShellEntry contract, and the gotchas.
Next
- Run the Shell → — clone, start its services, and sign in.
- Configure → — turn on sign-in providers, storage, email, tracing and AI.
- Database → — the
shell_dbschema behind identity, RBAC and the registry. - API → — the endpoints, grouped, with the platform-critical ones called out.
- Federation → — how the launchpad loads and mounts each satellite's UI.
- Build a satellite → — build an app that federates into this Shell.