jolt-sdk · TypeScript
Build apps that
borrow an identity.
Define typed application data with Schema Classes, compose it through App.create, then connect or test it through one generated interface. Jolt owns identity, signed storage, paths, compatibility, and scoped approval. The low-level client remains available when an application needs explicit protocol control.
01 / Install
One package. Five entry points.
yarn add jolt-sdk
The SDK also ships with every Jolt release as an installable tarball, if you prefer pinning to a release directly:
yarn add https://github.com/alexanderwanyoike/jolt/releases/latest/download/jolt-sdk.tgz
| Import | What it is |
|---|---|
jolt-sdk/data | Beginner-first Schema Classes and typed application data APIs |
jolt-sdk | Types, typed errors, and createJoltClient: tolerant, domain-shaped operations |
jolt-sdk/transport-http | fetch-based transport for browsers and Node.js 18+ |
jolt-sdk/transport-tauri | Tauri invoke-based transport for desktop shells |
jolt-sdk/testing | createFakeJolt: a deterministic in-memory fake for tests |
02 / Beginner Chirp
Describe the app. Jolt handles the machinery.
A Schema Class is both your TypeScript type and runtime validation. App.create derives paths and access; Chirp.connect() handles compatibility, host selection, and approval.
import { App, Collection, Field, Read, Schema } from "jolt-sdk/data";
@Schema({ version: 1 })
class Post {
@Field.string()
text!: string;
@Field.dateTime()
postedAt!: Date;
}
const Posts = Collection.create(Post, {
access: {
read: Read.AnyIdentity,
create: true,
update: true,
delete: true,
restore: true,
},
});
const Chirp = App.create({
id: "chirp.example",
name: "Chirp",
namespace: "chirp",
data: { posts: Posts },
});
const chirp = await Chirp.connect();
const post = await chirp.posts.create({
text: "Hello, Jolt!",
postedAt: new Date(),
});
Chirp.test() exposes the same typed API in memory. Follow the complete compile-checked Beginner Chirp guide.
03 / Advanced low-level client
Session in, signed content out.
This explicit session, transport, path, and decoder flow is for advanced applications. Most applications should begin with jolt-sdk/data.
An app declares who it is and exactly what it wants to do. The daemon holds the request until the user approves it in Jolt Console; the app polls until the approval carries a bearer token, then every call is checked against the granted capabilities.
import { createJoltClient } from "jolt-sdk";
import { HttpTransport } from "jolt-sdk/transport-http";
let token = "";
const jolt = createJoltClient({
transport: new HttpTransport({ daemonUrl: "http://127.0.0.1:9862" }),
getSessionToken: () => token,
});
// 1. Ask for a scoped session; the user approves it in Jolt Console.
const status = await jolt.getStatus();
const request = await jolt.requestSession({
appId: "myapp.local",
appName: "My App",
appOrigin: window.location.origin,
identity: status.identity_address,
capabilities: ["publish:/myapp/*", "resolve:public", "fetch:public"],
});
// 2. Poll until approved.
for (;;) {
const s = await jolt.getSessionRequestStatus(request.request_id);
if (s.session_token) { token = s.session_token; break; }
await new Promise((r) => setTimeout(r, 1000));
}
// 3. Publish signed content and read it back, versioned and decoded.
await jolt.publishJson("/myapp/profile", { name: "Alice" });
const profile = await jolt.read(
{ identity: status.identity_address, path: "/myapp/profile" },
(v) => (typeof v === "object" && v && "name" in v ? (v as { name: string }) : null)
);
Reads are tolerant: missing, unreachable, or undecodable content returns null instead of throwing, so one bad record never poisons an app projection. Failures from publishes and sends throw JoltApiError (the daemon answered with an error) or JoltTransportError (the daemon was unreachable); every operation accepts { signal, timeoutMs }.
04 / Transports
Same client. Two ways to the daemon.
Everything above the transport (operations, client, domain types) is identical in the browser, Node.js, and Tauri. Only the last hop changes.
HttpTransport talks to the daemon over fetch. Point it at the daemon directly, or use HttpTransport.viteProxy() behind a dev-server proxy to dodge CORS.
import { HttpTransport } from "jolt-sdk/transport-http";
const transport = new HttpTransport({
daemonUrl: "http://127.0.0.1:9862",
});
Daemon calls go through audited Rust proxy commands, so the webview never needs network access. The tauri-plugin-jolt plugin ships those commands; wiring it up is three lines:
- 01Add the crate and register it:
.plugin(tauri_plugin_jolt::init()) - 02Grant the
jolt:defaultcapability in your Tauri capabilities file. - 03Construct the transport:
new TauriTransport({ plugin: true })
05 / Testing
A whole daemon, faked in memory.
createFakeJolt returns a fully working JoltClient with no daemon and no network: publishes land in an in-memory store, reads resolve against it, sends are recorded, and incoming envelopes can be injected. Your tests exercise your schemas and flows, not HPKE.
import { createFakeJolt } from "jolt-sdk/testing";
const { client, sent, deliverIngress } = createFakeJolt("alice.jolt");
// client satisfies JoltClient and all of its sub-interfaces; sends are
// recorded in `sent`, and deliverIngress() injects incoming envelopes.
06 / Documentation
Where to go next.
APIGenerated from TSDoc · four modules
SDK reference
Every exported class, interface, function, and type: the client and its sub-interfaces, typed operations, wire DTOs, both transports, and the testing fake.
Beginner tutorial · Data SDK
Build Chirp
Define a typed social app with Schema Classes, connect it, mutate posts, and test Alice and Bob without a daemon.
Fundamentals · Data SDK
Understand the Data SDK
Learn Schema Classes, Resources, Apps, Items, access, testing, and where deterministic migrations fit.
Fundamentals · Data SDK
Migrate stored schemas
Upgrade historical values through small deterministic steps while application code keeps one current Schema Class.
Fundamentals · Data SDK
Change an Item
Update, replace, delete, and restore immutable Items while handling lifecycle states and expected errors by type.
Advanced · Data SDK
Resolve Manual conflicts
Opt into application-owned choices between concurrent alternatives only when the automatic defaults are not enough.
Fundamentals · Data SDK
Keep remote data current
Open a retained Data Subscription and receive local Change Stream events without writing a polling loop.
Fundamentals · Data SDK
Test without a daemon
Use the same typed App interface for isolated tests, Alice and Bob journeys, and deliberate concurrency cases.