Skip to content

Live plugin contracts and metering

Keep portable declarations separate from trusted host execution, current access checks and durable effects.

flowchart LR
Contract[Public contract] --> Host[Verified host scope]
Host --> Operation[Explicit runtime operation]
Operation --> Validation[Result and permission verification]

Install the package; the subpath below is an import target, not a separate package. Registry installation requires a published EAOS release; before publication use the owning workspace’s checksum-verified build artifact.

Terminal window
bun add @alneas/eaos-server
import { PluginEntitlementRuntime } from '@alneas/eaos-server/entitlements'
export function bindEntitlements(options: ConstructorParameters<typeof PluginEntitlementRuntime>[0]) {
return new PluginEntitlementRuntime(options)
}

Server runtime only. Supply the live contract loader, lifecycle admission and a shared durable atomic repository; importing does not create the control tables.

@alneas/eaos-server/entitlements exports PluginEntitlementRuntime, createPluginEntitlementGuard, createPluginAssetRouter, tenantEntitlementContract and createSqlTenantEntitlementLoader. Install the control contract through reviewed Lifecycle operations; importing it never creates a table. The SQL loader reads eaos_tenant_entitlements from the primary control database. Host administration owns grants, expiry and CAS updates; the framework does not issue payments or subscriptions.

Construct a runtime with the loader, current Lifecycle admission callback and a shared AtomicRepository. Every admitted metered attempt reserves one unit in a UTC calendar-month bucket and appends the existing durable atomic audit chain in the same transaction. A failed audit write rolls back the reservation. A handler failure still counts its admitted attempt. Retries count separately; business command idempotency does not deduplicate admission. Discovery and asset URL requests do not consume execution units. Repository CAS conflicts fail closed; callers must not blindly retry a business operation.

Pass the runtime as createApp({ entitlements, resolveEntitlementPrincipal }). An explicitly declared policy cannot be bypassed by disabling the legacy module guard or using development mode. A missing runtime returns 503 on HTTP; unmet subscriptions/addons/quotas return 402; a foreign Corporate tenant receives 404 before module checks. Existing manifests without entitlement retain their previous authorization behavior.

MCP discovery filters current contracts. Both execute and JSON-RPC calls check the contract before constructing dependencies and reserve quota immediately before the handler. JSON-RPC errors contain the HTTP-equivalent status in error.data.status.

Jobs and verified webhooks must use the same runtime’s run(principal, pluginId, rule, operation, callback) in addition to their host authentication/resource checks. Never derive the principal or policy from a job payload. Existing direct service method calls do not automatically gain entitlement checks.

createSdkPluginRouter in @alneas/eaos-server/plugin-sdk mounts a Core definePlugin definition. It requires a principal resolver, a current lifecycle/resource authorization callback and a scoped context factory. Identity cannot be overridden by the factory or request body. JSON bodies are bounded to 1 MiB; route-specific input validation remains the plugin’s responsibility. Browser hosts still supply their established CSRF/Origin middleware.

The asset router accepts only a trusted catalog and signing callback. It rechecks admission after signing, returns private, no-store metadata and limits the requested lifetime to 60 seconds. The signing adapter must enforce that lifetime and private storage. A copied signed URL can remain usable until expiry, and authorized browsers can read downloaded bytes; this is access control, not DRM. WASM and ONNX assets must not also be served from a public directory.

For React, merge current runtime decisions into the authenticated context’s pluginAccess map and refresh it on contract/session changes. Omit foreign Corporate plugins completely. UI gates never replace backend admission.