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
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 endpointSandbox
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.
Stack inputs
Declare the values a deployment needs in alien.ts, mark who provides each one, and Alien validates and collects it across the dashboard, deployment portal, CLI, CloudFormation, Terraform, and Helm.
Remote Commands
Invoke explicit handlers on remote deployments without opening inbound ports.