Node.js bindings for actnet's app-core — the Rust client library that implements account creation, end-to-end encrypted messaging, and group flows on top of libsignal + SQLCipher.
This package is a thin TypeScript layer over a napi-rs cdylib. Bots and server-side automation talk to a homeserver through this API the same way the iOS app does.
This crate isn't on npm yet; build from the actnet checkout:
# from repo root
make node # release build (~30s clean)
make node-debug # faster turnaround during development
# or, inside node/
npm install
npm run build
Outputs:
node/native/app-core.<triple>.node — the native addonnode/native/index.{js,d.ts} — auto-generated napi binding (raw types)node/dist/index.{js,d.ts} — the user-facing wrapper (use this)Temporal global. Standard nodejs.org binaries include it. Some distro images (notably node:26-alpine without the Rust toolchain at build time) ship without Temporal; the wrapper itself loads fine, but any method that takes or returns a Temporal.Instant will throw.x86_64-apple-darwin, aarch64-apple-darwin, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu. Windows isn't wired up.import { initLogging, AppCore } from "@actnet/app-core";
initLogging("info");
const core = await AppCore.createAccount(
"https://homeserver.example",
"/var/lib/echobot/store.db",
"", // SQLCipher passphrase (optional)
new Uint8Array(0), // no recovery blob for a bot
"EchoBot",
);
console.log("registered as", core.did());
for await (const e of core.events()) {
if (e.kind !== "message") continue;
await core.sendDm(e.message.senderDid, `you said: ${e.message.body}`);
}
*At field is a Temporal.Instant. Use Temporal.Now.instant() to produce them; Number(i.epochMilliseconds) to drop down to a raw number when you must.sendDm / sendGroupMessage take a body: string; received messages expose body: string. UTF-8 conversion happens at the wrapper boundary. The raw plaintext: Uint8Array is also available on DecryptedMessage for cases where a future application encodes binary payloads.Uint8Array. Inputs accept any Uint8Array; Buffer passes through zero-copy as it's already a subclass.deliveryStatus is "sending" | "sent" | "delivered" | "read"; group role is "member" | "admin". Conversions happen at the boundary.events() (and the underlying nextEvents()) is single-consumer; run from exactly one async loop.Error objects with the AppErrorFfi reason in .message.cd node
npm run docs # writes ./docs (open docs/index.html)
The browsable reference is generated from the JSDoc on src/index.ts via TypeDoc.
| Path | Description |
|---|---|
src/index.ts |
The TypeScript wrapper (what users import). |
native/ |
Generated napi-rs glue + the .node addon. |
dist/ |
tsc output (the published main/types). |
../core/crates/app-core-node/ |
The Rust crate that produces the addon. |
../core/crates/app-core/ |
The underlying client library (Rust). |