LaserData Cloud
Laser SDK

Connect

Connect with credentials and TLS, set publish timeouts and retries, and wait for managed services

Create one connected client before using SDK operations. The client supplies authentication, transport, and shared connection state. You can use it across all streams on the server.

Connection strings

Pass a bare connection string to connect:

import { Laser } from "@laserdata/laser-sdk"

await using laser = await Laser.connect("iggy:iggy@127.0.0.1:8090")
use laser_sdk::prelude::*;

let laser = Laser::connect("iggy:iggy@127.0.0.1:8090").await?;
import laser_sdk as ls

laser = await ls.Laser.connect("iggy:iggy@127.0.0.1:8090")

Use user:pwd@host or token@host. The default port is 8090.

# username and password
iggy:iggy@127.0.0.1:8090

# token in place of user:pwd
<token>@starter-123.us-west-1.aws.laserdata.cloud

The SDK adds the Iggy TCP scheme and creates a client.

Laser Stack starts Iggy and laser-plane locally and prints the connection export:

export LASER_CONNECTION_STRING='iggy:laser@127.0.0.1:8090'

Rust's Laser::local() and TypeScript's Laser.local() use iggy:iggy@127.0.0.1:8090. Laser Stack defaults to iggy:laser, and Python has no local shortcut. Use the connection string printed by Laser Stack when connecting to it.

Environment variables

Use Laser::connect_env() in Rust or Laser.connectEnv() in TypeScript to read the environment. Python reads it through Laser.connect:

await using laser = await Laser.connectEnv()
let laser = Laser::connect_env().await?;
import os
import laser_sdk as ls

laser = await ls.Laser.connect(
    os.environ["LASER_CONNECTION_STRING"],
    stream=os.environ.get("LASER_STREAM"),
)
VariableEffect
LASER_CONNECTION_STRINGThe whole bare connection target, exactly what connect() takes. Rust and TypeScript's environment helpers require it.
LASER_STREAMOptional default stream. Rust and TypeScript read it automatically. Pass it as Python's stream= argument.
LASER_TLS_CERTPath to a CA cert file, overriding the auto-attached LaserData CA wherever a connection string is resolved.
LASER_NO_TLS=1Disable automatic TLS setup (local development only). Same scope as LASER_TLS_CERT.
LASER_CONNECTION_STRING='user:pwd@starter-123.us-west-1.aws.laserdata.cloud'
# or with a token instead of user:pwd
LASER_CONNECTION_STRING='<token>@starter-123.us-west-1.aws.laserdata.cloud'

The SDK does not read LASER_SERVER, LASER_TOKEN, LASER_USERNAME, or LASER_PASSWORD directly. Some examples use them to construct LASER_CONNECTION_STRING.

Iggy transport

Rust, Python, and TypeScript use Iggy's native connection.

Streaming and managed operations share the connection. The server determines which operations require replication.

Token vs. username/password

Use <token>@host for a token or user:pwd@host for a username and password. Tokens support scoping and rotation for services and CI. Username and password authentication suits local development.

Both authenticate an Iggy user with stream and topic permissions. Permission failures return typed errors, such as is_permission_denied().

TLS

For *.laserdata.cloud and *.laserdata.com, the SDK enables TLS with the bundled LaserData root CA, a trusted certificate authority. Other hosts retain their connection-string TLS configuration.

TLS overrides follow these rules:

  • LASER_TLS_CERT=<path> or tls_ca_file= selects your own CA instead of the bundled certificate.
  • LASER_NO_TLS=1 disables automatic TLS setup. 0 and false do not disable it.
  • If the connection string contains tls_ca_file=, automatic CA attachment does not change it.

Reconnection

The client retries initial TCP connections and later disconnects. By default, it retries indefinitely at one-second intervals. Change this behavior through connection-string parameters:

user:pwd@host
  ?reconnection_retries=<count|unlimited>
  &reconnection_interval=<duration>

Duration values include 250ms, 1s, and 1m. After reconnection, the client authenticates with the connection-string credentials again.

Publish timeouts and retries

Each publish attempt has a 60-second timeout and up to three more attempts. Retry delays start at 250 milliseconds, double after each failure, and stop at 30 seconds. The timeout bounds one attempt. The transport can report a failure sooner.

SettingEnvironment variableDefault
Attempt timeoutLASER_PUBLISH_TIMEOUT_MS60000
Additional attemptsLASER_PUBLISH_MAX_RETRIES3
First retry delayLASER_PUBLISH_RETRY_BACKOFF_MS250

Explicit builder or connect settings override the variables. Set retries to zero to disable automatic resends. The timeout and the delay must be positive and at most 2147483647 milliseconds. Invalid settings fail before the connection opens.

const laser = await Laser.builder()
  .connectionString("iggy:iggy@127.0.0.1:8090")
  .publishTimeout(90_000)
  .publishMaxRetries(5)
  .publishRetryBackoff(500)
  .connect()
let laser = Laser::builder()
    .connection_string("iggy:iggy@127.0.0.1:8090")
    .publish_timeout(Duration::from_secs(90))
    .publish_max_retries(5)
    .publish_retry_backoff(Duration::from_millis(500))
    .build()
    .await?;
laser = await ls.Laser.connect(
    "iggy:iggy@127.0.0.1:8090",
    publish_timeout_ms=90000,
    publish_max_retries=5,
    publish_retry_backoff_ms=500,
)

The environment defaults apply to connection builders and the connect helpers. Rust's Laser::from_client uses the built-in defaults because it cannot return an error. To configure a client you supply, use Laser::builder().client(client).

These settings cover fluent, typed, agent, and direct streaming publishes. Explicit retry settings on a direct producer take precedence. Rust background producers keep Iggy's queue and worker behavior. Their send result only confirms that the queue accepted the records.

What triggers a retry

A timeout or a temporary connection failure triggers a retry within the configured limits. An Unauthenticated reply also triggers recovery when the connection had authenticated before. Reconnection or a leader change can move the client to a node that holds no session for it. The SDK reconnects with the connection-string credentials, then retries. Permission errors, rejected credentials, invalid requests, and malformed commit confirmations fail at once.

Rust reconnects the shared client in place. Consumers and reply readers keep their connection and diagnostic subscriptions. Recovery applies only to the connection that the failed attempt used. If another publish already replaced that connection, the failed publish uses the replacement instead of closing it again.

TypeScript closes a failed socket and rejoins registered consumer groups on the replacement. The Iggy client can replace its own socket during a leader change. TypeScript does not treat that as a lost connection, so the current send can finish. It also runs one publish attempt per connection at a time, which keeps the connection on the node that accepts the message. A supplied TypeScript client has no connection recipe, so its owner must replace it after a failure. The SDK still closes a socket that times out, because its pending reply prevents safe reuse.

Retries keep message IDs, payloads, headers, and routing. Rust retries in chunks and never resends a confirmed chunk. Delivery stays at-least-once. A lost acknowledgement can deliver a record twice even with the same message ID, so effects must be idempotent. An agent reply deadline is separate from the publish timeout.

Longer outages

Exhausted retries return an error in Rust, raise a typed exception in Python, and reject the promise in TypeScript. Nothing panics or exits. Handle the failure, then try again once the cluster recovers.

An unhandled exception or promise rejection can still stop the process. So can a Rust main that propagates the error, or an unwrap. Consumer and agent tasks can also finish with an error. Watch their run or join result and restart them.

The SDK has no durable queue for offline publishes. A long-running service keeps failed work in its own durable queue, slows intake during an outage, and retries later with the same idempotency key. Keep retry limits finite so one request cannot hold resources forever.

Managed Readiness

A successful connection establishes Iggy access. Queries, key-value storage, forks, graph, and other managed services also need a ready backend. A connected server can still be replaying managed state.

capabilities() reads the negotiated capability snapshot. Refresh it after startup or backend restart, or use a readiness wait with a deadline:

LanguageRefreshWait
Rustlaser.refresh_capabilities().awaitlaser.wait_until_ready(Duration::from_secs(30)).await?
TypeScriptawait laser.refreshCapabilities()await laser.waitUntilReady(30_000)
Pythonawait laser.refresh_capabilities()await laser.wait_until_ready(30)

The wait reports unsupported if the server has no managed backend descriptors. A configured backend remains unavailable until replay finishes and it reports ready.

Capabilities include operation versions, backend descriptions, readiness reasons, and resolved AGDX topic topology. Explicit client overrides take precedence. Existing handles use the shared connection state after a refresh.

The SDK authenticates owned connections again after reconnection. An injected Iggy client retains its caller's lifecycle and retry policy.

Retry behavior depends on the operation. The SDK does not automatically repeat lease acquisition after an ambiguous disconnect. It waits through the requested lease TTL before reporting uncertainty. The application cannot immediately assume that no lease exists. See State.

The single-connection model

Without a default stream, use the full stream and topic path:

laser.stream(name).topic(name)

The default stream helper

If most calls use one stream, select it when connecting:

await using laser = await Laser.connectWithStream(
  "iggy:iggy@127.0.0.1:8090",
  "shop"
)

// shorthand for laser.stream("shop").topic("orders")
const orders = laser.topic("orders")
let laser = Laser::connect_with_stream(
    "iggy:iggy@127.0.0.1:8090",
    "shop",
)
.await?;

// shorthand for laser.stream("shop").topic("orders")
let orders = laser.topic("orders");
laser = await ls.Laser.connect(
    "iggy:iggy@127.0.0.1:8090",
    stream="shop",
)

# shorthand for laser.stream("shop").topic("orders")
orders = laser.topic("orders")

Use connect_with_stream in Rust, connectWithStream in TypeScript, or connect(..., stream=...) in Python. This enables laser.topic(name). Other streams remain accessible through laser.stream(other).topic(name). Without a default, laser.topic(name) returns a configuration error.

To change the default, use with_default_stream(name), withDefaultStream(name), or with_stream(name) for the respective client. The returned client shares the existing connection.

Next

On this page