A Lace runtime owns participants, validated record storage, interlace driving, and app-facing operations. This guide helps choose the smallest current runtime and grow from local memory to durable and connected use. Policy remains a separate input: it selects checked records for convergence.
For the shared cross-runtime contract, use Lace-100 · Developer Surfaces. This page is a path through that surface, not another API specification.
Public first contact currently uses a Lace source checkout:
git clone https://codeberg.org/rs0/lace.git
cd lace
The repository can build local native archives and an installable
JavaScript package archive. These are local release artifacts; they do
not imply that @lace/lace-js is published in the npm
registry. See packages/README.md
for artifact commands and contents. Pin the exact artifacts an
application tests: Lace is an early draft with no compatibility
guarantee.
| Application host | Start with | Current guide or companion |
|---|---|---|
| Rust, synchronous/local | lace-single with Lace::empty_memory() |
First interlace |
| Rust on Tokio | lace-tokio |
rust-tokio-tcp-fs |
| Browser | @lace/lace-js/browser |
shared-todo-list |
| JavaScript without browser storage | @lace/lace-js memory runtime |
First interlace and the @lace/lace-js
package guide |
| Node with durable storage | @lace/lace-js/node |
@lace/lace-js
package guide |
| Python application or automation | repository-local Python package | First interlace and the Python runtime guide |
| Persistent interlace endpoint | laced |
laced(1)
and laced.conf(5) |
| Record-oriented shell work | lace CLI |
CLI guide |
Python remains active work in progress; its README identifies implemented and not-yet-implemented surfaces. Do not choose it on the assumption that it already matches every transport available in Rust.
laced is a persistent Lace participant and interlace
endpoint. It is not a static-file host, application command server, or
generic database server. Serve browser assets and application APIs with
ordinary application infrastructure.
Before adding a filesystem, browser persistence, or transport, prove one useful application action locally:
Record;store porcelain;list or get;
andThe first-interlace tutorial
extends that local proof to two memory participants. The smaller quickstart-local
companion focuses only on record construction and
store/list/get in JavaScript and
Rust.
Normal application code uses these shared operations:
store(target, records)
list(target, selector)
get(target, selector)
forEach(target, selector)
boundedInterlace(first, second, policy)
openInterlace(first, second, policy)
Bindings use language-appropriate spelling, but the semantics come
from Lace-100. Bounded calls return a terminal plus ordered
observations; list and get preserve partial
captures. Open handles use
next_event/nextEvent as their one-event
primitive, while iteration and
run_until_fixed_point/runUntilFixedPoint are
thin consumers. Observer streams yield owned RecordSaved
records plus FixedPoint and Closed. These
operations are interlace-backed porcelain; they are not raw store
insertion or iteration.
Choose durability only after the record shape and selected view work in memory:
| Host | Durable choice |
|---|---|
| Native Rust | Lace::filesystem(...) |
| Browser | Lace.opfs(...); use openBestEffort(...)
only when an explicit demo fallback is acceptable |
| Node | Lace.filesystem(...) from the Node entry point |
| Python | Lace.filesystem(...), within the runtime’s current
support |
| Standalone service | a laced DataDir |
A store change should not change the application record family or turn policy into a database query language. Test close/reopen behavior and verify the same public selected view after reopening.
For a complete native durability companion, run:
cargo run --manifest-path examples/rust-tokio-tcp-fs/Cargo.toml
It uses two filesystem-backed Tokio Laces and TCP/ILTP. The storage and transport are more realistic, but the participant, record, policy, and porcelain model is the same as the memory tutorial.
Use direct in-process interlace while both participants are in one runtime. Use a connected peer handle when another process or host owns one participant. Connected addresses and transport behavior are defined by Lace-060 · ILTP.
Policy belongs to the operation, not the participant handle:
| Participants | Policy argument |
|---|---|
| Two local Laces | Used for both operands |
| Local Lace and connected peer | Local operand; the peer supplies the other |
Current convenience connectors vary by runtime:
| Runtime | Connected client routes |
|---|---|
| Native Rust | tcp:, unix:, ws:;
quib: when lace-tokio/quib is enabled |
| Browser and Node JavaScript | ws:, wss: |
| Python | ws:, wss: |
lace-tokio opens client handles with
Lace::connect(...).await. For a server, the application
binds and accepts its listener, adapts one established stream as
AcceptedIltpTransport, constructs a one-use peer with
LaceHandle::from_accepted_iltp(...), and spawns an ordinary
interlace. Lace does not own listener admission or task supervision. The
synchronous lace-single binding uses the same client
constructor without .await:
Lace::connect(...).
ILTP can be hosted over other standard byte carriers, including
stdio, but a protocol binding does not imply that every
runtime supplies a high-level connector for it.
Choose operation lifetime deliberately:
Both use the same policy and checked-record semantics. A fixed point means neither side requested another record in the settled round; it does not mean the stores are identical.
Add connected operation in this order:
The browser/daemon shape is demonstrated by shared-todo-list.
It deliberately uses ordinary static hosting beside a WebSocket
laced endpoint.
laced only when a service is neededUse laced when a persistent waypoint or background
participant is actually part of the application. A minimal deployment
has one data directory, one explicit listener or sync job, and ordinary
Interlang/Datalog policy.
Before running it beyond a local smoke test:
laced(1)
and laced.conf(5);laced check-config;laced;Assistant-driven operational work can use the repository’s setup-laced
skill, but the manpages and numbered specs remain authoritative.
A useful review question is: did this change where records live and how peers meet, or did it change which checked records the policy selects for convergence?
selected/{*} to published/{*} is a policy
change.Continue with Interlang policy authoring
for the latter two. For larger implemented application shapes, browse incubator/, but treat
those as experiments rather than one architecture to copy wholesale.