Splash is a capability-first scripting runtime for dynamic workflows, tool orchestration, and data transformation. It starts from the Makepad Splash VM and keeps UI support optional rather than making UI the language boundary.
- A standalone, vendored VM and parser with upstream provenance.
- An effect-free, bounded canonical-language preflight with structured diagnostics for generated source and editor tooling, plus token-aware lowering of canonical newline statement boundaries for the inherited VM.
- An effect-free canonical formatter that preserves comments and literal spellings while normalizing valid Splash source for LLM and editor workflows.
- A bounded, grammar-aware lexical symbol index for imports, functions, local bindings, parameters, and loop bindings without evaluating source.
- Bounded same-document lexical completion at expression identifiers, with
scope-aware candidates, exact-token replacement edits, fixed
mod.toolmember suggestions for an exact visibleuse mod.toolbinding, an optional bounded refreshable advisory tool-catalog projection for direct tool-name literals, and an optional refreshable module-interface projection for direct import paths and bounded chained imported-module members, plus bounded direct-literal record-field completion, hover, and definition through exact direct child literals and bounded alias paths without runtime type inference. - An effect-free per-step workflow review that pairs syntax status with direct tool-call hints before a host issues ordered capability leases.
- A bounded, data-only workflow-draft JSON format and CLI review path for LLM plans before a host creates a trusted plan or issues authority.
- Approval-bound bounded JSON workflow dataflow: host input and completed step outputs are injected as data only, remain lease-constrained, and are never copied into workflow telemetry.
- Optional host-owned dataflow schema contracts that validate input and every completed step output before a later step's authority can become active, and bind their digest into contract-aware dataflow checkpoints.
- A host-only stdio language server that publishes canonical syntax diagnostics,
full-document formatting edits, top-level declaration symbols, and
same-document lexical definitions, references, binding-kind hover, and symbol
highlights, lexical completion including the fixed
mod.toolAPI and optional advisory tool and module metadata, bounded fixed/advisory capability signature help, one bounded direct-module output-object child level, and version-bound guarded rename without reading files or evaluating code. - Default runtime and capability-host evaluation that rejects noncanonical Makepad compatibility syntax before a tool can run.
- Standalone runtime initialization that masks inherited Makepad UI/debug and unbounded native entry points before source evaluation, leaving only the documented core plus trusted host-installed modules reachable from Splash.
- Frozen no-authority
mod.std.mathscalar helpers,mod.std.jsonbounded JSON helpers,mod.std.textbounded text helpers, andmod.std.arraybounded shallow-array helpers, plusmod.std.objectbounded own-field record helpers for common dataflow without restoring Makepad's broader shader-orientedmod.mathsurface or granting host authority. - A bounded evaluator with source, individual-string, tracked Splash-owned retained-heap, VM operand-stack, active-call-frame, instruction, and deadline limits. These VM ceilings are not an OS process-memory quota and exclude opaque trusted Rust adapter allocations.
- Direct
RuntimeJSON conversion through.parse_json()/.to_json(), the boundedstring.to_bytes()bridge, and frozenmod.std.json: strict, byte- and depth-bounded input plus cycle-aware, byte- and depth-bounded output, with ordinary script errors rather than unbounded VM work. - Recoverable
try ... catch ...control flow across Splash function calls, with hard resource stops kept uncatchable and no implicit effect rollback. - A deny-by-default tool host: scripts can call only explicitly registered
tools through
mod.tool. - A bounded LLM-facing tool catalog with aggregate descriptor-count and serialized-byte limits in addition to per-tool metadata and schema bounds.
- Cursor-safe bounded capability-audit export and workflow-event views with explicit eviction counters, plus an opt-in authenticated durable capability-audit journal and authenticated workflow-event journal for host-owned operator/audit replay that remain separate from workflow authority.
- Bounded host-receipt-order cross-stream telemetry for named capability-audit and workflow-event source segments, including an in-memory aggregator and an authenticated durable aggregate journal with exact source and aggregate cursors, explicit loss detection, and no recovery or capability authority.
- Audited tool calls with input/output and call-count limits.
- Bounded executable JSON contracts for structured tool inputs and outputs.
- Schema-required Serde bridges for reviewed Rust input and output types.
- Bounded, host-pumped deferred tool promises for cooperative mobile and embedded event loops.
- A sealed static-catalog mobile and embedded profile for reviewed local Rust adapters, with executable JSON contracts for structured script-visible data.
- A bounded host-owned fixed-file catalog adapter for reviewed regular UTF-8 files, addressed only by opaque identifiers and pinned at setup rather than by script-selected filesystem paths.
- A feature-gated host-owned fixed HTTP endpoint catalog and exact-origin policy catalog for reviewed JSON GET and POST calls. Fixed endpoints accept only opaque IDs; origin policies admit a bounded script URL only after exact scheme, host, and effective-port matching. Both use HTTPS by default, bound request/response data, disable proxies and redirects, and keep methods, headers, and credential bindings host controlled. A host can inject a resolved credential into one fixed HTTPS endpoint or intentionally across every accepted route at one exact HTTPS origin. An optional native resolver performs read-only exact credential lookup on macOS, iOS, and Windows without a mock fallback; this is API-level mediation, not egress containment or a general secret API.
- An optional Linux-only private Unix-socket HTTP broker for an isolated
Bubblewrap worker. It binds exactly the manifest's opaque
network_originIDs to one reviewed endpoint or origin catalog, retains Bubblewrap's isolated network namespace, and adds a descriptor-pinned private directory containing one socket. It is aggregate per worker session, HTTP-only, and not a portable firewall, raw network API, per-tool process boundary, or durable-effect protocol. - A bounded worker-side capability secret-broker contract for reviewed Rust
adapters: a host-owned provider can release a zeroizing binary secret only
to one exact preconfigured
(tool, secret-id)binding whose active worker grant carries that same opaqueSecretresource. It has no Splash lookup or enumeration API and is not itself a platform credential store or OS secret boundary. - A sealed mobile and embedded workflow profile that exposes data-only drafts, bounded JSON dataflow and schema contracts, host-owned plans, named per-step policies, checkpoints, and execution, including setup-only fixed-file and fixed-endpoint/origin catalog adapters and direct capability modules, without exposing mutable capability registration.
- Deferred-only external tools that hosts claim, complete, or cancel without installing an in-process handler.
- Per-tool deferred deadlines with host-driven expiry and auditable timeout outcomes.
- Host-only bounded retries for external tools, with stable non-authorizing idempotency keys for each deferred operation. External registration fails closed when OS entropy is unavailable unless the host supplies a bounded session nonce with a documented uniqueness scope.
- Bounded, optionally redacted external output chunks released only to the trusted host, never directly to Splash source.
- Keyed, directional, replay-checked worker protocol frames and authenticated reconciliation for live external operations.
- Authenticated durable-operation dispatch frames and a bounded worker journal for replay-safe idempotency across a worker restart.
- A capability-scoped worker runtime that dispatches only explicitly registered Rust adapters and enforces durable operation ordering.
- Host-approved, current-policy- and product-action-revalidated durable compensation intents with one inverse effect per succeeded operation and replay-safe worker recovery.
- Approval-bound, catalog-fingerprinted capability leases that attenuate
dynamic workflow tool calls across
awaitand resume, including one least-privilege lease per trusted workflow step. - Host-owned, ordered per-step capability policies that bind named trusted steps to grants before issuing those leases; they are configuration, not serialized or script-visible authority.
- Bounded, data-only workflow checkpoints with fresh host approval required for a restart to run the remaining plan suffix, with dataflow checkpoints retaining only a context digest rather than raw input or prior outputs.
- Resumable live external workflow steps that retain the approved capability lease through host completion or a two-phase cooperative adapter cancellation request and acknowledgement.
- Plan-bound durable external-operation ledgers with input fingerprints, derived worker keys, revision-watermark hooks, and a two-stage prepare/persist/exact-claim bridge for suspended external workflow steps.
- Host-only authenticated storage envelopes with key rotation and a strict rollback-protected compare-and-swap backend contract.
- Optional SQLite payload storage paired with an explicit trusted rollback anchor, including durable revision and fencing commitments.
- Bounded transactional rollback-anchor service protocol with an embeddable server-side dispatcher, optional exact caller/operation/record authorization gate, and optional fixed HTTPS client transport. It rejects malformed or regressing protocol data and disables client redirects and proxies, but the separately deployed service remains the rollback-resistant CAS authority.
- Fenced authenticated worker-journal storage that binds durable worker state to a host-selected record, revision, and current writer lease.
- Feature-gated authenticated in-process worker transport for app-provided mobile and embedded adapters; it preserves ordinary invocation framing but is not OS containment.
- Feature-gated bounded JSON-line worker channel and authenticated transport for host-provided contained-worker pipes; process creation, deadlines, and containment remain host policy.
- Feature-gated multiplexed JSON-line transport and worker driver for one authenticated ordinary invocation, with exact request-bound cooperative cancellation, explicit cancellable-adapter opt-in, and no false acknowledgement on process termination.
- A session-bound supervisor bridge that resolves watchdog races before it
applies worker completion or cancellation to
CapabilityRuntime, plus a workflow adapter that advances suspended steps throughWorkflowEngine. - Feature-gated one-shot authenticated durable-operation transport for a fresh contained-worker session; it validates one dispatch, reconciliation, or compensation result but does not automate recovery policy.
- Feature-gated Bubblewrap post-stop recovery coordinator that requires a session-bound reaping proof, starts a differently keyed least-privilege contained worker, performs one watchdog-bounded reconciliation, and commits the observation through fenced authenticated compare-and-swap storage.
- Linux Bubblewrap worker-policy compiler and launcher for a fixed,
host-selected worker and manifest-selected file roots, plus an optional exact
brokered HTTP
network_originpath; it rejects executable and secret selectors and every network-origin grant without that broker rather than claiming unsupported policy, denies persistent writable host roots unless they carry a verified, descriptor-pinned Linux project quota with a configured aggregate hard byte and inode bound plus mandatory further-user-namespace lockdown, or host code explicitly selects the weaker external-quota escape hatch, and drops every Linux capability before worker execution. - Optional Linux descriptor-pinned executable identity for the fixed Bubblewrap, worker, pre-exec runners, and explicit Landlock executable targets, with no path-launch fallback. It requires descriptor-pinned runtime roots and does not replace immutable runtime ownership or complete code-execution mediation.
- A one-shot, versioned private-pipe session bootstrap for Linux Bubblewrap workers that is bound to the exact manifest retained by the compiled command and precedes JSON worker frames without exposing the key through argv or environment variables.
- Optional bounded private
/tmpcapacity and a host lifecycle handle that force-terminates and reaps a Bubblewrap worker without treating termination as an adapter-effect result. - Manifest-selected bounded ephemeral
file_rootmounts at host-chosen worker paths. Active persistent host-backed writable roots fail closed unless they use a verified Linux generic project quota on the exact descriptor-pinned root, with configured aggregate hard byte and inode bounds and mandatory further-user-namespace lockdown, or host code explicitly acknowledges an independently enforced quota. An opt-in stricter policy rejects unverified persistent roots and an unbounded private/tmp, requires further-user-namespace lockdown, and remounts the base namespace filesystems read-only. Each ephemeral root has its owntmpfsallocation ceiling, and hosts can reject a configured aggregate potential capacity before launch. This remains independent per-mount tmpfs accounting, not a shared tmpfs runtime quota; it does not independently cap inodes and is not persistent storage, anoexecguarantee, or a portable host-filesystem quota. A worker plan also defaults to at most 64 unique activefile_rootselections; a host can lower that bound, including to zero, or explicitly raise it only to the fixed 256-root maximum, bounding mount-plan expansion rather than disk use. - Optional Linux cgroup-v2 worker sessions with host-delegated CPU bandwidth, memory, swap, task, and per-device I/O limits; a fixed runner joins the cgroup before Bubblewrap starts, and managed lifecycle teardown kills the whole worker process tree.
- Optional Linux Bubblewrap seccomp profiles: a compatibility-oriented fixed deny set and a bounded host-selected strict syscall allowlist that kills unlisted syscalls. With a Landlock executable runner, strict filtering is staged after Landlock setup and immediately before the fixed inner exec. Neither mediates executable paths or capability grants.
- Optional Linux Landlock filesystem-backed executable allowlist for exact worker-visible files, installed by a fixed pre-exec runner with no unsupported-kernel fallback. It is not a complete code-loading, network, secret, or capability boundary.
- Optional Bubblewrap watchdog and generic bounded worker transport with host-selected per-invocation and total-session wall-clock deadlines; expiry or host termination poisons the session and remains indeterminate.
- A small
splashCLI for local evaluation and the workflow example.
No ambient filesystem, subprocess, raw socket, HTTP client/server, or Makepad
platform/debug module is source-reachable by default. The vendored VM bootstrap
retains compatibility objects internally, but Runtime masks their source
entry points before either canonical or compatibility evaluation. The optional
fixed-file and fixed-endpoint catalogs are explicit, bounded tools rather than
general filesystem or network APIs. A capability check in the VM is not an OS
sandbox; adapters that execute local tools or need egress isolation must run
behind an appropriate target-specific containment boundary before they are
suitable for untrusted workloads.
For ordinary numeric dataflow, use mod.std.math provides a small frozen
Splash-owned scalar library. It is separate from the masked Makepad
mod.math shader module and cannot access files, processes, networking,
clocks, entropy, or Rust crates.
For strict local JSON conversion, use mod.std.json provides only
json.parse(document) and json.stringify(value). They reuse the same
byte/depth/cycle-bounded boundary as .parse_json() and .to_json() and have
no host, adapter, filesystem, process, network, clock, entropy, or crate
access.
For local text shaping, use mod.std.text provides trim, lower, upper,
Unicode-scalar len, Unicode-scalar slice, Unicode-scalar literal
index_of and last_index_of, literal predicates, literal replace_all,
split with literal matching, and join. slice uses a half-open scalar
range with 0 <= start <= end <= text.len(value). index_of returns the first
scalar position of a literal match or -1; an empty needle returns 0.
last_index_of returns the last scalar position or -1; an empty needle returns
text.len(value). split
matches a non-empty delimiter literally, preserves empty fields, and returns at
most 4,096 segments. join accepts an array of at most 4,096 strings,
preserves their order, and permits an empty string separator. Results use
Splash's configured string bound; the module does not expose regexes, host
state, filesystem, process, network, clock, entropy, or crate access.
For local collection shaping, use mod.std.array provides array.len(value),
array.has_index(value, index), array.get(value, index, fallback),
array.contains(value, item), array.index_of(value, item),
array.slice(value, start, end), array.range(start, end),
array.concat(left, right),
array.compact(value), array.unique(value), array.reverse(value),
array.flatten(value), and array.push(value, item).
has_index distinguishes an in-range nil item from an absent index, while
get returns its fallback only when the index is absent. Neither traverses the
array. contains and index_of scan at most 4,096 items with direct equality:
scalar values compare by value, while arrays and records match only by
reference; index_of returns the first match or -1. slice uses a half-open
range with non-negative integer indexes. range builds a fresh half-open
[start, end) array for indexed loops; its non-negative endpoints must be
exact Splash scalar integers through 2^53, start cannot exceed end, and
it returns at most 4,096 items. Canonical Splash deliberately has no range
operator.
compact returns a fresh shallow array without nil items while preserving
false, zero, empty strings, and order. unique returns a fresh shallow
array in first-occurrence order, removing later values with the same direct
equality as contains and index_of. flatten is one level only: every outer
item must be an array, and it rejects any source or result over 4,096 items
before copying. push mutates its array, returns nil, and rejects a result
over 4,096 items. The transforming helpers are callback-free and shallow;
len, has_index, and get are constant-time and uncapped. The module does
not expose host state, filesystem, process, network, clock, entropy, or crate
access.
For bounded record shaping, use mod.std.object provides object.len(value),
object.has(value, key), object.get(value, key, fallback),
object.pick(value, keys), object.omit(value, keys),
object.from_entries(entries),
object.with(value, key, item), object.keys(value), object.entries(value),
object.values(value), and object.merge(left, right).
The helpers with a record input accept plain record or JSON-object data only,
never follow prototypes, and never invoke callbacks. has distinguishes a present
nil own text field from an absent one; get returns its fallback only when
that own text field is absent. has, get, and pick do not traverse source
fields. pick accepts at most 4,096 string keys and returns a fresh shallow record of existing
requested fields in key-array order; missing fields are omitted. from_entries
accepts at most 4,096 exact [string, value] pairs, preserves first key
positions, and applies later duplicate values. with returns a fresh shallow
record with one string key updated or appended; an existing key keeps
its position, while a new key is rejected when the source already has 4,096
fields. omit accepts at most 4,096 string keys and returns a fresh shallow
record in source field order without matching own text fields; duplicate and
missing keys are ignored. It is a local denylist transform, not a boundary
whitelist: use pick before a tool boundary when unknown fields must be
excluded. omit, keys, entries, values, and merge shallowly process at
most 4,096 own text-keyed fields; entries returns fresh [text_key, value]
pairs in stored field order, and merge also rejects a combined source count
over that bound. len is
constant-time and uncapped. The module does not expose host state, filesystem,
process, network, clock, entropy, or crate access.
use mod.tool
let summary = tool.call("text.echo", "plan the release")
summary
The host, not the script, decides whether text.echo exists and what it can
access.
For work that should yield back to the host event loop, use an explicit
promise. The host runs at most one granted tool when it calls pump() (or a
bounded batch with pump_up_to).
use mod.tool
let summary = tool.start("text.echo", "plan the release").await()
summary
For a recovery-safe fallback, use canonical try/catch. Recovery does not
refund the call or imply that an adapter effect was rolled back.
use mod.tool
let summary = try {
tool.start("text.echo", "plan the release").await()
} catch {
"summary unavailable"
}
summary
Rust applications integrate their existing crate ecosystem by registering a narrow, policy-bound adapter for each effect. Splash does not import crates or ambient OS APIs directly.
JSON capabilities use object or array envelopes. Rust adapters can receive and
return serde_json::Value, or use the schema-required typed Serde bridge for
reviewed structs; Splash turns records and arrays into JSON with
tool.call_json or tool.start_json.
use mod.tool
use mod.std.assert
let response_json = tool.call_json("math.add", {left: 20, right: 22})
let response = response_json.parse_json()
assert(response.total == 42)
For a fixed reviewed adapter, a Rust host can instead register a bounded direct capability module and generated source can use decoded data directly:
use mod.arithmetic
use mod.std.assert
let math = arithmetic
let response = math.add({left: 20, right: 22})
assert(response.total == 42)
This is host-configured syntax over the same contract-enforced capability, not general module loading or direct crate access. It retains the target tool's policy, audit, and capability-lease checks. See Host Tool Catalog.
The development CLI registers this reviewed facade together with its math.add
demo capability:
cargo run -p splash-cli -- run --allow-json-add examples/direct_module_workflow.splash
cargo run -p splash-cli -- module-catalog --allow-json-add
cargo run -p splash-cli -- tool-calls --allow-json-add examples/direct_module_workflow.splash
cargo run -p splash-cli -- workflow-review --allow-json-add examples/direct_module_workflow_draft.json
cargo run -p splash-cli -- workflow-run --allow-json-add --grant calculate:math.add:1 examples/direct_module_workflow_draft.jsonThe catalog maps the arithmetic.add facade to math.add; workflow policies
and leases continue to grant the underlying math.add capability, never the
facade name. Review can preserve that mapping through a bounded exact local
root alias such as let math = arithmetic; it never treats an alias as a new
module, target tool, or grant.
Hosts can also register a with_deferred_method facade over a reviewed JSON
tool; its explicit mode: "deferred" returns the existing bounded promise and
await() yields decoded JSON while preserving the same underlying grant.
When the reviewed module catalog is configured, tool-calls and
workflow-review add advisory direct_module_calls entries that expose this
mapping for LLM and operator review. Those entries do not grant a tool or
replace the explicit calculate:math.add:1 workflow policy above.
cargo run -p splash-cli -- run --allow-echo examples/tool_workflow.splashThe deferred example is runnable with:
cargo run -p splash-cli -- run --allow-echo examples/deferred_tool_workflow.splashThe JSON dataflow example is runnable with:
cargo run -p splash-cli -- run --allow-json-add examples/json_tool_workflow.splashexamples/makepad_ui_counter.splash is a
small current-style Makepad UI body retained as a parser compatibility fixture.
It is deliberately not runnable through splash-cli: the standalone runtime
does not install Makepad widget modules, an event loop, or the ui handle.
The canonical workflow profile continues to reject it; trusted UI hosts can
use splash_core::check_vm_compatibility_named, which enforces source, token,
and delimiter-nesting bounds, before they install their own bindings. See
Makepad UI compatibility for the current
upstream example distinction and the exact boundary.
Inspect the exact demo-tool catalog supplied to an LLM host with:
cargo run -p splash-cli -- catalog --allow-echo --allow-json-addBefore generating source, an LLM host can query the versioned canonical language contract without creating a runtime or registering a tool:
cargo run -p splash-cli -- profileThe JSON response identifies the profile and grammar path, reports the active default bounds, and states the tool and workflow authority boundary. It is not a tool catalog, capability grant, or substitute for the normative Splash Grammar v0.2. Query the host's separate catalog before proposing effectful calls.
For an LLM-generated ordered workflow, query the bounded draft producer schema before writing its JSON envelope:
cargo run -p splash-cli -- workflow-schemaThe schema describes only format_version and ordered id/source steps,
including the decoder's limits. It deliberately has no fields for capabilities,
approvals, contracts, checkpoints, results, or external-operation handles;
review the resulting file with splash workflow-review before the host plans
or approves anything.
Validate generated source against the canonical Splash v0.2 profile without creating a capability host or running any bytecode:
cargo run -p splash-cli -- check examples/deferred_tool_workflow.splashThe command emits JSON diagnostics and exits nonzero for invalid source, including Makepad compatibility syntax outside the portable contract. The portable source contract is Splash Grammar v0.2.
Inspect valid top-level declarations without evaluating source or constructing a capability host:
cargo run -p splash-cli -- outline examples/json_tool_workflow.splashThe command emits JSON with function and let declarations plus UTF-8 byte
spans for each declaration and identifier. Invalid source still emits the
structured syntax diagnostics and exits nonzero with an empty declaration list.
Inspect direct source-level tool.call, tool.start, tool.call_json, and
tool.start_json sites before requesting approval:
cargo run -p splash-cli -- tool-calls examples/json_tool_workflow.splashThe command emits JSON locations plus a literal tool name when the first
argument is directly written as a string. It never evaluates source or creates
a capability host. It is a review aid only: aliases, shadowing, control flow,
and computed names remain unresolved, so the host must still issue a lease and
the runtime must authorize every actual call. The output retains at most 1,024
direct sites and sets tool_calls_truncated when later sites were omitted. A
host can additionally expose its reviewed direct-module mapping; the
development demonstration does so only when --allow-json-add is present and
emits a separate advisory direct_module_calls list. That mapping can follow a
bounded exact local root alias of a visible direct import, but never computed
receivers, member aliases, or source-derived authority.
Review an LLM-generated multi-step draft before it becomes a host-owned plan:
cargo run -p splash-cli -- workflow-review examples/release_workflow_draft.jsonThe versioned JSON draft contains only step IDs and source. Review output
includes per-step syntax status and direct tool-call hints, never grants or
approvals. Each step reports tool_calls_truncated when its direct-call review
was capped; a workflow retains at most 4,096 hints across all steps. See
workflow drafts for its bounds and host lifecycle.
With an explicitly configured host module catalog, a separate advisory
direct_module_calls list can map a direct facade call, including a bounded
exact local root alias, to its underlying tool; it has the same 4,096
workflow-wide cap and never selects a grant.
Run the bounded local demonstration catalog only with explicit host-selected per-step grants:
cargo run -p splash-cli -- workflow-run --allow-echo --allow-json-add \
--grant prepare:text.echo:1 --grant calculate:math.add:1 \
examples/local_workflow_draft.jsonRun the bounded dataflow example with explicit input and one reviewed grant:
cargo run -p splash-cli -- workflow-run --allow-json-add \
--input examples/dataflow_input.json \
--grant prepare:math.add:1 \
examples/dataflow_workflow_draft.jsonThe prepare step explicitly narrows the host input to the reviewed
math.add envelope with object.pick. The pure summarize step then uses a
dynamic own-field fallback lookup, bounded indexed-array lookup with an
empty-input fallback, a
bounded array transformation and loop, text normalization, own-field record
merging, and a bounded JSON round trip. It receives no tool grant; only
prepare can issue the reviewed effect.
workflow-run accepts only the two opt-in local demo adapters and prints a
structured execution/audit summary. It never derives grants from source hints,
opens filesystem/network/process authority, or supports external workers. A
production host must construct its own reviewed catalog and policy. With
--input, the direct result also contains raw dataflow input and outputs for
local inspection; the audit and workflow event views never do.
For production dataflow, a host can additionally bind compiled input and
per-step output schemas through WorkflowDataContract. Those schemas are
trusted application configuration, not draft or checkpoint fields; a failed
output contract stops the workflow before a later authorized step runs. Use the
paired contract-aware checkpoint/resume APIs to keep that policy across a
restart. See
workflow drafts and
workflow checkpoints.
Format valid canonical source without creating a capability host or rewriting the input file:
cargo run -p splash-cli -- format examples/deferred_tool_workflow.splashUse --check in an editor or CI workflow to require the canonical formatting
result without printing it:
cargo run -p splash-cli -- format --check examples/deferred_tool_workflow.splashRun the language server from an LSP-compatible editor with:
cargo run -p splash-lspIt accepts client-provided open-document text plus optional bounded advisory
initialization metadata and configuration refreshes, retains at most 128
document states and no document text above the standard 256 KiB source cap,
and provides full-sync diagnostics,
whole-document formatting, and top-level declaration symbols plus bounded
same-document lexical definition/reference requests, binding-kind hover, symbol
highlights, lexical completion, and guarded rename. Ordinary lexical completion
is offered only while the cursor is within or at the end of an expression-position
identifier. It returns the complete retained set of bindings visible at that
token, lets the client filter it, and supplies an exact replacement edit for the
identifier. Invalid source is eligible only at a site ending before the first
syntax diagnostic. Candidate occurrences and completion sites have independent
4,096-entry bounds; either truncation marks the LSP result incomplete. A
truncated site list can still serve a retained site, but a truncated symbol set
returns no candidates because an omitted inner definition could shadow a
retained outer binding. Rename is advertised only when the client supports
versioned documentChanges; every edit is bound to the exact open-document
version. It rejects truncated indexes, import path changes, invalid identifiers,
and rewrites that change the complete indexed lexical binding report. It never
reads a document URI, evaluates source, loads or resolves arbitrary imported
modules, creates a capability host, or loads a Rust adapter. For an exact,
lexically visible use mod.tool binding, it additionally suggests only the
fixed call, call_json, start, and start_json methods at a direct
tool. member site. For an exact visible use mod.std.math binding, it also
completes the documented fixed scalar functions and pi/e constants at a
direct math. member site, with plain-text hover and function signature help.
For an exact visible use mod.std.assert binding, it also provides fixed
plain-text hover and signature help for assert(condition); use mod.std
supports the same fixed signature at direct std.assert(...). These fixed
surfaces use no tool-catalog or adapter lookup, do not follow local aliases,
and do not imply a capability grant. For an exact visible use mod.std.json
binding, it completes parse and stringify with fixed plain-text hover and
signature help. For an exact visible use mod.std.text binding, it completes
the fixed text functions, including Unicode-scalar slice, index_of, and
last_index_of,
literal split, and string-array join, with plain-text hover and signature
help. For an
exact visible use mod.std.array binding, it completes len, has_index,
get, contains, index_of, slice, range, concat, compact,
reverse, unique, flatten, and push with the same fixed plain-text hover and
signature help.
For an exact visible use mod.std.object binding, it completes len, has,
get, pick, omit, from_entries, with, keys, entries, values,
and merge with the same fixed plain-text hover and signature help. At a
statement-position use mod. path, the same static projection completes std;
below use mod.std. it completes array, assert, json, math, object,
and text.
The frozen mod.std subtree cannot be extended by advisory catalog metadata. An
integration may additionally supply a
advisory tool-catalog projection through
initializationOptions.splash.toolCatalog or a later
workspace/didChangeConfiguration update; it accepts the name, format,
and description fields from the host catalog JSON. For an exact visible
mod.tool binding, the LSP completes the first string literal in direct
call/start calls from text entries and direct call_json/start_json
calls from JSON entries. It never connects to a capability runtime, reads a
catalog file, or derives a grant from this metadata. The projection is bounded
to 128 entries, 512 KiB of retained names and descriptions, 128-byte names,
and 4 KiB descriptions; malformed, duplicate, or oversized input is discarded
as a whole and marks that completion result incomplete. The lexical service
also recognizes an exact visible direct let binding = { ... } initializer.
At binding.field, a direct two-level literal path such as
binding.child.grandchild.field, or through an exact
let alias = binding, let alias = binding.child, or
let alias = binding.child.grandchild chain of at most 16 hops with at most
two alias child selections in total, whether carried by one edge or spread
across a chain, it offers the literal field names and supports hover and
definition to the field key. Alias targets resolve at their source position, so
lexical shadowing remains intact. This metadata has
1,024-shape, 4,096-field, and 1,024-direct-alias bounds. An omitted alias edge
makes retained record completion empty and incomplete and disables static field
hover and definition. The LSP suppresses a shape after an earlier direct write
or potentially mutating member, index, call, or escape path through the root or
any retained root, child, or grandchild alias that resolves to it. It does not infer
parenthesized or computed aliases, parenthesized or computed child values,
alias or member paths beyond that two-level budget, assignments, control flow,
function returns, imported values, or runtime data.
It otherwise remains conservative: it does not infer forward references,
general types, arbitrary record fields, builtins, arbitrary catalog data, or
runtime-derived imported-module exports.
An editor may also supply a separate advisory module-interface projection
through initializationOptions.splash.moduleCatalog or a later
workspace/didChangeConfiguration update. It completes the current segment in
a direct statement-position use mod.* path and bounded catalog paths after a
direct, visible imported module binding or a stable exact local root-alias
chain, and gives an exact catalog leaf a
plain-text advisory hover. An exact leaf that explicitly declares both a mode
and single_json call shape also receives a bounded one-value signature; the
server never infers a signature from a mode alone. It does not load a source
file, resolve a module, inspect a runtime export, or override the fixed
mod.tool API. Tool and module catalog keys refresh independently: an omitted
key keeps its prior value, JSON null explicitly clears it, and a malformed or
over-limit key value makes only that catalog unavailable. A malformed settings
value or non-object settings.splash clears all advisory catalogs. Neither
projection authorizes source. Module aliases must be exact let alias = binding
chains of at most 16 hops with complete source metadata and no write, member
extraction, parenthesized/computed edge, or other value escape in their resolved
group; otherwise catalog metadata fails closed. This does not extend the fixed
mod.tool API, whose editor support remains direct-import-only. See editor
module-interface projection for its exact format and bounds. A
truncated lexical index can still serve retained, sound definitions and hover,
but exhaustive reference, highlight, and rename requests fail instead of
returning a partial set.
For a host-managed dataflow authoring session, an editor can also supply a
separate initializationOptions.splash.workflowDataCatalog projection. It
completes direct unshadowed workflow.input.* and
workflow.outputs.<stepId>.* paths and hovers known field metadata. A host
using splash-workflow can generate a validated current-prefix update from a
suspended contract-bound continuation or checkpoint; the LSP itself still does
not load schemas or runtime state. It does not validate data, approve a
workflow, issue a lease, or make an adapter callable;
missing metadata does not create a workflow namespace, and malformed input
fails closed. A host may provide workflowDataStepContext to structurally bind
one projected current step and its prior projected output prefix, which filters
output completion and hover. It may later replace a complete catalog/context
pair through workspace/didChangeConfiguration; a relevant malformed or
partial refresh makes workflow metadata unavailable rather than retaining a
stale projection. A terminal or unavailable runtime state can atomically clear
both keys with JSON null. See editor workflow-data projection.
splash-core: bounded VM wrapper and diagnostics.splash-capabilities: explicit tool policy, cursor-safe bounded audit export with a feature-gated authenticated durable journal, deferred promises, LLM-facing host catalog, approval-bound capability leases, JSON contracts, fixed-file and feature-gated HTTP endpoint/origin catalogs, aggregate catalog limits, safe host bridge, and a sealed static-catalog mobile/embedded profile.splash-schema: bounded executable JSON-schema subset for tool contracts.splash-storage: host-only authenticated records, rollback protection, and fenced compare-and-swap backend boundary, plus an optional anchored SQLite payload adapter that requires a platform trust anchor and a bounded transactional-service anchor client; neither substitutes for the deployed trust authority.splash-protocol: portable worker messages, capability attenuation, fixed 128-grant manifest and 1,024 retained-request-identity session bounds, keyed session framing, instance-bound in-process authorization tokens, strict ordinary-call cancellation, and host-side invocation/result validation.splash-worker: worker-side session runtime, explicit Rust adapter registry, cancellable ordinary-invocation driver, capability-bound secret-broker contract, and authenticated journal-store bridge; it is not an OS sandbox or platform storage backend.splash-sandbox: target-specific worker containment policy; its initial Bubblewrap backend is Linux-only and deliberately narrow, with bounded manifest-selected ephemeral file roots for scratch data.splash-workflow: host-owned planning, lease-bound approval, bounded JSON dataflow, bounded in-memory and authenticated durable event and cross-stream telemetry journals, host-receipt-order aggregation, checkpointing, durable operation records, optional fenced Bubblewrap post-stop reconciliation, a multiplexed-worker completion sink, sequential execution, and a sealed mobile/embedded workflow facade for static local adapters and direct capability modules.splash-cli: local development CLI.splash-lsp: host-only stdio diagnostics, canonical formatting, top-level declaration symbols, and bounded same-document lexical navigation, hover, and highlights plus lexical completion and version-bound guarded rename for open editor documents.vendor/makepad: provenance-preserving compatibility import.
See SECURITY.md for the current threat model and UPSTREAM.md for the import boundary. The worker protocol defines the handoff to contained adapters. The host tool catalog defines safe discovery for an LLM orchestrator. JSON tool contracts define the executable structured-data boundary. External tools define the host-managed async boundary.
Fixed-file catalogs define the narrow descriptor-pinned local text-file boundary.
HTTP endpoint and origin catalogs define the narrow host-selected outbound JSON boundary, endpoint- and origin-bound credential injection, and their explicit non-guarantees.
Editor module-interface projection defines bounded
refreshable authoring metadata for host-defined mod.* interfaces.
Worker protocol v5 also defines keyed worker frames and the live-operation reconciliation boundary.
Workflow checkpoints define the durable host-orchestration boundary.
Durable workflow events define the authenticated telemetry replay boundary, which deliberately remains separate from recovery authority.
Capability audit export defines the contiguous host-export cursor, optional authenticated durable journal, and explicit observability-gap behavior.
Cross-stream telemetry defines bounded in-memory and authenticated durable host-receipt-order aggregation of source telemetry without creating recovery or capability authority.
Workflow drafts define the untrusted LLM-plan interchange and review boundary before a host-owned approval.
Positioning and feasibility compares Splash with its Makepad substrate and defines the realistic boundary for Python/JavaScript replacement claims.
Durable operation ledgers define how a host records and safely reconciles uncertain external effects across a restart.
Authenticated storage defines the trusted durable record boundary used to persist those host-owned records.
Transactional rollback-anchor service defines the bounded client protocol and embeddable server dispatcher for a separately trusted durable CAS authority.
Worker durable operations define the contained worker-side replay and persistence boundary for effectful operation keys.
Durable worker compensation defines the host-approval, worker-journal, and crash-recovery rules for one explicit inverse effect.
Worker adapter runtime defines the worker-side Rust adapter boundary and the integration requirements for a contained backend.
Linux Bubblewrap workers define the first contained worker launcher, its capability mapping, and its explicit non-guarantees.
Bubblewrap post-stop recovery defines the reaping, fresh-session reconciliation, and fenced host-ledger commit sequence.