Permissions and trust limits
Manifest/host intersection, revocable identities, UI roles, and separate controls for plugins, AI, and the server.
See versions, changes and requirements in Framework 4.0 and CLI 1.2.
Effective authority
WU calculates permissions as the intersection of manifest requests and shell grants. Without requests or grants, the app receives no technical capabilities. Adding a grant the app did not request does not expand its authority either.
{
"name": "cart",
"entry": "assets/cart.js",
"wu": {
"permissions": [
{ "capability": "store.read", "resource": "cart", "required": true },
{ "capability": "store.write", "resource": "cart.items", "required": true },
{ "capability": "events.emit", "resource": "cart:changed" }
]
}
}
await wu.init({
apps: [{
name: 'cart',
url: '/apps/cart',
grants: [
{ capability: 'store.read', resource: 'cart' },
{ capability: 'store.write', resource: 'cart.items' },
{ capability: 'events.emit', resource: 'cart:changed' },
],
}],
});
required: true demands complete coverage to register the app. An optional request may be denied or partially covered; the policy retains diagnostics. Optional does not mean it becomes authorized on the first call.
Supported technical capabilities
| Capability | Operation |
|---|---|
events.emit |
Emit facts on granted resources |
events.subscribe |
Subscribe to permitted events |
rpc.provide |
Provide bus handlers and method contracts |
rpc.call |
Request responses and consume contracts |
store.read |
Read, obtain snapshots, and subscribe to paths |
store.write |
Write and batch paths |
drag.offer |
Offer data of a type during a drag |
drag.accept |
Accept data of a type at a drop target |
store.update requires reading and writing. Arbitrary device.*, deploy.*, or build.* grants do not exist; the list is closed in wu-policy.js.
Resources use exact strings or a trailing wildcard. For the store, the wildcard must occupy a whole segment, such as cart.*. wu:, system:, and app: are reserved namespaces for emission and RPC provision/calls. An events.emit:* request overlaps them and is not a valid way to grant all host authority.
Identity during the lifecycle
The core creates a lease per application and supplies a frozen facade. Every method checks that the lease remains active; functions add the correct identity without allowing an app to become another by changing appName.
Unmounting revokes facades and cleans up associated resources. Contracts consumed through the facade are also wrapped to fail after revocation. A global variable retaining an old facade does not regain authority when an app with the same name mounts again: a new lifecycle identity is created.
Event/store hooks in all thirteen adapters respect the app’s facade in 4.0. This protects cooperative calls, but module shares the realm and can reach the host. Shadow DOM and same-origin sandboxes do not contain hostile code; use a remote app on another origin for that boundary.
Four different controls
| Control | What it decides | What it does not decide |
|---|---|---|
| WU technical grants | Bus, RPC, and store path usage | Access to backend data |
setPrincipal / roles |
Whether an app can mount in the UI | Reliable server-side user authorization |
| Plugin permissions | Which methods its installation API offers | Containment of code already running in the host |
| AI flags | Reading, writing, DOM, network, or AI action execution | Domain permissions for a remote operation |
Plugin configuration uses a different list: mount, events, store, apps, config, and unsafe. The host must explicitly grant requested permissions. AI actions use flags such as executeActions, writeStore, and network. These lists are not interchangeable.
The server boundary
Credentials, resource access, idempotency, and durable auditing belong to an authenticated service. An action handler is host code and can capture fetch or a privileged client in a closure; the API supplied to the handler cannot contain that closure.
For operations with effects, design a gateway that validates identity, parameters, scope, policy, and approval before execution. Timeline and store do not undo sends, payments, builds, or infrastructure changes. If the domain supports undo, implement explicit compensation.
The wu-framework/server subpath offers createNonce, securityHeaders, buildCsp, middleware, and response wrappers. These helpers construct headers; the server delivering the page and assets is responsible for actually installing them.
Controlled migration
await wu.init({
compatibility: { profile: 'compat-v2', warnings: 'once' },
apps: [],
});
console.table(wu.compatibility.getMigrationReport());
The compat-v2 profile enables specific bridges for eventPermissions, implicit plugin grants, and implicit AI action execution. It is an explicit migration with diagnostics; it should not become the base configuration for a new app. strictEvents: false is also a deliberate escape hatch for trusted code and reduces bus checks.
Implementation references
Policy: src/core/wu-policy.js, src/core/wu-policy.js, src/core/wu-policy.js; leases: src/core/wu-core.js and src/core/wu-core.js; plugins: src/core/wu-plugin.js; AI defaults: src/ai/wu-ai-permissions.js; compatibility: src/core/wu-compatibility.js. Tests: tests/unit/capability-security-v3.test.js, tests/unit/plugin-security.test.js, tests/unit/security-regressions.test.js, tests/security/compatibility-boundaries.test.js.