Appearance
Manifest Contract
The manifest is the single source of truth for how the host treats your feature.
Authoring vs generated fields
This is the most important distinction:
- Source manifest (
examples/feature-*/manifest.json) is what teams edit. - Published manifest (
artifacts/feature-*/<version>/manifest.json) is what the host loads.
With the default publish scripts, you usually do not need to maintain every runtime field manually.
| Field | Source manifest (examples/*) | Published manifest (artifacts/*) | Notes |
|---|---|---|---|
id | Required (manual) | Required | Team-owned identifier |
version | Required (manual) | Required | Must match release version |
displayName | Required (manual) | Required | UI label |
featureType | Optional (manual) | Required (normalized) | domain (default) or facet (application/module aliases accepted) |
mount.pathPrefix | Required for domain; optional for facet | Same | Mount/routing base |
mount.domElementId | Required for domain; optional for facet | Same | Root DOM mount id |
channel | Optional (manual) | Required (normalized) | Defaults to stable |
requirements.* | Optional (manual) | Optional | authRequired and minHostVersion |
runtime.permissions.* | Optional (manual) | Optional | Host capability scoping |
visibility | Optional (manual) | Optional | Metadata only; host does not enforce |
serverFunctions.module/endpoint | Optional (manual) | Optional | Needed only if feature exposes RPC |
routes | Seed/manual | Required for domain; optional for facet | Default publish scripts derive from router source |
module.mountExport/module.unmountExport | Optional/manual | Optional | Module loader export names (defaults: mount/unmount) |
serverFunctions.exports | Seed/manual | Required when serverFunctions present | Default publish scripts derive from src/actions.server.js |
shared | Optional/manual | Optional | Default publish scripts derive from package deps (React packages) |
entrypoints.js/css | Auto | Required (js) / optional (css) | Built asset paths |
integrity | Auto | Required in runtime | SRI hashes for entrypoint assets |
signature | Auto | Required in runtime | Ed25519 manifest signature |
metadata | Auto | Optional | Build timestamp/git SHA |
Do teams need to list every route manually?
Not with the default setup. The example publish scripts parse route definitions from src/App.jsx and write manifest.routes during publish.
You should still keep the source manifest meaningful because:
- Route extraction is static analysis, so unusual routing patterns may require manual/custom publish logic.
- If you build your own publish pipeline, you must still output valid runtime
routes.
Complete example
Application feature (route-owned)
json
{
"id": "feature-my-feature",
"version": "1.0.0",
"displayName": "My Feature",
"channel": "stable",
"mount": {
"pathPrefix": "/my-feature",
"domElementId": "feature-root"
},
"routes": ["/my-feature", "/my-feature/*"],
"requirements": {
"authRequired": true,
"minHostVersion": "1.0.0"
},
"visibility": {
"ownerTeam": "team-my-feature",
"notes": "Optional feature-owned metadata"
},
"shared": {
"react": { "singleton": true, "requiredVersion": "^19.0.0" },
"react-dom": { "singleton": true, "requiredVersion": "^19.0.0" },
"react-router-dom": { "singleton": true, "requiredVersion": "^7.1.0" }
},
"runtime": {
"permissions": {
"eventBus": {
"publish": ["my-feature:*"],
"subscribe": ["*:*"]
},
"hostStore": {
"read": ["*"],
"write": ["locale", "theme"],
"subscribe": ["locale", "theme"]
},
"lifecycle": { "allow": true }
}
},
"serverFunctions": {
"module": "server-functions.js",
"exports": ["getData", "saveData"]
},
"entrypoints": {
"js": ["/static/feature-my-feature/1.0.0/feature-my-feature.abc123.js"],
"css": ["/static/feature-my-feature/1.0.0/feature-my-feature.def456.css"]
},
"integrity": {
"/static/feature-my-feature/1.0.0/feature-my-feature.abc123.js": "sha384-...",
"/static/feature-my-feature/1.0.0/feature-my-feature.def456.css": "sha384-..."
},
"signature": {
"algorithm": "ed25519",
"keyId": "prod-ed25519-2026-01",
"signature": "...",
"signedAt": "2026-03-02T09:12:00.000Z"
},
"isolation": {
"shadowDOM": false
}
}Facet feature (reusable CSR module)
json
{
"id": "feature-summary-module",
"version": "1.0.0",
"displayName": "Summary Module",
"featureType": "facet",
"channel": "stable",
"module": {
"mountExport": "mount",
"unmountExport": "unmount"
},
"entrypoints": {
"js": ["/static/feature-summary-module/1.0.0/feature-summary-module.abc123.js"]
}
}Key fields
| Field | Required | Purpose |
|---|---|---|
id | Yes | Unique feature identifier (kebab-case, prefixed with feature-) |
version | Yes | Semver version |
displayName | Yes | Human-readable name for admin UI and navigation |
featureType | No | domain (default) or facet (application/module aliases accepted) |
channel | No | Release channel: stable, canary, or dev (defaults to stable) |
mount.pathPrefix | Yes for domain; optional for facet | URL path prefix (e.g., /my-feature) |
mount.domElementId | Yes for domain; optional for facet | DOM element ID for React mount |
routes | Yes for domain; optional for facet | Routes this feature owns |
module.mountExport / module.unmountExport | No | Module export names (defaults mount / unmount) |
requirements.authRequired | Recommended | If true, host returns 401 for unauthenticated requests |
requirements.minHostVersion | Recommended | Minimum host version required |
visibility | Optional | Team-owned visibility metadata (host does not enforce) |
shared | Recommended | Shared dependencies (React, etc.) |
runtime.permissions | Optional | Scoped access to host APIs |
serverFunctions | Optional | Server-side RPC functions |
entrypoints.js | Yes | Client module entrypoints loaded by the host shell |
entrypoints.css | Optional | CSS assets loaded by host shell (or injected into Shadow DOM mode) |
integrity | Yes (published artifacts) | SRI map for all declared JS/CSS entrypoints |
signature | Yes (published artifacts) | Manifest signature used by host trust verification |
isolation.shadowDOM | Optional | CSS isolation via Shadow DOM |
Validation rules
The host validates every manifest at load time:
id,version(semver), anddisplayNameare requiredfeatureTypemust bedomainorfacet(legacyapplication/modulealiases are accepted; normalized todomain/facet)channelis optional; when omitted it is normalized tostablemount.pathPrefixandmount.domElementIdare required fordomainentrypoints.jsmust be non-empty (entrypoints.cssoptional)routesmust be non-empty fordomain- Server functions require
moduleorendpointplus non-emptyexports - Endpoint must be absolute
http/httpswithout credentials - Runtime permissions shape is validated
requirements.minHostVersionis enforced against the host version
During runtime loading, the host additionally enforces:
- Manifest signature validity against trusted public keys
- Non-empty integrity map and SRI hash verification for all entrypoint assets
The runtime host always validates the published artifact manifest, not your source manifest directly.
Visibility
Visibility fields are informational metadata for feature-team logic:
json
{
"visibility": {
"featureToggles": ["my-feature-enabled"],
"tenants": ["hospital-a"]
}
}The host does not hide routes based on visibility. If you need gating, enforce it in your feature server/backend logic.
Authentication
Set requirements.authRequired: true to enforce authentication at the route level.
Without an auth signal (bearer token, forwarded token, or cookie), the host returns 401 before your feature code loads.
Auth signals recognized:
Authorization: Bearer <token>X-Auth-Request-Access-Token: <token>(ingress-forwarded)Cookie: ...(session-based)