Example: Plugin System
A full plugin system built on xript’s tier 2 features: namespaces to organize bindings, capabilities to gate destructive operations, and custom types to describe data structures. Five plugins run against the same host, each with a different permission profile.
The full source is in examples/plugin-system/.
The Manifest
Section titled “The Manifest”The manifest declares a tasks namespace with five methods and a top-level log function:
{ "xript": "0.1", "name": "task-manager", "version": "1.0.0", "bindings": { "tasks": { "description": "Read and manage tasks.", "members": { "list": { "description": "Returns all tasks.", "returns": { "array": "Task" } }, "get": { "description": "Returns a task by ID.", "params": [{ "name": "id", "type": "string" }] }, "add": { "description": "Creates a new task.", "params": [...], "capability": "manage-tasks" }, "complete": { "description": "Marks a task as done.", "params": [...], "capability": "manage-tasks" }, "remove": { "description": "Permanently removes a task.", "params": [...], "capability": "admin" } } }, "log": { "description": "Writes a message to the plugin console.", "params": [...] } }}Key observations:
tasks.listandtasks.gethave no capability gate: any plugin can read taskstasks.addandtasks.completerequire themanage-taskscapabilitytasks.removerequires theadmincapability, a higher privilege tierlogis a top-level function with no gate
Capabilities
Section titled “Capabilities”{ "capabilities": { "manage-tasks": { "description": "Create and complete tasks.", "risk": "medium" }, "admin": { "description": "Delete tasks and admin operations.", "risk": "high" } }}Custom Types
Section titled “Custom Types”{ "types": { "Task": { "description": "A task in the task manager.", "fields": { "id": { "type": "string" }, "title": { "type": "string" }, "done": { "type": "boolean" }, "priority": { "type": "Priority" }, "createdAt": { "type": "string" } } }, "Priority": { "description": "Task priority levels.", "values": ["low", "medium", "high", "urgent"] } }}Types do not enforce runtime validation; they serve as documentation for extenders and feed into tools like xript typegen and xript docgen.
The Host
Section titled “The Host”The host creates a shared task store and wires up the namespace methods:
const taskStore = [];
const hostBindings = { tasks: { list: () => [...taskStore], get: (id) => taskStore.find((t) => t.id === id), add: (title, priority) => { const task = { id: String(nextId++), title, done: false, priority, createdAt: new Date().toISOString() }; taskStore.push(task); return task; }, complete: (id) => { /* mark done */ }, remove: (id) => { /* splice from array */ }, }, log: (msg) => console.log(`[plugin] ${msg}`),};Each plugin gets its own runtime instance but shares the same underlying taskStore, so plugins see each other’s changes; that shared visibility is deliberate here.
The factory is initialized once, then each plugin creates a runtime from it:
import { initXript } from "@xriptjs/runtime";const xript = await initXript();The Five Plugins
Section titled “The Five Plugins”1. Task Reporter (no capabilities)
Section titled “1. Task Reporter (no capabilities)”const runtime = xript.createRuntime(manifest, { hostBindings, capabilities: [] });runtime.execute("tasks.list()");runtime.execute('log("Found " + tasks.list().length + " tasks")');Can read tasks and call log, but cannot create, complete, or remove tasks.
2. Task Creator (manage-tasks)
Section titled “2. Task Creator (manage-tasks)”const runtime = xript.createRuntime(manifest, { hostBindings, capabilities: ["manage-tasks"] });runtime.execute('tasks.add("Write documentation", "high")');runtime.execute('tasks.complete("1")');Can create and complete tasks, but cannot remove them (no admin capability).
3. Read-Only Dashboard (no capabilities)
Section titled “3. Read-Only Dashboard (no capabilities)”const runtime = xript.createRuntime(manifest, { hostBindings, capabilities: [] });runtime.execute('tasks.list().filter(t => !t.done).length'); // worksruntime.execute('tasks.add("Sneaky task", "low")'); // CapabilityDeniedErrorDemonstrates that a plugin without capabilities still cannot modify tasks, even after other plugins have created them.
4. Admin Cleanup (manage-tasks + admin)
Section titled “4. Admin Cleanup (manage-tasks + admin)”const runtime = xript.createRuntime(manifest, { hostBindings, capabilities: ["manage-tasks", "admin"] });runtime.execute('tasks.remove("1")');Full access. Can read, create, complete, and remove tasks.
5. Privilege Escalation Attempt (manage-tasks only)
Section titled “5. Privilege Escalation Attempt (manage-tasks only)”const runtime = xript.createRuntime(manifest, { hostBindings, capabilities: ["manage-tasks"] });runtime.execute('tasks.remove("2")'); // CapabilityDeniedError: requires "admin"Having manage-tasks does not grant admin; each capability must be explicitly granted.
Auditing Capability Use
Section titled “Auditing Capability Use”Every runtime accepts an optional audit callback. The host receives an AuditEvent ({ binding, capability, at }) each time a plugin invokes a gated binding, so a host can log, rate-limit, or alarm on sensitive operations without changing the bindings themselves:
const runtime = xript.createRuntime(manifest, { hostBindings: createHostBindings(), capabilities: ["manage-tasks", "admin"], audit: ({ binding, capability, at }) => { console.log(`[audit] ${binding} (cap: ${capability ?? "none"}) at ${at}`); },});runtime.execute('tasks.remove("1")'); // emits an audit event for "tasks.remove" / "admin"The audit channel is per-capability and host-side only; it observes grants the runtime already enforces and never relaxes them.
Running the Demo
Section titled “Running the Demo”cd examples/plugin-systemnode src/demo.jsThe demo runs all five plugins sequentially, showing which operations succeed and which are denied.
You can also point the toolchain at this manifest to validate it, inspect its extension surface, and see how moddable it is:
npx xript validate examples/plugin-system/manifest.json # check it against the spec schemanpx xript describe examples/plugin-system/manifest.json # summarize bindings, capabilities, and typesnpx xript score examples/plugin-system/manifest.json # rate the exposed moddability surfacenpx xript lint examples/plugin-system/manifest.json # findings-based review of the manifestxript score measures moddability capacity: how much of the host’s surface (bindings, slots, capabilities, events) is exposed to extenders, not how heavily any particular plugin exercises it.
Concepts Demonstrated
Section titled “Concepts Demonstrated”| Concept | Where |
|---|---|
| Namespaces | tasks.list(), tasks.add() |
| Capability gating | manage-tasks on add/complete, admin on remove |
| Tiered permissions | Plugin 5 has manage-tasks but not admin |
| Custom types | Task and Priority in the manifest |
| Default-deny | Plugins 1 and 3 get read-only access |
| Shared state | All plugins see the same task store |
When to Use This Pattern
Section titled “When to Use This Pattern”Tier 2 is the right choice when:
- You need to organize bindings into logical groups (namespaces)
- Some operations are destructive or sensitive and need permission gating
- You want to document data structures for extenders
- Different plugins need different permission levels