Docs

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.

Most deployments need a few values that aren't known until setup: a database URL inside the customer's network, an API key for your control plane, a log level. Stack inputs let you declare those values in alien.ts, say who provides each one, and Alien handles collecting and validating them everywhere setup happens.

In BYOC, setup has two sides. Some values are yours, like a control-plane key. Some belong to the customer's admin, like an endpoint that only exists inside their network. If you hardcode values or pass ad hoc env flags, the deployment portal, generated IaC, and white-labeled CLI don't know those values exist, so an admin can follow setup and still end up with a broken deployment. Stack inputs fix that: one declaration drives every surface.

Declaring inputs

Declare a set with alien.inputs(...) and attach it to your stack with .inputs(). Each key is the input's id.

import * as alien from "@alienplatform/core"

const inputs = alien.inputs({
  databaseUrl: alien.string({
    providedBy: "deployer",
    required: true,
    label: "Database URL",
    description: "Postgres connection string inside the customer's network.",
    pattern: "^postgres://",
    env: "DATABASE_URL",
  }),
  controlPlaneApiKey: alien.secret({
    providedBy: "developer",
    required: true,
    label: "Control plane API key",
    description: "Authorizes this deployment with your control plane.",
    env: { name: "CONTROL_PLANE_API_KEY", targetResources: ["api"] },
  }),
  logLevel: alien.enum(["debug", "info", "warn", "error"], {
    providedBy: "deployer",
    required: false,
    label: "Log level",
    description: "Verbosity for the deployment's logs.",
    default: "info",
    env: "LOG_LEVEL",
  }),
})

export default new alien.Stack("acme-app").inputs(inputs).build()

Input ids are normal identifiers (letters, digits, underscores) and can't be all-caps env-var style. Every input needs a label, and a description is required whenever required is true.

Who provides a value

providedBy decides which side is asked for a value, and on which surfaces it appears.

providedByCollected onNotes
"developer"Your surfaces: alien onboard, the dashboard generate-link flow, deployment-group APIsNever shown to the customer and never emitted into generated IaC or the portal
"deployer"The customer's surfaces: deployment portal, generated CloudFormation, Terraform, Helm, and the white-labeled CLIAsked of the customer's admin at install time
["developer", "deployer"]BothYou can prefill it; if you don't, the admin is asked at install time

This is what keeps your secrets on your side: a developer input is collected only where you control it, so it never reaches the customer or lands in anything they can read.

Input types

alien.string({ ... })   // free text
alien.secret({ ... })   // masked, encrypted, no default
alien.number({ ... })   // any number
alien.integer({ ... })  // whole number
alien.boolean({ ... })  // true / false
alien.enum(["a", "b"], { ... })  // one of a fixed set
alien.stringList({ ... })        // list of strings

Every type takes the common options providedBy, required, label, description, and optionally placeholder, default (not for secret), platforms, and env. Use platforms to scope an input to specific targets, so a local-only value isn't requested for an AWS-only deployment link.

Validation

Validation is a small portable set, declared right on the input, so it's enforced identically in TypeScript, the deployment portal, CloudFormation, and Terraform.

TypeOptions
string, secretminLength, maxLength, pattern, format
number, integermin, max
enumthe allowed values
stringListminItems, maxItems

pattern is matched against the whole value. Keep it portable: no regex delimiters, backreferences, or lookarounds, since the same pattern has to run in CloudFormation rules and Terraform validation blocks, not just JavaScript.

webhookUrl: alien.string({
  providedBy: "deployer",
  required: true,
  label: "Webhook URL",
  description: "HTTPS endpoint the deployment calls back.",
  pattern: "^https://",
  maxLength: 2048,
  env: "WEBHOOK_URL",
}),

Environment variables

env maps an input into the deployment's runtime environment. Pass a variable name, or an object to target specific resources:

env: "DATABASE_URL"
env: { name: "DATABASE_URL", targetResources: ["api", "worker"] }

Variable names are upper snake case. By default an input is available to every resource; targetResources narrows it to the listed ones.

Secrets

alien.secret values are masked in the dashboard and portal, encrypted at rest, and kept out of generated IaC state, logs, and launch URLs. They can't declare a default, and they flow through Alien's runtime secret vault rather than plain environment snapshots.

Gating resources

A boolean deployer input can decide whether a resource exists at all. Call .enabled(input) on the resource builder — every resource type supports it, except the reserved secrets vault and infrastructure Alien derives from the stack itself (networks, service accounts, registries):

const io = alien.inputs({
  analyticsEnabled: alien.boolean({
    providedBy: "deployer",
    required: false,
    label: "Enable analytics",
    description: "Provision the analytics store.",
    default: false,
  }),
})

const analytics = new alien.Kv("analytics").enabled(io.analyticsEnabled).build()

A deployer who answers no never gets the resource: it isn't provisioned, and it's absent from the generated IaC and IAM policies. What happens on later edits depends on the resource's frozen or live mode:

  • Frozen — the answer is fixed when the deployment is created and can't change afterwards.
  • Live — the resource follows later edits to the input. Turning it off deletes the resource together with its data; turning it back on recreates it empty.

The input must be a deployer-provided boolean, and it must be required or declare a default. Compute resources (Workers, Containers, Daemons) can be gated too: declining a live workload follows the same removal path as deleting it from a release, and accepting later brings it back.

What's next

On this page