@econ-v1/app-sdk
v7.6.168
Published
TypeScript SDK for building Node mini-app plugins (Bun runtime) on the Lightning-powered marketplace daemon
Maintainers
Readme
@econ-v1/app-sdk
Status:
1.0.0-experimental— API surface is frozen pending validation by at least two first-party packages. Breaking changes between1.0.0-experimental.*releases are possible. Pin to an exact version in production.
TypeScript / Bun SDK for building Node-App plugins for the Node Lightning-powered marketplace daemon. Apps run as separate Bun processes and talk to the host over a Unix-domain socket using a JSON-newline protocol.
What it gives you
- An abstract
NodeAppclass — extend it, declare metadata, and implement the hooks you need. - A declarative
route()helper — register HTTP handlers with built-in scope checks (defense-in-depth on top of the host's manifest enforcement). invokeCapability()— call any capability registered with the host's capability router.publishEvent()— publish namespaced domain events to the host event bus.host.{trace,debug,info,warn,error}— file-backed structured logging.
Quick start
bun add @econ-v1/[email protected]import { NodeApp, runNodeApp, type AppRequest, type AppResponse } from "@econ-v1/app-sdk";
class MyApp extends NodeApp {
readonly metadata = {
name: "my-app",
version: "0.1.0",
author: "Me",
description: "Hello-world Node-App",
capabilities: ["http_handler"],
};
async init() {
this.route("GET", "/hello", { requiredPermissions: [] }, async () =>
this.json({ hello: "world" }),
);
}
}
runNodeApp(new MyApp());The host launches the Bun process, supplies a Unix socket via the NODE_APP_SOCKET environment variable, and routes proxied HTTP requests, events, and capability calls over the socket.
Burger apps
The same package serves apps that run on Burger (app_type: "burger"), and
it carries their toolchain, so a Burger app depends on @econ-v1/app-sdk
and nothing else from @econ-v1:
| Part | How an app uses it |
|---|---|
| burger-build | The package's bin: burger-build build src/index.ts --outdir dist and burger-build test-prepare … (see sdk/burger-build/README.md). node-app build / node-app test run it for you. Needs Bun ≥ 1.4.2. The same code is importable as @econ-v1/app-sdk/burger-build (buildApp, prepareTests). |
| burger-types | "types": ["@econ-v1/app-sdk/burger-types"] in tsconfig.json: the burger:* modules, their node:/bun: aliases and the runtime globals. @econ-v1/app-sdk/burger-types/modules declares only burger:*. |
Both are built from sdk/burger-build and sdk/burger-types in this
repository by bun run build (scripts/build-burger-toolchain.ts).
Bundling with burger-build selects the burger export condition,
which resolves @econ-v1/app-sdk to dist/burger/index.js: identical API,
but the host channel is burger:host (framing and JSON handled by the
runtime), memory samples come from the QuickJS runtime, and there is no
worker-thread detection. Differences an app can observe:
openDatabase()defaults to a 256 KiB page cache (Burger's cap) instead of 1 MiB.- Telemetry stays inactive (
telemetryActive()isfalse): Burger Phase 1 has nofetchfor an OTLP exporter. heap_snapshot_requestis answered withsuccess: false.- On node-app-burger >= 0.3.3 a nested
invokeCapabilitycarriesinvocation_context_idhowever manyawaits separate it from the handler, as on Bun: the SDK loadsburger:async_hooks(ADR 004) with a dynamic import and uses itsAsyncLocalStorage. On an older runtime that import fails quietly and the Phase 1 behaviour stays: only calls made before the handler's firstawaitcarry the id, later ones are classified by the host as background work, never as foreground (Burger Contract C6,docs/superpowers/plans/2026-09-17-burger-00-overview-and-contracts.md). The same SDK therefore loads on every runtime version. Rebuilding an app on this SDK changes its nested calls on 0.3.3: they now carry the caller's context, so the host judges them under that caller (and rejects them withcapability.invalid_invocation_contextif the caller's context has already closed). Run the app's suite on 0.3.3 before releasing it.
Burger apps bundle the SDK into dist/index.js; nothing is shared at runtime.
Building for production
@econ-v1/app-sdk is itself a shared dependency in the Node deb (installed
once at /usr/lib/node/shared/node_modules/@econ-v1/app-sdk), so it's not
bundled into your app's dist/index.js. The build pipeline handles this
automatically — you don't have to do anything special.
Your app's other deps land in one of three buckets:
| Where | When |
|---|---|
| Bundled into dist/index.js (default) | Pure-JS deps; tree-shaken + minified |
| Shared at /usr/lib/node/shared/node_modules/ | Dep is in the curated SHARED_EXTERNALS list (LLM SDKs, this SDK) |
| Private at <your-app>/node_modules/ | You opt in via manifest.json#nodeApp.privateRuntime (native bindings, version overrides) |
Opt into a private dep by editing your manifest.json:
{
"name": "my-app",
"version": "0.1.0",
"entrypoint": "dist/index.js",
"nodeApp": {
"privateRuntime": ["better-sqlite3"]
}
}Use privateRuntime only for packages with native bindings or version
conflicts with the shared tree — each entry adds its full installed size to
your app. See the canonical reference at
docs/app-development/12-typescript-dependencies.md
for the full bundling and resolution model.
Build command
# Inside the node monorepo:
bash infra/scripts/build-bun-app.sh modules/my-app dist/build-aarch64/builtin-apps/my-app
# As a downstream app (after publishing):
bunx @econ-v1/app-sdk build .Defense-in-depth scope checks
this.route(
"POST",
"/admin/**",
{ requiredPermissions: ["payments:write"] },
async (req) => this.json({ ok: true }),
);The host already enforces endpoint_policies from your manifest before the request reaches the app — route() re-checks request.caller.granted_permissions as a second line of defense.
Calling host capabilities
import { invokeCapability } from "@econ-v1/app-sdk";
const response = await invokeCapability({
id: crypto.randomUUID(),
capability: "core.storage.get",
payload: { key: "user_pref" },
});Publishing events
import { publishEvent } from "@econ-v1/app-sdk";
publishEvent("my-app.user_created", { userId: 42 });Event names must be namespaced with the app name (my-app.*); the host rejects un-namespaced events.
ABI compatibility
The TypeScript SDK targets Node Host API v1 over IPC. The wire format is independently versioned but tracks the same semantics as the C ABI used by native (Rust / Go / C / Zig) apps. See core/host-abi-v1/include/node-host-api-v1.h for the C surface.
License
Licensed under either of
- Apache License, Version 2.0
- MIT License
at your option.
