Docs

Using resources

A resource is a piece of cloud infrastructure your code uses, such as a bucket, a key-value store, or a queue. Every resource works the same way: you declare it in alien.ts, connect it to the code that uses it, and call it from that code.

alien.ts
const documents = new alien.Storage("documents").versioning(true).build()
const index = new alien.Kv("index").build()

const processor = new alien.Worker("processor")
  .code({ type: "source", src: "./processor", toolchain: { type: "typescript" } })
  .link(documents)
  .link(index)
  .permissions("processor")
  .build()
processor/src/index.ts
import { kv, storage } from "@alienplatform/sdk"

const documents = storage("documents")
const index = kv("index")

async function main() {
  const file = await documents.get("contracts/2026/msa.pdf")
  await index.setJson("doc:contracts/2026/msa.pdf", { bytes: file.data.length })
}

void main()

Alien creates the native service on each platform. On AWS, documents is an S3 bucket and index is a DynamoDB table. On Google Cloud, the same code reads Cloud Storage and writes Firestore.

Declare

Every builder follows one pattern: new alien.X(id), chained setters, then .build(). Add the result to the stack with .add(resource, "frozen") or .add(resource, "live"), which decides who creates and changes it. See Frozen or live.

A link connects a resource to a workload, the code that uses it. .link() does two things:

  • It injects the resource's connection details into the workload's environment.
  • It grants access through the workload's permission profile, the named set of cloud permissions the workload runs with. Name the resource in the profile to choose the exact access.

Code can only reach resources it is linked to. The cloud enforces this through the workload's identity.

Call from code

A binding is how your code calls a linked resource: Alien injects the resource's connection details into the workload, and a client from @alienplatform/sdk reads them. Each client factory takes the resource id from alien.ts, as in storage("documents") above.

Creating a binding does no network I/O. The first call resolves the resource and authenticates with the workload's cloud identity: an IAM role on AWS, a service account on Google Cloud, a managed identity on Azure. Your code holds no keys or connection strings.

Containers and Daemons use the same bindings. A TypeScript service that isn't built from source imports them from @alienplatform/bindings. Python (alienplatform) and Rust (alien-sdk) wrap the same core; see the SDK reference.

Linking a resource injects ALIEN_<ID>_BINDING into the workload: a JSON description of the provisioned service. The id is uppercased and hyphens become underscores. See Environment variables.

Cloud SDKs

The Alien SDK is optional. The binding carries the native identifiers, so you can use the cloud's own SDK for features Alien doesn't wrap, such as DynamoDB streams or Pub/Sub ordering keys:

processor/src/report.ts
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"

const binding = JSON.parse(process.env.ALIEN_DOCUMENTS_BINDING!)
// { service: "s3", bucketName: "...", region: "us-east-1" }

const s3 = new S3Client({ region: binding.region })
await s3.send(new GetObjectCommand({ Bucket: binding.bucketName, Key: "report.json" }))

Both approaches work in the same app. Some resources, such as Email, have no Alien client and are used this way.

Frozen or live

Each resource is added to the stack as "frozen" or "live". The difference is who creates and changes it:

  • Frozen: the customer's admin creates it once, during initial setup, with their own credentials. Alien can't change it afterward.
  • Live: Alien creates and updates it on every release, through the deployment's management identity, a scoped identity in the customer's account.

See Frozen and live resources. Stack.add() always takes the lifecycle explicitly. Each resource accepts:

ResourceLifecycle
Worker, Container, DaemonLive
ComputeClusterFrozen
SandboxFrozen (Live on AWS)
Storage, Kv, Queue, Vault, Postgres, AIFrozen or live
Key, Email, NetworkFrozen
ArtifactRegistry, Build, ServiceAccountFrozen

Resources Alien adds for you, such as the Network and a ServiceAccount per permission profile, are frozen. ArtifactRegistry and Key are frozen, but the management identity can still manage what's inside them, such as repositories and access grants.

Optional resources

.enabled(input) creates a resource only when a boolean input the admin answers at initial setup is true, for example an AI resource that only some customers turn on. See Inputs and secrets.

A frozen gated resource keeps the answer given at creation. A live gated resource follows later edits to the input, and turning it off deletes the resource and its data.

Common to every resource

MemberNotes
new alien.X(id)Ids match [A-Za-z0-9-_], at most 64 characters. Container and Sandbox ids must be DNS-compatible (lowercase letters, digits, hyphens).
.build()Validates the config against its schema and returns a Resource. Throws on invalid input.
.enabled(input)Creates the resource only when a boolean stack input is true. Not on ArtifactRegistry, Build, ServiceAccount, or ComputeCluster.
static any()Returns the resource type, for permission targets that cover every resource of that type.
resource.ref()Returns { type, id }, used by triggers and links.

Common patterns

Hand out an upload URL. Your code creates a signed URL, and the customer's browser uploads straight into their bucket. The file never passes through your cloud:

processor/src/index.ts
const upload = await documents.signedUrl({ method: "PUT", path: "inbox/new.pdf", expiresIn: 900 })

React to new data. A Worker trigger runs a handler for each event:

  • { type: "storage", storage, events: ["created"] } with onStorageEvent
  • { type: "queue", queue } with onQueueMessage
  • { type: "schedule", cron } with onCronEvent

Delivery can repeat, so make handlers safe to run twice.

Call another service in the stack. Link a Container to the Worker, then read its private URL:

processor/src/index.ts
import { container } from "@alienplatform/sdk"

const ocrUrl = await container("ocr").getInternalUrl()
const res = await fetch(`${ocrUrl}/extract`, { method: "POST", body: new Uint8Array(file.data) })

Local development

alien dev runs your stack on your machine with no cloud credentials. Most resources get a local equivalent behind the same SDK calls, so code that runs locally runs unchanged in a customer's cloud.

AI uses your own provider key locally, and Email is AWS only. See Local development.

Next

On this page