Northlight
SDK

SDK overview

Drive a crouter daemon from a Node or browser application with the typed SDK.

The ESM-only client an application installs to drive a crouter daemon: create agent runs, watch streamed events, wait for typed results, and reach the rest of the daemon's /v1 API.

One package, one class. new Crouter() in Node talks to the owner's unix socket. new Crouter({ baseURL, token }) in a browser or a remote process talks to the daemon's TCP listener with a bearer token. Resource methods issue /v1 requests; createAndWait, parse, and auth.status compose more than one request. crtrd stays the sole owner of canvas state.

import Crouter from '@north-light/sdk';
import { z } from 'zod';

const client = new Crouter();

const run = await client.nodes.parse({
  prompt: 'Summarize the failing tests in this repo.',
  cwd: '/path/to/repo',
  output_schema: z.object({ failures: z.array(z.string()), root_cause: z.string() }),
});

if (run.kind === 'result') console.log(run.output_parsed.root_cause);

Go live with an app key

Once you have installed the SDK and tried the local flow in Getting started, build against production: Build and register an app takes you from signing up at https://account.northlight.ai and joining your laptop (crtr sys join) to a running app on your laptop, registered with crtr cloud apps register. Generate the key and pull the settings with:

npx @north-light/sdk keygen --app <client id>
npx @north-light/sdk env --app <client id>

keygen saves the private JWK to local/app-key.json (mode 0600); keep that file out of git. With --app, it offers to add the public key to your registered app through your own crtr, as an additional key that never replaces the current one. Otherwise, add the public JWK printed on stdout with crtr cloud apps keys add --app-id <client id> --jwk '<public JWK>'. An app authenticates only with its key; the directory issues no client secret.

env reads the registration through crtr cloud apps get and writes CROUTER_ISSUER, CROUTER_CLIENT_ID and APP_ORIGIN into local/app.env (--out picks another file), keeping every other line. Both commands need this machine's crtr joined to your Northlight account; on a machine that isn't, they print a join step and exit 2. An app is configured from four variables:

VariableValue
CROUTER_ISSUERThe directory's issuer URL
CROUTER_CLIENT_IDThe app's client id
APP_ORIGINThe app's origin; its redirect URI is ${APP_ORIGIN}/auth/callback by convention
CROUTER_KEY_FILEPath to the private JWK, default local/app-key.json. env never writes it

The SDK reads no environment itself: load the values in your own config and pass them to OAuth2Client.

import {readFile} from 'node:fs/promises';
import {OAuth2Client} from '@north-light/sdk';

const privateKey = JSON.parse(await readFile(process.env.CROUTER_KEY_FILE ?? 'local/app-key.json', 'utf8'));
const oauth = new OAuth2Client({
  clientId: process.env.CROUTER_CLIENT_ID!,
  issuer: process.env.CROUTER_ISSUER!,
  redirectUri: `${process.env.APP_ORIGIN}/auth/callback`,
  privateKey,
  store, // your ConnectionStore
});
const {url, state, codeVerifier, nonce} = await oauth.authorizeUrl({scopes: ['openid', 'crtr:llm', 'crtr:act'], state: sessionState});
// Save state, codeVerifier, nonce in the user's session, then redirect to url.
const connection = await oauth.exchangeCode({code: callbackCode, state: callbackState, expectedState: state, codeVerifier, nonce});
await store.save(connection);
const client = await oauth.client(connection.userId); // Refreshes through the store from here on.

The directory rejects the key until its public JWK is added to the app. To change keys later, follow Rotating your key: add the new key, switch the app to it, verify a real sign-in, then retire the old key. An app can hold two active keys at once, so there is no downtime.

Pages

PageCovers
Getting startedInstall, new Crouter() on the owner's socket, client.auth.status() and crtr sys connect for a browser or remote app, Chrome's local-network prompt
Client constructionEvery constructor option and its environment-variable fallback; per-request options
Nodescreate parameters, the outcome union, parse() with a zod schema, waitForOutcome, message, cancel, nested resources
StreamingThe event table, stream() / events(), the activity helper, resume with after, stream_gap and stream_dropped
FilesAbsolute-path reads, writes, and one-level lists
BashOne bounded command and its output result
Memorymemory.list and memory.get: an app reads the person's public documents with no scope
Resource mapEvery namespace (including customObjects, runs, memory, shares, providers) with its routes, what is deliberately excluded, and the client.request() escape hatch
ErrorsThe error class table and the retry policy
Docker environmentstart(), attach(), connection() from @north-light/env-docker
Testing your appFakeCloud from @north-light/sdk/testing: a fake directory and runtime your real OAuth2Client and Crouter talk to through fetch
Release notesBehavior changes an existing app must act on, newest first
Apps on NorthlightBuilding and registering an app against production (joining a laptop, crtr cloud apps registration, keys, env file, scopes, running locally, the crtr cloud apps commands); typed results from app runs with runs.parse, declines, bounding a run, run_idle, and waiting on a provider your app requires
Apps with many usersPer-person runtime connections, app-owned user access, privacy, and spending

Phases

The surface ships in three cuts.

PhaseWhat landsState
Phase 1Client construction and transports; client.auth.status(); client.nodes.create, retrieve, list, outcome, waitForOutcome, createAndWait, parse, message, cancel, and interrupt; nodes.reports.list; profiles.ensure and retrieve; system.status and health; the error hierarchy; per-request options; crtr sys connect; and env-docker.connection()Shipped
Phase 2 namespacesNode lifecycle, job, worktree, and result; canvas and canvas history; crons; human requests and inbox; models; client.files.read through /v1/files/peek, write, and list; and client.bash.runShipped
Phase 2 streamingGET /v1/nodes/{id}/events, nodes.stream(params, options?), nodes.events(id, options?), NodeStream, and the activity helperShipped
Phase 3The review, comment, and chat-inventory namespacesNot shipped

The per-run scopes create field ships in phase 1. nodes.update scope support remains phase 2 and is not in the shipped client or wire declarations.

Verification status

The SDK declarations and streaming examples are verified.

Where the agents read this

The same content routed for agents lives in the built-in document crouter-sdk, which ships with the crtr binary. An agent working in an application's repository reaches it with crtr canvas read crouter-sdk and does not need this repository checked out.

On this page