TypeScript/JavaScript SDK for Namespace Cloud APIs, providing authentication, client management, and type-safe API access.
- Modular Authentication: Multiple token loading strategies (user tokens, workload tokens, environment-based)
- Token Management: Automatic token caching and refresh
- Type Safety: Full TypeScript support with generated types
- Dual Module Support: Works with both ESM and CommonJS
- Flexible Client Creation: Support for both bearer tokens and TokenSource instances
npm install @namespacelabs/sdk @connectrpc/connect @connectrpc/connect-node @bufbuild/protobufimport { loadUserToken } from "@namespacelabs/sdk/auth";
import { createRegionTransport, createClient } from "@namespacelabs/sdk/api";
// Load token from user configuration
const tokenSource = await loadUserToken();
// Create transport with token source
const transport = createRegionTransport("us", { tokenSource });
// Use with your service clients...import { bearerAuthInterceptor } from "@namespacelabs/sdk/api";
const transport = createRegionTransport("us", {
token: "your-bearer-token",
});The SDK provides multiple ways to load authentication tokens, following patterns from the Go SDK:
Each function returns a TokenSource (or a promise of one). Call and await
them to load eagerly, or pass them uncalled wherever a token source is
accepted (e.g. tokenSource: loadUserToken) — the SDK invokes and awaits them
internally on first use, in the same style as AWS SDK credential providers.
Automatically detects and loads the appropriate token based on context:
- Checks
NSC_TOKEN_FILEenvironment variable - Tries
/var/run/nsc/token.json(workload token) - Falls back to user token from config directory
import { loadDefaults } from "@namespacelabs/sdk/auth";
const tokenSource = await loadDefaults();
const token = await tokenSource.issueToken(5 * 60 * 1000); // 5 minutesLoads token from user's local configuration:
- macOS:
~/Library/Application Support/ns/token.json - Linux:
~/.config/ns/token.json - Windows:
%APPDATA%/ns/token.json
import { loadUserToken } from "@namespacelabs/sdk/auth";
const tokenSource = await loadUserToken();Loads token from workload environment:
- Checks
NSC_TOKEN_FILEenvironment variable - Falls back to
/var/run/nsc/token.json
import { loadWorkloadToken } from "@namespacelabs/sdk/auth";
const tokenSource = await loadWorkloadToken();Creates a TokenSource from a bearer token string:
import { fromBearerToken } from "@namespacelabs/sdk/auth";
const tokenSource = fromBearerToken("nsct_...");All token loading functions return a TokenSource that implements:
interface TokenSource {
issueToken(minDuration: number, force?: boolean): Promise<string>;
}minDuration: Minimum duration (in milliseconds) the token should be validforce: Force token refresh even if cached token is valid
The SDK automatically caches tokens to minimize token refresh requests:
- Cached tokens are stored in
token.cachenext to the source token file - Cache is validated against tenant ID and expiration time
- Cache files are created with secure permissions (0600)
Extract and validate JWT token claims:
import { extractClaims, isTokenExpired, getTenantId } from "@namespacelabs/sdk/auth";
const claims = extractClaims(token);
console.log(claims.tenant_id);
const expired = isTokenExpired(claims);
const tenantId = getTenantId(token);The Devbox client exposes product-level resources for devboxes, blueprints, and images. A created or fetched devbox is an operational handle: starting it, establishing authenticated connections, and reusing those connections happen automatically.
import { createDevboxClient } from "@namespacelabs/sdk";
// Also available from the subpath: "@namespacelabs/sdk/devbox".
// With no options, authentication defaults to the workload token when
// running in a Namespace workload, falling back to the local user token.
const client = createDevboxClient();
// Or pass an explicit token source (invoked and awaited internally
// on first use):
// createDevboxClient({ tokenSource: loadUserToken })
const blueprint = await client.blueprints.create("typescript", {
image: "node:22",
size: "m",
environment: { NODE_ENV: "development" },
});
const devbox = await client.devboxes.create({
name: "my-devbox",
blueprint: blueprint.name,
});
// Structured argv: arguments are passed literally and do not expand in a shell.
const result = await devbox.exec(["node", "--version"]);
// Shell syntax, without allocating a TTY.
await devbox.shell("npm install && npm test", {
cwd: "/workspace",
});
// Commands run through the devbox agent, which retains each command and its
// output for later inspection (`devbox logs`). Relative `cwd` paths resolve
// against the devbox workspace directory. When `cwd` is omitted, commands run
// in the devbox default directory; if the devbox checks out a repository, that
// directory only exists once the checkout completes, so pass an explicit `cwd`
// when running commands immediately after creation.
await devbox.fs.upload("./package.json", "/workspace/package.json");
await devbox.fs.download("/workspace/results.json", "./results.json");
await devbox.fs.copy("/workspace/results.json", "/workspace/results-copy.json");
// PTY sessions are explicit and separate from shell execution.
const terminal = await devbox.terminal.open({ columns: 120, rows: 40 });
terminal.onData((data) => process.stdout.write(data));
terminal.write("pwd\n");
terminal.close();
client.close();upload() and download() transfer one file. copy() operates inside the devbox and accepts { recursive: true } for directories. All operations accept AbortSignal and timeout options.
Devboxes with a graphical display — macOS devboxes — expose screen access through devbox.display, backed by VNC. Methods reject with DevboxDisplayUnavailableError when the devbox has no display (for example, Linux devboxes):
import { writeFile } from "node:fs/promises";
import { DevboxDisplayUnavailableError } from "@namespacelabs/sdk";
const macos = await client.devboxes.create({
name: "my-mac",
os: "macos",
size: "m",
});
try {
const screenshot = await macos.display.screenshot();
await writeFile("screen.png", screenshot.png);
// Click at framebuffer coordinates (origin top-left).
await macos.display.click(100, 200);
await macos.display.click(100, 200, { button: "right" });
} catch (error) {
if (error instanceof DevboxDisplayUnavailableError) {
// This devbox has no display.
}
throw error;
}Like other connection-backed operations, using devbox.display on a stopped devbox activates it first, and the underlying VNC session is cached and reused across calls.
The VNC client behind devbox.display is also available standalone as @namespacelabs/sdk/vnc (openVnc): a minimal RFB 3.8 client over websockets with Apple Remote Desktop authentication, raw encoding, and PNG screenshots — no native dependencies and no Namespace-specific behavior.
Images can be registered from an existing image reference, listed, inspected, optimized for a site, and deleted:
const image = await client.images.register({
name: "node-22",
ref: "node:22",
});
// Image optimization defaults to iad when no site is specified.
await client.images.optimize(image.name);The SDK provides high-level client factories for each Namespace Cloud API:
- Compute (
@namespacelabs/sdk/api/compute) - Instance management, regional - IAM (
@namespacelabs/sdk/api/iam) - Tenant and token management, global - Builds (
@namespacelabs/sdk/api/builds) - Container image builds, regional - Storage (
@namespacelabs/sdk/api/storage) - Artifact storage, regional - Registry (
@namespacelabs/sdk/api/registry) - Container registry, global - Vault (
@namespacelabs/sdk/api/vault) - Secrets management, regional - Devboxes (
@namespacelabs/sdk/devbox) - Devboxes, blueprints, and images
Each API provides a client factory function:
import { loadUserToken } from "@namespacelabs/sdk/auth";
import { createComputeClient } from "@namespacelabs/sdk/api/compute";
import { createIAMClient } from "@namespacelabs/sdk/api/iam";
const tokenSource = await loadUserToken();
// Create a compute client for US region
const computeClient = createComputeClient({
region: "us",
tokenSource,
});
// Use the client
const instances = await computeClient.compute.listInstances({
tenantId: "your-tenant-id",
});
// Create an IAM client (global)
const iamClient = createIAMClient({ tokenSource });
// Use the IAM client
const tenants = await iamClient.tenants.listTenants({});The SDK provides transport creation utilities for different API endpoints:
For regional APIs (Compute, Builds, Storage, Vault):
import { createRegionTransport } from "@namespacelabs/sdk/api";
// With token source
const transport = createRegionTransport("us", {
tokenSource: await loadUserToken(),
});
// With custom base URL
const transport = createRegionTransport("us", {
tokenSource,
baseUrl: "https://custom.api.endpoint.com",
});For global APIs (IAM, Registry):
import { createGlobalTransport } from "@namespacelabs/sdk/api";
const transport = createGlobalTransport({
tokenSource: await loadUserToken(),
});The SDK provides an interceptor for adding authentication to requests:
Dynamically fetches tokens for each request from a TokenSource:
import { bearerAuthInterceptor } from "@namespacelabs/sdk/api";
import { loadUserToken } from "@namespacelabs/sdk/auth";
import { createConnectTransport } from "@connectrpc/connect-node";
const tokenSource = await loadUserToken();
const transport = createConnectTransport({
baseUrl: "https://api.example.com",
interceptors: [
bearerAuthInterceptor(tokenSource, 5 * 60 * 1000), // 5 minutes
],
});The interceptor automatically refreshes tokens as needed based on the minDuration parameter (default: 5 minutes).
typescript-sdk/
├── src/
│ ├── auth/ # Authentication module
│ │ ├── types.ts # Core types and interfaces
│ │ ├── claims.ts # JWT claims extraction
│ │ ├── token.ts # Token loading and management
│ │ └── index.ts # Module exports
│ ├── api/ # API client module
│ │ ├── clients.ts # Transport creation utilities
│ │ ├── interceptors.ts # Auth interceptors
│ │ └── index.ts # Module exports
│ └── index.ts # Main SDK export
├── dist/ # Compiled output (CJS, ESM, types)
├── examples/ # Example usage
├── package.json
├── tsconfig.json
└── README.md
The SDK provides multiple export paths:
@namespacelabs/sdk- Main entry point (re-exports auth, API clients, and Devboxes)@namespacelabs/sdk/auth- Authentication module only@namespacelabs/sdk/api- API client utilities only@namespacelabs/sdk/devbox- Devbox product API
Each export path supports both ESM and CommonJS:
// ESM
import { loadUserToken } from "@namespacelabs/sdk/auth";
// CommonJS
const { loadUserToken } = require("@namespacelabs/sdk/auth");NSC_TOKEN_FILE- Override default token file locationXDG_CONFIG_HOME- Linux config directory (defaults to~/.config)APPDATA- Windows config directory
The SDK provides specific error types:
import { NotLoggedInError } from "@namespacelabs/sdk/auth";
try {
const tokenSource = await loadUserToken();
} catch (error) {
if (error instanceof NotLoggedInError) {
console.error("Please run `nsc login` first");
}
}The SDK is written in TypeScript and provides full type definitions:
import type { TokenSource, TokenClaims, CachedToken } from "@namespacelabs/sdk/auth";
import type { CreateRegionTransportOpts, CreateClientOpts } from "@namespacelabs/sdk/api";npm run buildThis generates:
- CommonJS output in
dist/cjs/ - ES Module output in
dist/esm/ - Type declarations in
dist/types/
npm run cleanThis TypeScript SDK follows similar patterns to the Go integrations/auth package:
| Go SDK | TypeScript SDK |
|---|---|
auth.LoadDefaults() |
loadDefaults() |
auth.LoadUserToken() |
loadUserToken() |
auth.LoadWorkloadToken() |
loadWorkloadToken() |
api.TokenSource |
TokenSource interface |
auth.ExtractClaims() |
extractClaims() |
| Bearer token via gRPC metadata | Bearer token via HTTP Authorization header |
Apache-2.0
Contributions are welcome! Please open issues or pull requests on GitHub.