Docs

Resource APIs

Your stack defines resources like storage buckets, queues, and vaults. To use them in your application code, link them to your worker and import the SDK.

1. Link resources to your worker

alien.ts
const data = new alien.Storage("data").build()

const api = new alien.Worker("api")
  .code({ type: "source", src: "./src", toolchain: { type: "typescript" } })
  .link(data)
  .permissions("execution")
  .build()

2. Access them in your code

import { storage } from "@alienplatform/sdk"

const store = storage("data")  // same name as in alien.ts
await store.put("reports/q1.json", Buffer.from(JSON.stringify(report)))

The binding factories come straight from the SDK — no runtime, no connection setup. Constructing a handle does no I/O; the first operation on it resolves the resource and reuses it after that. Credentials are injected automatically — IAM roles on AWS, Workload Identity on GCP, Managed Identity on Azure. No config files, no connection strings.

In Rust, a Container or Daemon resolves the same bindings without a worker context via alien_bindings::Bindings::from_env():

let bindings = alien_bindings::Bindings::from_env()?;

let store = bindings.storage("data").await?;
// also: bindings.kv("cache"), bindings.queue("tasks"), bindings.vault("secrets")

from_env() reads the injected binding configuration synchronously and does no I/O; the first call on a returned handle resolves the resource.

Storage

Object storage — S3 on AWS, Cloud Storage on GCP, Blob Storage on Azure.

import { storage } from "@alienplatform/sdk"

const store = storage("files")

// Write (bytes)
await store.put("reports/q1.json", Buffer.from(JSON.stringify(report)))

// Read — get() returns data plus object metadata
const result = await store.get("reports/q1.json")
const content = result.data.toString("utf8")
console.log(result.meta.eTag, result.attributes.contentType)

// List — resolves to an array of object metadata
for (const entry of await store.list("reports/")) {
  console.log(entry.location, entry.size)
}

// Delete
await store.delete("reports/old.json")

// Presigned request for direct browser uploads/downloads
const req = await store.signedUrl({
  method: "GET",
  path: "reports/q1.json",
  expiresIn: 3600,
})

Full reference: Storage API | Behavior & limits

Key

Provider-backed encryption for small values. Import key directly from @alienplatform/bindings:

import { key } from "@alienplatform/bindings"

const encryptionKey = key("customer-key")
const plaintext = new TextEncoder().encode("small secret")
const ciphertext = await encryptionKey.encrypt(plaintext)
const decrypted = await encryptionKey.decrypt(ciphertext)

The binding accepts values up to 128 bytes and supports authenticated context. See Key.

KV

Key-value store — DynamoDB on AWS, Firestore on GCP, Table Storage on Azure.

import { kv } from "@alienplatform/sdk"

const cache = kv("cache")

// Write (with optional TTL in seconds)
await cache.setJson("user:123", { name: "Alice" })
await cache.set("session:abc", token, { ttl: 3600 })

// Read — entries carry the value plus an opaque version for conditional writes
const user = await cache.getJson<{ name: string }>("user:123")
console.log(user?.value.name, user?.version)
const session = await cache.getText("session:abc")
console.log(session?.value)

// Scan by prefix — resolves to a page of items plus a cursor
const page = await cache.scan("user:")
for (const entry of page.items) {
  console.log(entry.key, entry.value.toString("utf8"))
}

// Delete
await cache.delete("user:123")

The TypeScript handle also has get (raw bytes), exists, setJson, and paginates through nextCursor. Full reference: KV API | Behavior & limits

Queue

Message queue — SQS on AWS, Pub/Sub on GCP, Service Bus on Azure.

import { queue } from "@alienplatform/sdk"

const q = queue("tasks")  // bind the queue once by name
await q.send({ type: "process", id: "abc" })  // serialized as JSON
await q.sendText("raw text message")

To receive messages on a Worker, use event handlers — see Responding to Events below.

Full reference: Queue API | Behavior & limits

Vault

Secret storage — SSM Parameter Store on AWS, Secret Manager on GCP, Key Vault on Azure.

import { vault } from "@alienplatform/sdk"

const secrets = vault("credentials")

// Read a secret (get() resolves to a string; getJson() parses it)
const config = await secrets.getJson<{ host: string; database: string; password: string }>("database")

// Connect to the customer's database using their own credentials
const pool = new Pool({
    host: config.host,
    database: config.database,
    password: config.password,  // read at runtime from the customer vault
})

Secrets are stored in the customer's cloud vault and read by your deployed code at runtime. Keep them out of generated config, logs, and telemetry.

Full reference: Vault API | Behavior & limits

Container

Service discovery for a linked Container — resolve its URLs instead of hard-coding cloud-specific service names.

import { container } from "@alienplatform/sdk"

const api = container("api")

const internalUrl = await api.getInternalUrl()  // reachable from the deployment's private network
const publicUrl = await api.getPublicUrl()      // null when the container has no public endpoint

Sandbox

Create an isolated session and stream a command. Resource configuration such as CPU, memory, lifetime, and egress lives in alien.ts; runtime code cannot raise those limits.

import { sandbox } from "@alienplatform/sdk"

const box = sandbox("code-runner")
const session = await box.create({ sessionId: "turn-123" })

for await (const frame of box.runCommand(
  session.sessionId,
  ["python3", "main.py"],
  { deadlineMs: 30_000 },
)) {
  if (frame.kind === "stdout") process.stdout.write(frame.data)
  if (frame.kind === "exit") console.log(frame.exitCode)
}

Check await box.capabilities() before using platform-dependent file, reconnect, preview, or suspend/resume operations. See Sandbox API.

Binding errors

Binding failures are AlienError values with stable metadata:

import { AlienError } from "@alienplatform/bindings"

try {
  await storage("files").get("missing.txt")
} catch (error) {
  if (error instanceof AlienError) {
    console.error(error.code, error.retryable, error.hint)
  }
}

The binding layer preserves code, context, retryable, internal, httpStatusCode, and hint when the backend supplies them. Handle the code; show the human message or hint instead of parsing it.

Responding to Events

Workers can react to events — queue messages, file uploads, and cron schedules. Register a handler, and Alien wires the trigger. Event delivery is a Worker capability — Containers and Daemons don't receive events.

Queue Messages

import { kv, onQueueMessage } from "@alienplatform/sdk"

onQueueMessage("*", async (message) => {
  const store = kv("events")
  await store.setJson(`queue:${message.id}`, {
    source: message.source,
    payload: message.payload,
    processedAt: new Date().toISOString(),
  })
})

Use "*" to handle messages from any linked queue, or pass a specific queue name.

Storage Events

import { onStorageEvent } from "@alienplatform/sdk"

onStorageEvent("*", async (event) => {
  console.log(event.eventType, event.objectKey, event.size)
  // "created", "uploads/photo.jpg", 1048576
})

Cron / Scheduled Events

import { onCronEvent } from "@alienplatform/sdk"

onCronEvent("*", async (event) => {
  console.log(event.scheduleName, event.timestamp)
  // run cleanup, generate reports, sync data...
})

The schedule is defined in alien.ts via .trigger({ type: "schedule", cron: "0 * * * *" }). See Events & Triggers for trigger configuration.

Remote Commands

Define callable handlers that your control plane can invoke remotely — no inbound networking, no open ports. On a Worker, register handlers with command():

import { command, vault, kv } from "@alienplatform/sdk"
import { z } from "zod"

command(
  "query",
  z.object({ sql: z.string(), useCache: z.boolean() }),
  async ({ sql, useCache }) => {
    const secrets = vault("credentials")
    const cache = kv("cache")

    if (useCache) {
      const cached = await cache.getJson(`query:${hash(sql)}`)
      if (cached) return { ...cached, cached: true }
    }

    const config = await secrets.getJson<DbConfig>("database")
    const result = await runQuery(config, sql)

    if (useCache) {
      await cache.setJson(`query:${hash(sql)}`, result)
    }

    return { ...result, cached: false }
  },
)

A Container or Daemon receives commands through an explicit pull receiver instead. See Remote Commands for the full guide, including the sender and the Container/Daemon receiver.

Using Native Cloud SDKs

Every linked resource is also available as a JSON environment variable. Use any language, any SDK:

const binding = JSON.parse(process.env.ALIEN_DATA_BINDING!)

if (binding.service === "s3") {
  const s3 = new S3Client({})
  await s3.send(new GetObjectCommand({
    Bucket: binding.bucketName,
    Key: "reports/q1.json",
  }))
}
import json, os, boto3

binding = json.loads(os.environ["ALIEN_DATA_BINDING"])
s3 = boto3.client("s3")
s3.get_object(Bucket=binding["bucketName"], Key="reports/q1.json")

The environment variable name follows the pattern ALIEN_{NAME}_BINDING — uppercased, hyphens become underscores. The JSON contains resource identifiers, never credentials. Cloud credentials are injected automatically (IAM roles, Workload Identity, Managed Identity).

Use native SDKs when you need platform-specific features like DynamoDB streams, S3 Select, or Pub/Sub ordering keys. They coexist with the Alien SDK in the same app.

On this page