Getting Started
End-to-end tutorial from app registration to loading a federated remote in the Shell host.
Audience: Shell host + federated remote developers
Time: ~1–2 hours first run (excluding Control Plane stack setup)
This walkthrough takes you from zero to a published federated app loaded by the Shell host, calling Control Plane APIs with Keycloak auth.
What you will build
Portal (register app + publish remoteEntry.js)
↓
Control Plane (manifest + bootstrap API)
↓
Shell host (loads remote via Module Federation)
↓
Federated remote (your React pages + optional API calls)Prerequisites
Infrastructure (usually already running for your team)
| Component | Default URL | Notes |
|---|---|---|
| Control Plane API | http://localhost:8088 | make compose-up in groundfloor-client-portal |
| Customer Portal | http://localhost:3000 | npm run dev:customer |
| Keycloak | https://dev-auth.groundfloor.co | Platform realm; see .env.example |
| Dataplane | http://localhost:8080 | Sibling repo groundfloor-dataplane-oss |
| MinIO (files / publish) | http://localhost:9000 | Phase 2 deps compose profile |
Optional for LLM/secrets locally: Infisical + LiteLLM — see Phase 2 deps runbook.
Repos
| Repo | Role |
|---|---|
groundfloor-client-portal | Control Plane + Portal (register/publish) |
groundfloor-v2.5 | Shell host + starter-kit remote template |
Access
- Keycloak user with access to a workspace (member with at least
read) - Workspace UUID (from Portal URL:
/workspaces/{uuid}/…)
Step 1 — Register the app (Portal)
- Sign in to the Customer Portal with Keycloak.
- Open a workspace → Apps → Register app.
- Set:
- Name: e.g.
My Federated App - Slug: e.g.
my-federated-app(URL-safe, unique per workspace) - Kind: Shell (federated)
- Name: e.g.
- Paste a manifest (minimum viable):
{
"version": "1.0",
"appId": "my-federated-app",
"name": "My Federated App",
"routes": [
{ "path": "home", "title": "Home", "icon": "Home", "layout": [] }
],
"theme": {
"primaryColor": "#2563EB",
"accentColor": "#7C3AED",
"mode": "light"
},
"remoteUrl": ""
}- Save. Note app id (UUID) and slug on the app detail page.
See 09-manifest-and-routes.md for field details.
Step 2 — Build the federated remote (Shell starter-kit)
In groundfloor-v2.5/starter-kit (or your fork):
pnpm install # or npm install- Set
appIdand routes ingroundfloor.manifest.jsonto match Portal (sameappIdas manifest). - Implement pages under
src/matchingroutes[].path. - Build:
pnpm run build
# Output: dist/assets/remoteEntry.js (path may vary — confirm in build output)- For local Shell dev without publishing, run the remote dev server if your template provides one, and set
SHELL_REMOTE_DEV_URLon the Shell host.
Step 3 — Configure the Shell host
Set environment variables (.env.local or deploy config):
CONTROLPLANE_URL=http://localhost:8088
SHELL_WORKSPACE_ID=<workspace-uuid-from-portal>
SHELL_REMOTE_DEV_URL=http://localhost:3002/remoteEntry.js # optional until publishedKeycloak (Shell host browser client):
NEXT_PUBLIC_KEYCLOAK_URL=https://dev-auth.groundfloor.co
NEXT_PUBLIC_KEYCLOAK_REALM=groundfloor_dev
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=groundfloor-portal # or dedicated Shell client
NEXT_PUBLIC_API_URL=http://localhost:8088Ensure the Shell client's id is listed in Control Plane KEYCLOAK_AUDIENCE.
Wire bootstrap on layout load — see 02-shell-bootstrap.md.
Step 4 — Publish the remote (Portal)
- Portal → workspace → your app → Releases / Publish build.
- Upload
remoteEntry.jsor a.zipwithremoteEntry.jsat the zip root. - Wait for finalize; confirm build number and remote URL on app detail.
Verify bootstrap (no auth):
export CONTROLPLANE_URL=http://localhost:8088
export WORKSPACE_ID=<uuid>
export APP_SLUG=my-federated-app
curl -s "${CONTROLPLANE_URL}/v1/public/workspaces/${WORKSPACE_ID}/apps/by-slug?slug=${APP_SLUG}" \
| jq '{slug, remoteUrl: .manifest.remoteUrl, build: .release.build_number}'manifest.remoteUrl must be non-empty after publish.
Step 5 — Load in Shell
- Start Shell host dev server.
- Open
/apps/my-federated-app/home(or your Shell's route convention). - Shell should:
- Fetch bootstrap JSON
- Load
remoteEntry.jsfrommanifest.remoteUrl - Render your federated page inside the Shell chrome
If unpublished, Shell falls back to SHELL_REMOTE_DEV_URL.
Step 6 — Call Control Plane from your remote (optional)
- Obtain Keycloak token from Shell auth context — 03-authentication.md.
- Configure
@groundfloor/api-client:
import { configureControlPlaneClient, setControlPlaneAuthProvider } from "@groundfloor/api-client";
configureControlPlaneClient({ baseURL: process.env.NEXT_PUBLIC_API_URL });
setControlPlaneAuthProvider(async () => {
await keycloak.updateToken(30);
return keycloak.token ?? null;
});- Try a read-only call, e.g. list vault collections or secrets keys:
// GET /v1/workspaces/{id}/vault/collectionsFor LLM: mint virtual key (admin) → store in Secrets → call LiteLLM — 07-llm-gateway.md.
Checklist
| Step | Done? |
|---|---|
App registered (shell_federated) | ☐ |
Manifest validates (routes, theme, appId) | ☐ |
Remote builds to remoteEntry.js | ☐ |
| Published at least once | ☐ |
Bootstrap curl returns remoteUrl | ☐ |
Shell env: CONTROLPLANE_URL, SHELL_WORKSPACE_ID | ☐ |
| Shell loads federated route | ☐ |
| Keycloak token reaches Control Plane APIs (optional) | ☐ |
Next steps
| Goal | Document |
|---|---|
| Bootstrap details | 02-shell-bootstrap.md |
| Auth wiring | 03-authentication.md |
| Customer data | 04-data-vault.md |
| File uploads | 05-files.md |
| Secrets / LLM | 06-secrets.md, 07-llm-gateway.md |
| curl recipes | 11-local-dev-recipes.md |
| When things break | 12-troubleshooting.md |
Control Plane dev stack (maintainers)
If you need to run Control Plane itself from source:
cd groundfloor-client-portal
make install && npm install && npm run codegen
docker compose -f deploy/docker-compose.phase2-deps.yml --profile all up -d
make e2e-up
make compose-up
npm run dev:customer
curl http://localhost:8088/healthSee repo README.md for full details.