Skip to content

Repository files navigation

stackless

stackless

crates.ionpmPyPIGo ReferenceGitHub releaseCILicense: MIT OR Apache-2.0

Ephemeral software stacks: named, leased, isolated, proven, destroyed.

What

stackless is a CLI that owns the full lifecycle of an ephemeral stack. One stackless.toml describes the product — services, secrets, wiring, health. One verb (up) spawns an isolated, named instance with a URL; one verb (verify) proves it; one verb (down) or an expired lease destroys it verifiably.

Unopinionated about the application. Opinionated about the lifecycle.

Built for AI agents first. A human at a terminal is a guest in an interface shaped for machines — do not drive stacks by hand.

Why

Agent fleets need many simultaneous, isolated, ephemeral instances per day. Container tools, IaC, and provider CLIs each own a layer and none of the whole — so every team rebuilds naming, wiring, teardown, and cost hygiene, and rediscovers the same failure modes.

stackless is that glue: the lifecycle layer between an agent and the stack it works on. An agent handed a repo with stackless.toml runs up, gets a working named URL, proves health, walks away; within the lease window it is gone, verifiably. No wiki, no teammate, no manual cleanup.

Invariants and the trust boundary: VISION.md.

How

Install

Binary:

curl --proto '=https' --tlsv1.2 -LsSf \ https://github.com/snowmead/stackless/releases/latest/download/stackless-installer.sh | sh

Re-running the installer upgrades in place. When installed via that shell installer (install receipt present), the CLI also auto-updates from GitHub Releases on ordinary use and via stackless update. Set STACKLESS_NO_SELF_UPDATE=1 to disable.

Agent skill:

bunx skills add snowmead/stackless --skill stackless -g

Lifecycle

stackless check stackless.toml --on local --json
stackless up --name demo --on local --json
stackless verify demo --json
stackless down demo --json
  • --on <substrate> is required at creation. Supported hosts and catalog integrations: PROVIDERS.md. Resume by name; substrate is fixed after create.
  • Cloud needs provider API keys (see stackless doctor); paid resources need --confirm-paid.
  • Local edit loop: --source svc=/path pins a service to a checkout (cloud rejects --source).
  • Integrations via [integrations.*] provision through Stripe Projects (every provider in the catalog registry is first-class). Authoring: init / adopt, then check. Full stackless.toml reference: docs/SCHEMA.md.

Machine contract

  • stdout — final envelope: { "ok": true, … } or { "ok": false, "error": { … } }.
  • stderr — NDJSON progress events during up --json.
  • Every error carries what failed, why (observed), and remediation. Branch on error.code only.

Fleets, parallel agents, and MCP: docs/AGENT-FLEETS.md.

Verbs

VerbDoes
up [--name]Create or resume; --on required at creation
down <name>Verified teardown
verify <name>Run proof contract; renews lease
status / listStaged truth / all instances
logs <name>Captured output (survives teardown)
check <file>Validate definition + derived graph
bindCompile stackless.toml → IDL + typed language bindings
init / adopt / doctorScaffold, detect, preflight

Every command is non-interactive and exits with codes an agent can branch on.

Typed bindings

stackless bind projects a stack definition into a language-neutral IDL (.stackless/stack.idl.json) and typed bags so tests can name services, verify tiers, and integration outputs without stringly DNS keys. Emitters cover Rust, TypeScript, Go, and Python. Each language gets Origins / bindOrigins, Integrations / bindIntegrations, SECRETS_REQUIRED, and VerifyTier when declared. Language identifiers are computed at emit time from DNS wire names and provider output keys (not stored as language idents in the IDL).

stackless bind --file stackless.toml \
--idl .stackless/stack.idl.json \
--emit typescript=e2e/stack.gen.ts \
--emit rust=tests/support/stack_bind.rs \
--emit go=internal/stack/origins.go \
--emit python=tests/stack_bind.py
# Aliases still work: --ts PATH, --rs PATH# Go package defaults to stacklessbind; override with --go-package NAME# CI: fail if any output is stale
stackless bind --file stackless.toml \
--idl .stackless/stack.idl.json \
--emit typescript=e2e/stack.gen.ts \
--emit rust=tests/support/stack_bind.rs \
--check

Rust build.rs consumers that already check in the IDL can regenerate only $OUT_DIR via stackless-bindgen (no stackless-core / libsql link):

fnmain() -> Result<(),Box<dyn std::error::Error>>{
stackless_bindgen::emit_rust(".stackless/stack.idl.json")?;Ok(())}

Language SDKs

Published packages for Rust, TypeScript, Python, and Go — same lifecycle verbs (up / verify / down / …), same envelopes. Versioned in lockstep with the CLI; publish runbook: docs/PUBLISHING.md.

LanguagePackageSource
Ruststackless (crates.io)crates/stackless
TypeScriptstackless-sdk (npm)sdks/typescript
Pythonstackless-sdk (PyPI)sdks/python
Gosdks/gosdks/go
# Rust
[dependencies]
stackless = "0.2"
# TypeScript
npm i stackless-sdk
# Python (import stackless)
pip install stackless-sdk
# Go
go get github.com/snowmead/stackless/sdks/go@v0.3.3

All clients need the stackless CLI on PATH (or STACKLESS_BIN) for the operator daemon. Rust can also embed a hermetic daemon via feature test-support / TestContext. Non-Rust packages speak the CLI JSON protocol (sdks/PROTOCOL.md).

use stackless::{Client,Create,UpRequest};let client = Client::system()?;let created = client.up(UpRequest::Create(Create::new("stackless.toml","local").named("demo"),))?;println!("{}", created.origin("web")?);
client.verify(&created.name,None)?;
client.down(&created.name)?;
import{Client}from"stackless-sdk";constclient=Client.system();constup=awaitclient.up({kind: "create",name: "demo",on: "local",file: "stackless.toml",});console.log(up.origins.web);awaitclient.verify(up.instance);awaitclient.down(up.instance);

up returns service origins and, when present, nested integration outputs. Prefer verify-tier env interpolation when secrets must not appear on stdout. Product test harnesses (Playwright fixtures, etc.) belong in the application repo; stackless stops at Client + bind + delivery.

Development

Activate mise tools (mise install, then mise exec -- or an activated shell). Gates live in mise.toml[tasks]:

TaskDoes
mise run checkfmt + clippy + taplo
mise run testcargo nextest run --workspace --all-features
mise run cicheck + test + supply-chain audit/deny/vet

Plain cargo build / cargo test also work. Architecture: ARCHITECTURE.md. Supported providers: PROVIDERS.md. Contributor map and provider tooling: CLAUDE.md. Releases: CHANGELOG.md. Cursor Cloud notes: AGENTS.md.

License

Licensed under either of:

at your option.

About

Ephemeral software stacks for agents to run and test end to end.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages