ionscale-client
v0.2.4
Published
TypeScript client for the ionscale (https://github.com/jsiebens/ionscale) admin RPC API
Readme
ionscale-client
NOTE TO HUMANS: This is 90-100% generated by AI, it is built to test generation of API Clients in Node/TS
A TypeScript client for the ionscale admin
RPC API. It implements the Connect RPC protocol
used by ionscale's ionscale.v1.IonscaleService, so it can talk to any ionscale
server without needing a gRPC runtime.
- Fully typed requests/responses, validated with Zod v4
- HTTP requests performed with
ofetch - Zero dependency on protobuf/gRPC tooling — plain JSON over HTTP, as used by the Connect protocol
- Ships both ESM and CJS builds with bundled type declarations
Installation
pnpm add ionscale-clientUsage
import { IonscaleClient } from "ionscale-client";
const client = new IonscaleClient({
baseUrl: "https://ionscale.example.com",
token: process.env.IONSCALE_TOKEN,
});
const { tailnet } = await client.listTailnets().then((r) => ({ tailnet: r.tailnet[0] }));
const machines = await client.listMachines({ tailnetId: tailnet!.id });
for (const machine of machines.machines) {
console.log(machine.name, machine.ipv4);
}The token option also accepts a (possibly async) function, useful when the
credential needs to be refreshed or generated on demand:
const client = new IonscaleClient({
baseUrl: "https://ionscale.example.com",
token: async () => await getShortLivedToken(),
});Authenticating with the system admin key
If you're using ionscale's system admin key
(e.g. the value configured via IONSCALE_SYSTEM_ADMIN_KEY), pass it as
systemAdminKey instead of token:
const client = new IonscaleClient({
baseUrl: "https://ionscale.example.com",
systemAdminKey: process.env.IONSCALE_SYSTEM_ADMIN_KEY,
});The system admin key is not a bearer token itself — it's a private key that
must be used to sign a fresh, short-lived token for every request, the same
way the official ionscale CLI does. Passing it as token sends it verbatim
as a static Authorization header, which the server rejects with
invalid token.
Error handling
RPC failures are surfaced as a ConnectError, exposing the Connect protocol
error code (e.g. not_found, permission_denied, unauthenticated), the
message returned by the server, and the original HTTP status:
import { ConnectError } from "ionscale-client";
try {
await client.getTailnet({ id: 999 });
} catch (error) {
if (error instanceof ConnectError && error.code === "not_found") {
console.log("tailnet does not exist");
} else {
throw error;
}
}Streaming: interactive authentication
Authenticate is a server-streaming RPC used for interactive login flows. It
is exposed as an async generator:
for await (const message of client.authenticate()) {
if (message.authUrl) {
console.log(`Visit ${message.authUrl} to authenticate`);
}
if (message.token) {
console.log(`Received token: ${message.token}`);
}
}Supported RPCs
All RPCs defined by ionscale.v1.IonscaleService are implemented, covering:
- Server info (
getVersion) and interactive auth (authenticate) - Tailnets (create/update/get/list/delete, feature toggles, DERP maps)
- DNS config, IAM policy and ACL policy
- Auth keys (create/get/delete/list)
- Users (list/delete)
- Machines (get/list/rename/authorize/expire/delete, key-expiry)
- Subnet routes and exit nodes (get/enable/disable)
Development
pnpm install
pnpm build # bundle with tsup (ESM + CJS + d.ts)
pnpm test # run the vitest suite
pnpm typecheck # tsc --noEmit
pnpm lint # eslintThis project uses pnpm as its package manager, pinned via
the packageManager field in package.json, and Node.js version pinned in
.nvmrc.
