TypeScript

Browsers and Node 22 or later. The protocol, the sessions and the persisted state are the same Rust the native heads run, compiled to WebAssembly; the TypeScript around it fetches the service document, opens one WebSocket per service, and gives the client a JavaScript shape. A page needs nothing but HTTPS.

Not on npm yet: the package is built from the repository. Everything else on this page runs today, and the repository's own test runs it in Node on every commit.

Build the package

From a checkout of the tacenta repository, with Rust, the wasm32-unknown-unknown target, wasm-pack and Node installed:

cd sdk/typescript
npm install
npm run build:wasm
npm run build

That leaves the package in place, ready to install into an app with npm install ../path/to/tacenta/sdk/typescript.

Send a message

import { Tacenta } from "@tacenta/sdk";

const tenant = await Tacenta.connect("tct_your_api_key");
await tenant.signUp("alice", "correct horse");
await tenant.signUp("bob", "correct horse");

const alice = await tenant.signIn("alice", "correct horse");
const bob = await tenant.signIn("bob", "correct horse");

const toBob = await alice.find("bob");
if (toBob) await alice.send(toBob, "hello from the browser");

for await (const m of bob.inbound()) {
  console.log(m.from, m.text());
}

inbound() is the client's messages one at a time as they arrive; receive() is the same as batches. A pending one does not hold the client, so a send from elsewhere goes through meanwhile. tacenta init typescript writes this sample, with your key in place, as a project.

connect fetches the server's service document from https://tacenta.com/.well-known/tacenta and holds it to the same rule the native client applies: over HTTPS it may not turn encryption off or send your users anywhere but the domain it came from. For a self-hosted server pass { server: "chat.example.org" }; for a local development gateway, { documentUrl: "http://127.0.0.1:4780/.well-known/tacenta" }.

Keep the identity across runs

A client's identity and its live sessions can be exported and resumed, so a conversation continues after a reload rather than starting over. Store the bytes where your app keeps secrets: IndexedDB in a page, a file or a keychain in Node.

const state = await alice.exportState();
// later, on the next run:
const again = await tenant.signInWithState("alice", "correct horse", state);

What this head does not have yet

Sealed state
The rollback-resistant state path and the secure-store hook the Swift and Kotlin heads have are not on this head yet.
A published package
Build it from the repository for now; the shape above is the shape that will publish.

Reference

The generated reference, TypeDoc of the package, regenerated at every release from the code it documents: tacenta.com/dl/reference/typescript/.