Changelog
ChangelogCompute

Containers without a runtime

Until now, every Container and Daemon ran under a small Alien runtime — a Rust process that started your app, supervised it, and brokered access to bindings and commands. That layer is gone. Your entrypoint is now PID 1: what runs in the customer's cloud is exactly what's in your Dockerfile, with nothing in front of it.

Bindings moved in-process to make that possible. Instead of talking to the runtime, your app imports a library, resolves a resource once, and calls it:

import { kv, queue, container } from "@alienplatform/bindings"

const index = kv("index")
const jobs = queue("jobs")

await index.setJson("user:42", { plan: "pro" })
await jobs.send({ type: "welcome-email", userId: 42 })

const db = container("postgres")
const url = await db.getInternalUrl()

The library is a native binding over the same Rust core on every language, so behavior is identical across Node, Bun, and Rust. Credentials are minted short-lived and refreshed behind the handle — nothing long-lived sits in your environment.

Commands got the same treatment. A container registers named handlers, with optional schema validation using any Standard Schema library (Zod, Valibot, ArkType), and your control plane invokes them without any inbound port:

import { createCommandReceiver } from "@alienplatform/commands"
import * as z from "zod"

const receiver = createCommandReceiver()

receiver.command("reindex", z.object({ tenant: z.string() }), async input => {
  return { started: true, tenant: input.tenant }
})

await receiver.run()

Validated commands infer their input types; handleRaw is there when you want the raw payload.

  • No wrapper — your process runs directly, smaller images, one less thing between you and your app
  • In-process bindings — resolve once, call like a library; queue and KV APIs no longer take a name on every call
  • Typed commands — schema-validated input in Rust and TypeScript

Read the docs →

Dan LilienblumAlon Gubkin
Dan & Alon