xript Guide

Every non-primitive node in epixtudio is a xript. Not “powered by” or “built on top of”; is a xript. The built-in nodes use the same system you use when you write your own. There’s no inner ring.

What Works Today

The Custom Nodes tutorial covers the in-app node editor. You define a name, category, ports, and a JavaScript script; epixtudio stores it in your local database and treats it like any built-in node. No files to manage, no build step, no manifest to hand-write.

Scripts run in a QuickJS WASM sandbox. They receive inputs via inputs.portName(), process data in plain JavaScript, and return results as the last expression. The sandbox has no access to the filesystem, network, or browser APIs. This means custom nodes today are pure computation: string manipulation, math, JSON reshaping, data formatting. Anything that doesn’t need to reach outside the sandbox.

The built-in nodes that do reach outside (file I/O, HTTP requests, image processing) use host bindings that are wired up in Rust on the backend. Custom nodes don’t have access to those bindings yet.

The Sandbox

The sandbox (@xriptjs/runtime) isn’t a suggestion or a best practice; it’s the execution model.

This is a feature, not a limitation. When someone shares a custom node with you, the sandbox is the reason you can trust it. The worst a sandboxed script can do is return bad data; it can’t touch your files, phone home, or side-channel anything.

What’s Coming: Host Bindings and Capabilities

The architecture supports host bindings (Rust functions exposed to the sandbox), but custom nodes can’t use them yet. The plan:

Host bindings are functions the sandbox can call into the host. A small set of generic primitives would cover most use cases:

BindingPurpose
net.request(url, method, headers, body)Generic HTTP; covers REST APIs, webhooks, SMTP-via-API
fs.read(path)Read a file from disk
fs.write(path, data)Write a file to disk

The built-in nodes already use internal equivalents of these. Exposing them to custom nodes is plumbing, not invention.

Capabilities are the permission model. Each binding requires a declared capability in the node’s manifest. When you write a node yourself and it goes straight into your local database, no approval is needed. You wrote it; you know what it does.

When someone else’s node enters the picture, the capability declarations become the consent layer. Before an imported node can be added to your database, epixtudio shows you what it’s asking for (network access, filesystem access, etc.) and you approve or reject. One-time decision. Once approved, the node runs like any other. No runtime prompts, no per-execution dialogs.

This applies to individual nodes shared between users. Graph-level import (a .xtudio file referencing custom nodes you don’t have) is a harder problem that’s still being worked out.

The Manifest System

Under the hood, every xript is a JSON manifest paired with a JavaScript module. The in-app node editor generates this for you, but the format matters if you’re building tooling or distributing nodes.

{
  "xript": "0.1",
  "name": "my-custom-node",
  "description": "What this node does",
  "inputPorts": [
    { "name": "text", "type": "Text" }
  ],
  "outputPorts": [
    { "name": "result", "type": "Text" }
  ],
  "capabilities": {
    "network": {
      "description": "Makes HTTP requests to external APIs",
      "risk": "medium"
    }
  }
}

xript — manifest version. Currently "0.1".

name — unique identifier for the node type.

inputPorts / outputPorts — typed port declarations. Types: Text, Number, Boolean, Json, Image, Any.

capabilities — what the node needs beyond pure computation. Each capability has a description (shown to the user on import) and a risk level (low, medium, high).

Presentation Layer

There’s a separate presentation xript for UI extensibility. Same sandbox, different surface area. It has access to a limited set of host bindings for theming and keybindings:

ui namespace:

app namespace (read-only):

npm Packages

PackagePurpose
@xriptjs/runtimeQuickJS WASM sandbox for script execution
@xriptjs/validateManifest schema validation
@xriptjs/typegenTypeScript type generation from manifests
@xriptjs/docgenDocumentation generation from manifests
@xriptjs/initScaffolding CLI for new xript projects

These are the building blocks for tooling around xripts. @xriptjs/init scaffolds a valid manifest and entry point; @xriptjs/validate catches structural problems before they turn into silent failures at load time.

Principles