@idyl-labs/sdk is the official TypeScript SDK for IDYL. It is one package
with one entry point, the Idyl client, and no runtime dependencies. It runs
on Node.js 22 and later, in evergreen browsers, and in Workers-class edge
runtimes from a single build, using only web-standard APIs: fetch,
AbortSignal, ReadableStream, TextEncoder, TextDecoder, and URL. The
package ships ESM and CommonJS builds with type declarations.
The SDK implements the model the Go SDK
defines: the same resources, the same names, and the same wire format, held by
a conformance suite against fixtures taken from the Go SDK. Use the
repository and the
package page as the source and
API reference.
Installation
Configure a client
Everything the client needs is in oneClientConfig value. The SDK reads no
environment variable, configuration file, or ambient credential store, and
substitutes nothing for a field left unset. Invalid configuration throws a
TypeError naming the field.
credential is a CredentialSource. Its token method is called for every
authenticated request rather than once at construction, so a source that
refreshes or rotates a short-lived credential is adopted without rebuilding the
client or disturbing clients scoped from it. Every token is opaque to the SDK:
it is sent as a bearer credential and never inspected. staticToken wraps a
fixed token. credentialSourceFunc adapts a function that receives the
request’s AbortSignal, so a cancelled request never leaves a token exchange
running.
Quick start
Create a Sandbox, wait until it is ready, and delete it:createAndWait takes an optional second argument,
{ timeoutMs, pollIntervalMs, signal }. Left out, the wait allows ten minutes
and polls every second. A Sandbox with neither cpu nor memory set is
limited to one CPU and 1Gi of memory; setting either overrides the default.
The handle it returns is an immutable snapshot of the ready Sandbox.
The examples on the rest of this page use the idyl client configured here.
Scope
A client is immutable. Every scoping method returns a copy and leaves the receiver unchanged, so a broadly configured client can be narrowed per call site while the original stays valid. Copies share one transport, which is safe for concurrent use.
Direct Workload operations need only the Namespace and placement.
Computer operations take a Computer name or ID through
idyl.account.computers. scope reports the frozen Account, Namespace, and
placement a client runs in.
Placement
Placement says where Workloads run. Values come from four constructors, are frozen, and are validated when they are built:PlacementUnavailableError whose reason names the classification:
reason is a closed vocabulary that only grows: a value is never renamed or
removed, and a new value is a new case to handle.
A caller that has already resolved and authorized a Subnet route can bind it
instead. A bound client addresses that endpoint directly and repeats no
discovery. Binding changes neither authentication, scope, nor server-side
authorization:
Services
TheIdyl client exposes one service per resource family. Workload services
run in the client’s Namespace and resolved Subnet; Account services address the
Account directly.
Every Namespace-scoped service also lists its collection account-wide through
listAllNamespaces, which spans every Namespace the caller is authorized to
list and needs no Namespace on the client. List options are limit and
offset; list responses carry items, totalCount, and snapshotCursor.
Handles and waits
Every Workload operation returns a handle, an immutable snapshot of what the platform recorded. A handle reads its own state, such asname and phase,
without touching the network, and manifest() returns an independent copy of
the snapshot. refresh reads the resource again and returns a new handle;
update replaces the definition and returns a new handle; delete asks the
platform to remove the resource and resolves once the request is accepted. The
receiver is never changed.
createManifest accepts the
full manifest type when the options cannot express a definition. createAndWait
and createManifestAndWait submit and wait in one call:
Every wait takes a
WaitOptions value, { timeoutMs, pollIntervalMs, signal }.
The defaults are ten minutes and one second. A wait that ends in a state the
resource cannot leave rejects with a WorkloadTerminalError carrying kind,
workloadName, condition, and the platform’s explanation as detail when it
supplied one. A wait that exhausts its timeout rejects with a TimeoutError
DOMException, and one cancelled through signal rejects with the signal’s
reason. None of those outcomes deletes anything.
Identity and discovery
Sign-in is anonymous, so it uses a separateIdentity client with no Account
or credential:
Every operation that needs a refresh token takes it as an argument and sends it
as the bearer credential of that one request. The client holds no credential of
its own.
Public region and Subnet discovery needs neither an Account nor a credential.
Discovery reads a Network’s metadata endpoint anonymously. The same reads are
available as idyl.regions and idyl.subnets on an authenticated client,
where they are anonymous too: a bearer credential never reaches the metadata
endpoint.
Logs, shells, and watches
idyl.workloadSessions.logs streams a container’s logs over a WebSocket
session. The SDK reserves the session, presents its one-use grant as the first
message on the socket, delivers the frames to the sinks it is given in order,
and closes the socket on completion or cancellation:
WritableStream<Uint8Array> or a function called with each chunk,
and one sink may serve both streams. A session that ends in an error frame
rejects with WorkloadSessionError, whose code is the session’s closed
vocabulary. A transport failure rejects with WorkloadSessionTransportError,
whose code is the transport’s closed vocabulary and whose message is a fixed
sentence. Cancelling the request signal closes the socket and rejects with the
signal’s reason.
The SDK speaks no SSH. openShellTransport reserves a shell session for the
caller’s ephemeral key, receives a one-use certificate bound to that key, and
opens the byte duplex an SSH implementation authenticates over. The caller owns
the key pair and gives the SDK the public key in OpenSSH authorized-key form;
the implementation that attaches owns the key, the handshake, and the host-key
decision:
close.
watchWorkloads streams Workload changes in the resolved Subnet, narrowed to
the client’s Namespace when it has one. watchRuntimePods streams the observed
pods of the client’s Namespace and watchRuntimeNodes the observed Nodes of
its Subnet. Take a Workload snapshot before watching when a consumer needs a
gap-free initial view, then pass its snapshotCursor to the matching watch:
every change after the snapshot is delivered exactly once and nothing before it
is redelivered. Always close a watch a consumer leaves early.
ApiError. Iteration ends when the platform closes the stream,
rejects with LiveStreamError on an error frame, and rejects with the request
signal’s reason on cancellation. Events of a type the SDK does not name are
ignored, so a newer platform never ends a watch.
Errors and cancellation
A non-2xx response becomes anApiError carrying status, the stable code
when the endpoint publishes one, message, details, requestId, and
retryAfterMs. Branch on the fields or on the predicates isApiStatus,
isApiCode, and isNotFound. The message is written for logs and for people
and is not a contract.
Every class sets
name and preserves its cause. Invalid arguments and
configuration, a malformed option, a manifest whose scope disagrees with the
client’s, or a Workload operation on a client with no Namespace or placement,
throw a TypeError before any request is made.
AbortSignal, as signal in the request options
of a call or in the WaitOptions of a wait. Cancellation propagates through
the credential lookup, the request, the body read, and any attached stream or
session, and the operation rejects with the signal’s reason. Each request is
also bounded by requestTimeoutMs, 30 seconds by default; a request that
exceeds it rejects with a TimeoutError DOMException. Cancelling an
operation never deletes a resource.
Manifests
A manifest is an envelope ofkind, metadata, and spec, with status
added by the platform on read, and the SDK ships every kind as a plain
read-only type with the same field names and wire spellings as the Go SDK.
Decoding is lenient on read and strict on write. decodeSubmission prepares a
manifest for submission: it takes JSON text or a parsed value and refuses an
unknown field, a duplicate key, a value of the wrong type, and a missing
envelope field, throwing a StrictDecodeError that names the field by its full
path from the document root. decodeObserved reads a manifest the platform
reported: a field the SDK does not declare is carried, a value it cannot read is
carried as it arrived, and a missing field reads as its zero value. Each kind’s
validate* function, such as validateSandbox, holds a manifest to the
contract the Go SDK enforces and throws a TypeError naming the first
violation.

