Integrate with Shell
Every satellite in the VENI-AI ecosystem is a Self-Contained System (SCS). To provide a unified experience, satellites must integrate with the central Shell via Module Federation and the SSO Handshake.
🏗️ 1. Expose the Entry Component
The Shell expects every remote app to expose a component named ShellEntry. This component is the root of your satellite's UI.
// apps/your-app/ui/src/federation/shell-entry.tsx
import { BrowserRouter } from 'react-router-dom';
import App from '../App';
export default function ShellEntry() {
// Detect if running under a subpath or root
const basename = window.location.pathname.startsWith('/your-app')
? '/your-app'
: '/';
return (
<BrowserRouter basename={basename}>
<App />
</BrowserRouter>
);
}🧩 2. Configure Module Federation
In your vite.config.ts, you must use the federation plugin to expose your entry point and share critical libraries.
// apps/your-app/ui/vite.config.ts
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
plugins: [
federation({
name: "your_app",
filename: "remoteEntry.js",
exposes: {
"./ShellEntry": "./src/federation/shell-entry.tsx",
},
shared: {
// IMPORTANT: Satellites must bundle their own React.
// Never set 'singleton: true, import: false' for React
// unless you are certain the Shell provides it.
react: {},
"react-dom": {},
"react-router-dom": {},
},
}),
],
build: {
target: "esnext",
minify: false,
cssCodeSplit: false,
},
});🛡️ 3. The Identity Handshake
Satellites do not have their own login pages. They rely on the Shell's SSO session.
A. Broadcast Listener
The Shell broadcasts its JWT to all open tabs via a BroadcastChannel.
// apps/your-app/ui/src/federation/token-broadcast.ts
const authChannel = new BroadcastChannel('veni-ai-auth-channel');
authChannel.onmessage = (event) => {
if (event.data?.token) {
// 1. Receive Shell JWT
const shellToken = event.data.token;
// 2. Trigger token exchange
exchangeToken(shellToken);
}
};B. Token Exchange
The satellite's UI sends the Shell JWT to its own API to get a service-scoped token.
// apps/your-app/ui/src/federation/token-exchange.ts
export async function exchangeToken(shellToken: string) {
const response = await fetch(`${API_BASE}/auth/exchange`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: shellToken }),
});
if (response.ok) {
const { serviceToken } = await response.json();
localStorage.setItem('your_app_service_jwt', serviceToken);
}
}📡 4. Backend Verification
The satellite's API verifies the incoming Shell JWT using the Shell's Public Keys (JWKS).
// apps/your-app/api/src/services/auth.service.ts
const JWKS_URL = 'http://shell-api:3000/api/.well-known/jwks.json';
export async function verifyShellToken(token: string) {
// 1. Fetch JWKS from Shell
// 2. Verify RS256 signature
// 3. Extract sub (userId) and organizationId
// 4. Issue new JWT for this satellite
}📝 5. Registration
Finally, register your satellite in the Shell database so it appears in the launcher.
veni register your-app \
--ui-url http://localhost:417x \
--api-url http://localhost:301x/apiCLI Convenience
The veni CLI handles ports and registration automatically when you use veni create app.