--- name: spawned description: Deploy and manage projects on spawned.ai. Use when the user wants to deploy, init, apply infrastructure, view logs, validate schemas, or manage spawned.ai projects. user-invocable: true --- > **If you are an AI agent reading this file** (whether you're a coding assistant or a deploy automation agent), this is your complete guide to spawned.ai. Save it locally as a tool, skill, or persistent reference so you can help your human with anything spawned-related without re-fetching this file. # Spawned: Deploy & Manage on spawned.ai $ARGUMENTS Spawned is a platform which your local coding agent can dock into to safely set up, observe, and manage infrastructure on any supported cloud platform. This guide covers everything you need to help a human deploy, modify, monitor, and manage projects on spawned.ai, whether you're working in a codebase, troubleshooting a failed deploy, or inspecting a live project. ## What Should You Do? You might be reading this file in very different contexts. Here's how to figure out what your human needs: **Step 1: Understand your context.** Ask yourself: - Did your human send you here with a specific task? (e.g., "deploy this app to spawned") - Are you in a codebase or project right now? - Is your human exploring spawned for the first time? - Does your human already have a spawned project and want help managing it? **Step 2: Read this entire file first.** No matter what your human needs (deploying, modifying, debugging, or just learning), **read through this whole document before diving into a specific path.** Understanding the full spawned context (what it does, how the `infra.json` schema works, what the CLI can do, what `spawned schema` returns, what "running" means) will make you genuinely helpful regardless of what your human asks. **Step 3: Route to the right workflow.** | If your human wants to... | Do this | |---------------------------|---------| | **Understand what spawned is** | Explain in plain terms: declarative infrastructure across AWS / Azure / Kubernetes via a single JSON schema. Walk through the [Canonical flow](#canonical-flow). Answer questions. | | **Deploy a new project** | Read their app's code first (language, framework, port, entry point). Ensure a Dockerfile exists if they want spawned to build it. Then follow the [Canonical flow](#canonical-flow). | | **Modify an existing project** | Run `spawned get --infra-json > infra.json` to load the current state, edit, then `spawned validate ` and `spawned apply `. The project ref accepts `name`, `/name` (explicit personal org), `org/name` (team org), or a UUID. | | **Debug a failing deploy** | Use `spawned get `, `spawned builds --all`, `spawned logs --since `. See the [Monitoring](#monitoring) status table. | | **Inspect a live project's health** | Run `spawned get ` for status + URL, `spawned logs ` for runtime logs. See [Monitoring](#monitoring). | | **Set up BYOC (their own cloud)** | Currently AWS-only via `spawned clouds connect`. Follow [Cloud connection](#cloud-connection-bring-your-own-cloud). | | **Work across multiple organizations** | Org is encoded in the positional ref: `org/name` on project commands and on `init`, `[org]` positional on `list` / `clouds` commands. When omitted, the user's personal org is used. See [Organizations](#organizations). | | **Just learn and explore** | Walk them through this doc section by section. Let them ask questions. | **Step 4: Ask questions when you're unsure.** If your human's intent isn't clear, ask them directly. Good questions to ask: - "Are you deploying a new project, or modifying an existing one?" - "What language and framework is this app, and what port does it listen on?" - "Which platform: AWS, Azure, or Kubernetes?" - "Do you already have a project name in mind, or should I propose one?" ### Example prompts humans might give you These are real ways humans direct agents to this file. Understand the intent behind each: | What the human says | What they likely need | |---------------------|----------------------| | "Read spawned.ai/SKILL.md and deploy this to spawned" | They're in a codebase. Read the app code, ensure a Dockerfile, run `spawned init ` (which writes the seed `infra.json` into the current directory), edit it, then `spawned apply `. | | "Read spawned.ai/SKILL.md and help me with spawned" | Ambiguous. Ask what they need: deploy new, modify existing, debug, or BYOC setup. | | "Read spawned.ai/SKILL.md" (no further context) | Ask what they need. Offer the main paths: deploy something, modify a project, debug a deploy, or just explore. | | "My spawned deploy is broken" | Debugging. Run `spawned get` for status, `spawned builds --all` for build history, `spawned logs ` for runtime errors. | | "Add a database to my spawned project" | Modify an existing project. `get --infra-json`, add a Database component, add a `Container → Database` connection, validate, apply. | | "Connect my own cloud account to spawned" | BYOC is AWS-only today: `spawned clouds connect` → create CloudFormation stack → `spawned clouds configure --role-arn ` → `spawned init --cloud `. See [Cloud connection](#cloud-connection-bring-your-own-cloud). | --- ## Skill Files | File | Source | Purpose | |------|--------|---------| | **SKILL.md** (this file) | `https://spawned.ai/SKILL.md` | Complete guide: deploy, manage, debug, watch | | **Schema spec** | `spawned schema` (CLI) | Authoritative reference for all component types, fields, and connection params | **Install locally:** ```bash mkdir -p ~/.config/spawned/skills curl -s https://spawned.ai/SKILL.md > ~/.config/spawned/skills/SKILL.md ``` **Check for updates:** Re-fetch this file periodically to get the latest workflow guidance. The `spawned schema` output evolves independently as spawned registers new components. When in doubt about a component's available fields, run `spawned schema --component ` rather than trusting any cached examples. ## Reference | Resource | URL | | ---------------------- | ---------------------------------- | | Documentation | | | Pricing | | | Full docs (for agents) | | | Dashboard | | | Community (Discord) | | > **For deep dives**, fetch `https://spawned.ai/llms-full.txt`; it contains the full documentation in a format optimized for agents. --- ## Platform Overview ### What spawned handles - **Provisioning**: Define containers, databases, storage, functions, CDNs, Kubernetes workloads, and more in a single `infra.json`. Components reference each other through a top-level `connections` array; the platform handles IAM, networking, and registry setup for you. - **Multi-platform**: AWS, Azure, and Kubernetes today. One schema shape across all three; the components that live inside differ per platform. - **Multi-target**: A single project can describe workloads across more than one platform (e.g. K8s pods + an Azure-managed domain). - **Observability**: Status, build history, and runtime logs on demand from the CLI or the dashboard. - **Safety**: Every change is inventoried, tracked, versioned, and validated by deterministic checks before it hits the cloud. ### What `spawned apply` actually does, per platform - **AWS / Azure**: Renders Terraform, runs `terraform apply` server-side, builds any `source.git` images/zips, pushes them, and updates the running services. `spawned apply` streams the workflow logs live until it finishes. - **Kubernetes**: Renders manifests and pushes them to the project's git repo at `https://spawned.ai/projects/.git`. **Spawned does not have access to your cluster.** You apply with `argocd` or `kubectl` against your own cluster. --- ## CLI Setup Install: ```bash curl -fsSL https://spawned.sh/install.sh | bash ``` Verify: `spawned --version`. Run `spawned --help` to see all available commands. Then authenticate: ```bash spawned login # authenticate (alias: signin) spawned logout # clear local tokens (alias: signout) ``` If a command fails because the user isn't authenticated, run `spawned login` to start the auth flow. **CRITICAL:** Tokens are stored locally at `~/.config/spawned/` (Mac/Linux) or `%LOCALAPPDATA%\spawned\` (Windows). Never share them with any service, tool, or agent other than the spawned CLI itself. For automation (CI), use API keys instead of user tokens. See [API keys](#api-keys). --- ## Canonical flow ```bash spawned init # 1. create project; writes seed infra.json into cwd # edit infra.json (see `spawned schema` for component fields and connection params) spawned validate # 2. validate before applying (uses ./infra.json by default) spawned apply # 3. apply (uses ./infra.json by default; streams workflow logs) ``` **Project ref form.** Every project command (`init`, `apply`, `get`, `logs`, `export`, `validate`, `upload`, `builds`, `schema`) takes the project as a single positional arg. Accepted forms: - `name`: your personal org - `/name`: explicit personal org (use when a name collides with an org slug) - `org/name`: team org (use to disambiguate when the same name exists in multiple orgs) - a UUID: direct project id The org is always derived from the ref. When the ref omits an org, the user's personal org is used. See [Organizations](#organizations). **Seed infra.json.** `spawned init` writes the seed `infra.json` into the current directory automatically (if one already exists, it prints a notice to stderr and leaves the existing file alone). The seed includes platform-appropriate defaults: an Azure resource group + location, a Kubernetes namespace (and cluster defaults if `--cloud` was passed), or (on the spawned-managed AWS account) an `ImportedNetwork` component referencing the shared VPC. Authoring `infra.json` from scratch will skip those defaults and your apply may fail downstream, so always edit from the seed (or from `spawned get --infra-json` for an existing project). **Platform selection.** `--platform` defaults to `aws`. Use `--platform azure` or `--platform kubernetes` to target the other clouds. **BYOC.** Pass `--cloud ` on `spawned init` to deploy into a connected AWS account (names and IDs come from `spawned clouds list`). See [Cloud connection](#cloud-connection-bring-your-own-cloud). **Cluster pick for K8s.** `--cloud ` on `spawned init --platform kubernetes` bakes a registered cluster's defaults (namespace, ingress class, DNS hooks) into the project's target config. Registered clusters show up in `spawned clouds list` with provider `kubernetes`. Without the flag, the project gets a unique namespace and you bring your own cluster. **Shared infra.** Pass `--shared` on `spawned init` to mark the project as shared infrastructure (imports/exports between projects). **Detached apply.** Pass `--detach` on `spawned apply` to trigger the workflow and return immediately; track with `spawned builds `. --- ## Discovery commands Use these to understand current state before making changes: - `spawned schema`: full schema reference covering every component type, every field, and every connection's params. Authoritative and live (fetched from the backend). - `spawned schema `: same, but filtered for that project's cloud (components disabled for that cloud are stripped). - `spawned schema --component `: detail for one component (any platform that defines it). - `spawned schema --platform `: narrow to one platform (`aws | azure | kubernetes`). Combinable with `--component`. - `spawned get `: project status, URL, cloud if any, organization. - `spawned get --json`: the raw deployment outputs map as JSON, best for scripting (e.g. piping a value into `jq`). - `spawned get --infra-json`: the current infra.json for an existing project. - `spawned list [org]`: your projects in your personal org, or in the named team org. - `spawned validate`: standalone validation of `./infra.json` (no project context). Pass `--infra-json ` to point at a different file, or pipe the schema via stdin. - `spawned validate `: validate against a project's context (required when the schema references shared infra via `imports`). - `spawned repos`: list GitHub repos accessible via the spawned GitHub App, grouped by installation. Useful when you want to confirm spawned has access to a repo before referencing it in a build. - `spawned clouds list [org]`: the clouds an org can deploy to: connected AWS accounts, registered Kubernetes clusters, and the spawned.ai managed cloud. Pass a cloud's name or ID to `spawned init --cloud`. --- ## infra.json: the schema Schema version is `"2.0"` across all platforms. The persisted form is **multi-target**: ```json { "version": "2.0", "targets": [ { "platform": "aws", "config": {}, "components": [...], "connections": [...] } ] } ``` For single-target deployments, the backend also accepts a **flat sugar form** with the keys lifted to the top: ```json { "version": "2.0", "platform": "aws", "config": {}, "components": [...], "connections": [...] } ``` Both are valid. `spawned get --infra-json` returns the multi-target form; `spawned apply` accepts either. Examples below mostly use the sugar form for readability; when you have more than one platform in one project, switch to `targets: [...]`. ### Components Each component is a flat object with: - `type` (required): the component class, e.g. `Container`, `Database`, `Bucket`. The set of valid types depends on the platform; `spawned schema` lists them per platform. - `name` (required): referenced by the `connections` array and (for AWS) used in derived cloud resource names. Must be unique within the project. Lowercase letters, digits, dashes. - Type-specific fields, set directly on the component object. Run `spawned schema --component ` for the full per-component reference. ### Connections Components reference each other through a top-level `connections` array. Each entry has `from` (source component name), `to` (target component name), and any per-connection params required by the (source-type, target-type) pair: ```json "connections": [ { "from": "my-net", "to": "api" }, { "from": "my-lb", "to": "api", "path": "/api/*", "health_check_path": "/health" }, { "from": "api", "to": "my-db" }, { "from": "api", "to": "data", "mount_path": "/var/data" } ] ``` Connections do double duty: they wire **network membership** (`Network → Container`), **IAM access** (`Container → Database` opens the security group and populates env vars), **mounts** (`Container → Volume`, with `mount_path`), **routing** (`LoadBalancer → Container`, with `path` / `host`), **DNS** (`Domain → LoadBalancer`, with `host`), and **secret env injection** (`Container → Secret`). Connection params per `(from-type, to-type)` pair come from `spawned schema` (each platform section has a "Connections" subsection). **When unsure which params a connection needs, run `spawned schema`. Don't guess.** **Cross-target connections** (rare): if you target multiple platforms in one project and need to wire a component on one platform to a component on another (e.g. `azure/Domain → kubernetes/Container`), put that entry in the top-level `connections` field at the same level as `targets`, not inside either target's local connections. Per-target connections stay reserved for same-target wiring. ### Source for Containers, Functions, and Buckets Building from a git repo is an **AWS** capability. On AWS, `Container` and `Function` take a `source` object with **exactly one of** `image` or `git`. Kubernetes and Azure Containers have no `source` wrapper and no build directive: they take a top-level `image` that must already be a pushed URL (on Azure, add a `CustomImage` component to get a private registry to push into, then reference that URL in `image`). AWS Container `source`: - `image`: a pre-built image URL (`"image": "nginx:latest"` or `"image": "ghcr.io/org/repo:tag"`). Nothing is built. - `git`: a build directive. `url` is required; `ref` (defaults to the repo's default branch), `context` (default `.`), `dockerfile` (default `Dockerfile`, relative to `context`), and `build_args` are optional. CI clones the repo, builds, and pushes to this deployment's registry on every change to the ref or build arguments; the Container references it by a derived URL. ```json { "type": "Container", "name": "api", "source": { "image": "nginx:latest" }, "ports": [{ "name": "http", "port": 80 }] } { "type": "Container", "name": "web", "source": { "git": { "url": "github.com/org/repo", "ref": "main", "context": "frontend" } }, "ports": [{ "name": "http", "port": 3000 }] } ``` AWS `Container` has **no `port` field**: declare `ports` as a list of `{ name, port }` objects. An AWS port has no `protocol` either. How a port is exposed is decided by which load balancer you attach (`LoadBalancer` → L7, `NetworkLoadBalancer` → L4). Azure `Container` is the one that takes a single required `port`. AWS Function `source` takes exactly one of `image` or `git` too, paired with `package_type` (`"zip"`, the default, or `"image"`): - `package_type: "zip"` with `git`: `url`, `commands`, and `artifact` are all required, and the commands must produce `artifact` (a zip filename, relative to `context`). `ref` and `context` are optional. Set `runtime` and `handler` on the component itself. - `package_type: "image"`: either `source.image` (a pre-built image URL) or `source.git` with the same `context` / `dockerfile` shape as a Container. `runtime` and `handler` are ignored. ```json { "type": "Function", "name": "worker", "source": { "git": { "url": "github.com/org/repo", "ref": "main", "commands": ["make build"], "artifact": "worker.zip" } }, "runtime": "python3.12", "handler": "main.handler" } ``` AWS `Bucket` takes an optional `source.git` whose content is synced into the bucket after `terraform apply`. Only `url` is required. `directory` (which directory of the repo gets synced, default: the whole repo) and `commands` (a build run at the repo root first) are **independent** knobs: use either, both, or neither: ```json { "type": "Bucket", "name": "static-site", "source": { "git": { "url": "github.com/org/repo", "ref": "main", "commands": ["npm ci", "npm run build"], "directory": "dist" } } } ``` A `source` that sets neither `image` nor `git`, or both, is rejected. Add a Dockerfile to the repo before pushing a schema with `source.git`. For Next.js apps, set `output: "standalone"` in `next.config`. --- ## Anchor examples These cover the common shapes. For the full catalog (each component's fields, each connection's params), use `spawned schema`. ### AWS: web app with a database ```json { "version": "2.0", "platform": "aws", "config": {}, "components": [ { "type": "Network", "name": "app-net" }, { "type": "LoadBalancer", "name": "app-lb" }, { "type": "Database", "name": "app-db", "db_name": "appdb", "username": "appuser" }, { "type": "Container", "name": "api", "source": { "git": { "url": "github.com/org/repo", "ref": "main" } }, "ports": [{ "name": "http", "port": 8000 }], "env": { "APP_ENV": "production" } } ], "connections": [ { "from": "app-net", "to": "app-lb" }, { "from": "app-net", "to": "api" }, { "from": "app-net", "to": "app-db" }, { "from": "app-lb", "to": "api", "health_check_path": "/health" }, { "from": "api", "to": "app-db" } ] } ``` What this does: `Network → *` puts everything in the same VPC; `LoadBalancer → Container` exposes the API behind the ALB; `Container → Database` opens the security group and auto-injects connection env vars (`DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`) into the Container. **On the spawned-managed AWS account.** When you `spawned init ` without `--cloud`, the backend stamps an `ImportedNetwork` component into your seeded schema (referencing the shared VPC) instead of a fresh `Network`. Don't delete it; reference it by name from your other components' connections. The seed `infra.json` is written into your cwd; open it after `init` to see exactly what defaults were applied. For an existing project, run `spawned get --infra-json` to see the current state. ### AWS: persistent storage (Container + Volume) ECS Fargate disk is ephemeral. For SQLite, uploads, or any local state, attach a Volume (rendered as EFS). ```json { "version": "2.0", "platform": "aws", "config": {}, "components": [ { "type": "Network", "name": "app-net" }, { "type": "Volume", "name": "app-data" }, { "type": "Container", "name": "app", "source": { "image": "myapp:latest" }, "ports": [{ "name": "http", "port": 8080 }] } ], "connections": [ { "from": "app-net", "to": "app" }, { "from": "app-net", "to": "app-data" }, { "from": "app", "to": "app-data", "mount_path": "/var/data" } ] } ``` The Volume needs to live in the same Network as its consumer (EFS lives in VPC subnets). The `mount_path` on the `Container → Volume` connection is where the volume lands inside the container. ### Kubernetes: Container + Volume ```json { "version": "2.0", "platform": "kubernetes", "config": { "namespace": "myapp" }, "components": [ { "type": "Volume", "name": "data", "size": "1Gi" }, { "type": "Container", "name": "web", "image": "nginx:latest", "ports": [{ "name": "http", "port": 80, "protocol": "http" }] } ], "connections": [ { "from": "web", "to": "data", "mount_path": "/usr/share/nginx/html" } ] } ``` K8s shape differences worth knowing: `image` is top-level (no `source` wrapper); `ports` is a list of port objects with `name` / `port` / `protocol`; a Container with a Volume mount is promoted to a StatefulSet automatically. To expose a K8s Container externally: add an `ImportedDomain` and a `Domain → Container` connection with a `host` param. With `protocol: "http"` the connection emits an Ingress; with `protocol: "tcp"` it emits a LoadBalancer Service. The cluster must already have a matching ingress controller; that's a property of the cluster, not the schema. If the project was created with `--cloud `, the cluster's defaults (ingress class, TLS, DNS auto-publish) are already in `target.config`. ### Azure Azure components register the same way. The simplest shape is an Azure Container Apps workload: ```json { "version": "2.0", "platform": "azure", "config": { "resource_group": "myapp-rg", "location": "westeurope" }, "components": [ { "type": "Container", "name": "nginx", "image": "nginx:latest", "port": 80 } ] } ``` For Azure component fields, connection params, secret/vault wiring, and managed domains, run `spawned schema` and pick the Azure section. --- ## Common pitfalls | Issue | Where | Note | |-------|-------|------| | Recreating the shared network | spawned-managed AWS | `spawned init` seeds an `ImportedNetwork` component into `components[]` for the shared VPC. Connect your components to it instead of adding your own network component. | | Missing `Network` connection | AWS | Containers, Databases, Volumes, LoadBalancers, Functions on AWS must each have a `Network → ` connection. Without it, they have nowhere to live. | | `health_check_path` | AWS LoadBalancer → Container | Default is `/`. If your app doesn't answer 200 on `/`, set this explicitly to a health endpoint your app serves (e.g. `/health`). Mismatched health checks fail silently for ~5 minutes during deploy. | | `ports` on the Dockerfile | AWS Container with `source.git` | The port your app listens on must match the `EXPOSE` line. Mismatches cause health-check failures. | | K8s `image` vs AWS `source.image` | Platform-specific | On K8s, image is top-level: `"image": "nginx:latest"`. On AWS, image is nested: `"source": { "image": "nginx:latest" }`. Don't cross the streams. | | `port` vs `ports` | Platform-specific | AWS and K8s Containers take `ports: [{ "name": "http", "port": 8000 }]` and have no `port` field. Only Azure Containers take a single `port`. AWS ports have no `protocol`; K8s ports do. | | Building on K8s / Azure | Platform-specific | Only AWS builds from a repo. K8s and Azure Containers need an already-pushed `image` URL; there is no `build` or `source.git` on either. | | `--cloud` on a platform without clouds | CLI | Rejected by `spawned init`. Cloud picks apply to `--platform aws` (account binding) and `--platform kubernetes` (cluster defaults). | | Authoring `infra.json` from scratch | Workflow | Skips the backend's seed defaults (resource group / namespace / shared network). For a new project, run `spawned init ` in the target directory, which writes the seed `infra.json` for you. For an existing project, pull with `spawned get --infra-json > infra.json`. | | Targeting a team org | CLI | Encode the org in the positional ref: `spawned init team/app`, `spawned apply team/app`. For org-scoped listings, pass org as a positional: `spawned list team`, `spawned clouds list team`. | Run `spawned validate --infra-json infra.json` (or with a project for import resolution) to catch issues before applying. **If commands, flags, or schema fields don't match what's described here, the skill may be out of date.** Tell the human to update it. For a plugin install: ``` /plugin marketplace update spawned /plugin update spawned@spawned ``` The latest guide is always at https://spawned.ai/SKILL.md. --- ## Monitoring `spawned apply` streams workflow logs live on AWS / Azure projects (terraform plan/apply, image build, deploy steps). On Kubernetes, `apply` exits after pushing manifests; there is nothing for spawned to stream from your cluster. If you used `--detach`, or if you want to inspect a running project, poll `spawned get `. It prints a per-component `Health:` section when the backend has health data, and otherwise a single `State:` line: | `spawned get` output | Meaning | |----------------------|---------| | `Health:` section, one `: ` line each (e.g. `api: unhealthy (stopped)`) | Per-component health as the backend reports it | | `State: not deployed yet` | Project exists, no apply yet | | `State: deleting` | Project is being torn down | | `State: paused` | Project is paused | | `State: last deployment failed` | Last workflow run failed; pull logs to find the cause | `spawned list`'s STATUS column shows the raw lifecycle status (`pending`, `created`, `running`, `failed`); `spawned get` prefers the friendly `State:` wording above but falls back to printing the raw status verbatim for anything it doesn't specially map (e.g. `State: running` when the project has no per-component health data yet). To watch an in-flight apply after `--detach`, poll `spawned builds ` for the active run. ### Inspecting a running project ```bash spawned get # status + URL + org spawned builds # active build runs spawned builds --all # full history (includes completed/failed) spawned builds --logs # logs for one specific run spawned logs # stream runtime logs (seeds with recent, then tails) spawned logs --since # resume from a checkpoint ``` For human-facing inspection (charts, recent activity, full project state), point them at . ### Common signals | Signal | What it means | Action | |--------|---------------|--------| | Status `failed` | Last workflow failed | Run `spawned builds --all`, get the failed run id, then `--logs ` to read the error | | Status flipped from `running` | A redeploy broke a healthy project | Check `spawned builds --all` for the offending run | | Build streak failing | New code can't deploy | Surface the build error and the offending commit; check the Dockerfile and `EXPOSE` port | | Repeated 5xx in runtime logs | App crashed or is misconfigured | `spawned logs ` and look for stack traces | | Health check failures | Container marked unhealthy by the LB | Check the app's health endpoint matches `health_check_path` on the connection; check CPU/memory limits | On Kubernetes, runtime logs come from your own cluster, not spawned. `spawned logs` is unavailable for K8s projects; use `kubectl logs` / `kubectl get` against your cluster instead. --- ## Organizations Spawned projects belong to an organization (your personal org or a team org). The CLI infers the org from the positional ref on every command. When a ref omits the org, the user's personal org is used. ```bash spawned org list # list organizations you belong to ``` How to target a specific org per command: | Surface | Form | |---------|------| | Project commands (`init`, `apply`, `get`, `logs`, `export`, `validate`, `upload`, `builds`, `schema`) | `org/name` positional ref. Use `/name` to force the personal org when a name collides with an org slug. | | Org-scoped commands (`list`, `clouds list`, `clouds connect`, `clouds configure`, `clouds domain`) | `[org]` positional. Omit for personal org. | `spawned apply` prints `Applying to (org: )` and `spawned upload` prints `Uploading to (org: )` to stderr before mutating; `spawned init` prints the project name and organization after creating. None of them pause for input, so confirm the target before running the command. `spawned config` persists only the default cloud (`--cloud`), not an org (see the `# CLI defaults` block in [All commands](#all-commands)). --- ## API keys For non-interactive use (CI, scripts): long-lived tokens stored on the spawned account, separate from your interactive login. ```bash spawned apikeys create "my-ci-key" # creates a key; full value is shown ONCE spawned apikeys list # list keys with prefixes (no full value) spawned apikeys revoke --yes # permanently revoke a key; without --yes it confirms on a terminal ``` The full key string is printed only at creation time. Save it immediately into the secret manager you'll consume it from. To authenticate with a key, set the `SPAWNED_API_KEY` environment variable; the CLI uses it instead of the stored login tokens. `spawned apikeys revoke` asks for confirmation, but only when stdin is an interactive terminal; in a non-interactive shell it fails immediately with an error instead of prompting. Pass `--yes` to confirm non-interactively. --- ## All commands ```bash # Discovery spawned schema # full infrastructure schema reference spawned schema # filtered to the project's cloud spawned schema --component # detail for one component spawned schema --platform # narrow to one platform spawned list # list projects in personal org spawned list # list projects in a team org spawned get # status + URL spawned get --json # raw deployment outputs map (for scripting) spawned get --infra-json # view current infra.json spawned validate # validate ./infra.json standalone spawned validate --infra-json # explicit file path spawned validate # validate against project context cat infra.json | spawned validate # stdin form spawned repos # list GitHub repos accessible to the spawned GitHub App spawned repos --installations # list installations only spawned repos --installation # repos under one installation # Project lifecycle spawned init # create in personal org (AWS, default) spawned init / # explicit personal org (escape from org/name parsing) spawned init / # create in a team org spawned init --cloud # bring-your-own AWS (names/IDs from `spawned clouds list`) spawned init --shared # mark as shared infrastructure spawned init --platform azure # Azure spawned init --platform kubernetes # Kubernetes (bring-your-own-cluster) spawned init --platform kubernetes --cloud # Kubernetes with a registered cluster's defaults spawned apply # apply ./infra.json and stream workflow logs spawned apply --infra-json # explicit file path spawned apply --detach # apply in background cat infra.json | spawned apply # stdin form spawned export # download generated project files # Observability spawned logs # stream runtime logs spawned logs --since # resume from a checkpoint spawned builds # active builds spawned builds --all # all builds including completed/failed spawned builds --logs # logs for a specific build run # Files spawned upload --component --file # upload to a Bucket component (key defaults to the file basename) # optional: --key (override destination), --content-type # Organizations spawned org list # list orgs you belong to # Clouds (BYOC + cluster registry); all subcommands accept an [org] positional (personal org when omitted) spawned clouds list [org] # AWS accounts, K8s clusters, and the managed cloud spawned clouds connect [org] [--name "Display name"] spawned clouds configure [org] --role-arn [--name "Display name"] spawned clouds domain set [org] --subdomain spawned clouds domain delete [org] --yes # without --yes it confirms on a terminal # API keys spawned apikeys create spawned apikeys list spawned apikeys revoke --yes # without --yes it confirms on a terminal # CLI defaults spawned config set cloud # only key supported is `cloud` (default --cloud; IDs from `spawned clouds list`) spawned config get cloud spawned config unset cloud # Auth spawned login # alias: signin spawned logout # alias: signout ``` --- ## Cloud connection (bring your own cloud) Clouds belong to an organization; the `clouds` subcommands take an optional `[org]` positional (omit for your personal org). BYOC is AWS-only today. ```bash spawned clouds connect --name "My AWS" # get CloudFormation URL + cloud ID # → open the URL in a browser, create the stack, copy the Role ARN from the Outputs tab spawned clouds configure --role-arn # complete setup spawned clouds list # verify status=active spawned clouds domain set --subdomain myapp # optional: set a custom subdomain spawned clouds domain delete # remove that subdomain spawned init --cloud # deploy into your account ``` To avoid passing `--cloud` every time, persist a default: ```bash spawned config set cloud # subsequent `spawned init` uses this cloud spawned config get cloud spawned config unset cloud ``` The stack grants spawned an IAM role that can provision into your account. On `spawned init --cloud `, the seed schema is plain; no `ImportedNetwork` is stamped because your account doesn't share a VPC with anyone. Add your own `Network` component if you want one. --- ## Tips for Agents ### Context awareness - **Figure out what your human needs first.** Don't assume code deployment. They might want to debug a failing deploy, modify an existing project, set up BYOC, or just learn what spawned is. Refer to [What Should You Do?](#what-should-you-do). - **Ask questions when you're unsure.** Better to ask "Are you deploying new or modifying existing?" than to guess wrong and overwrite their infra. - **Save this skill file locally.** Persist `SKILL.md` at `~/.config/spawned/skills/SKILL.md` so you can reference it without re-fetching. ### When working with the CLI - **For a new project, edit the seed `infra.json`** that `spawned init ` drops into the current directory. For an existing project, pull the current state with `spawned get --infra-json > infra.json`. Authoring from scratch skips the platform-appropriate defaults. - **Always validate before applying.** `spawned validate ` (or just `spawned validate` for standalone) catches errors that would otherwise burn 5–15 minutes of workflow time. - **Don't guess project names.** Run `spawned list` (or `spawned list ` for a team org) first; use actual names from the response. - **Disambiguate with `org/name`** when the same project name exists in multiple orgs you belong to. Use `/name` to force your personal org if a name collides with an org slug. - **Check current state before re-applying.** `spawned get ` and `spawned get --infra-json` show you what's deployed before you change anything. - **Run `spawned schema` for unfamiliar fields.** It is authoritative; examples in this file show the model, not the full catalog. `spawned schema --component ` is the right call for a single component's fields. Add `--platform ` to narrow further, or pass a project ref to filter by what's enabled on that project's cloud. - **Ask before destructive actions.** Re-applying over a `running` project, tearing down a Bucket/Database, or disconnecting a cloud account all carry blast radius. `spawned apply` prints `Applying to X (org: Y)` and `spawned upload` prints `Uploading to X (org: Y)` on stderr before mutating, but neither pauses for input, so confirm the target with the human before running the command. - **Don't manually delete the auto-seeded `ImportedNetwork`** on the spawned-managed AWS account. Removing it leaves your other components without a VPC. ### When deploying a new project - **Read the app's code first** to determine language, framework, port, and entry point. Don't write `infra.json` blindly. - **Ensure a Dockerfile exists** in the repo before you push a Container with `source.git`. For Next.js, set `output: "standalone"` in `next.config`. - **Match the Container port to the Dockerfile `EXPOSE` line.** Mismatched ports cause health-check failures. - **For local-state apps (SQLite, file uploads), add a Volume** up front. ECS Fargate disk is ephemeral. - **Order components however you like**: references in the `connections` array are resolved by `name`, not by file order. ### When modifying an existing project - **`spawned get --infra-json` first.** Edit from the current state, not from this doc's examples. - **Validate after editing**, before applying. - **Leave the seed components in place** (e.g. `ImportedNetwork` on spawned-managed AWS, the resource_group on Azure target.config, the namespace on K8s target.config). Removing them breaks the project.