Skip to content
Spinbox is experimental: APIs change and data may be lost. Status lists what is built and what proves it.

Installation config file

View Markdown

The file an installation starts from - installation_config in Terraform. It seeds the control plane’s database once; after that the dashboard edits the installation, and a later change to the file shows up there as a proposal to apply or dismiss. Every block is optional. Check a file before applying it with controlplane config check <file>.

spin.yaml
# An installation's configuration, as a file to keep in git.
#
# controlplane config check spin.yaml # what it declares; opens no database
#
# The file seeds an installation: when a control plane starts, each item here the installation
# does not have yet is created from it, once. From then on the installation is its database, and
# the dashboard edits all of it. A later file that says something else of an item - a changed
# default, a policy taken out - is not written: the dashboard shows it as a change to apply or
# dismiss, and remembers the answer until the file changes again.
#
# Every block is optional.
settings:
# What a workspace may be created from. Declared and empty is refused: it would allow
# nothing at all. Leave the key out for the installation's default.
allowed_images:
- ghcr.io/acme/sandbox:v1
# Zero is no limit.
max_workspaces_per_user: 10
# Who a workspace's identity token may be for - a cloud's STS, a Vault, a cluster's API server.
# Leave the key out for anybody; a relying party still decides which workspaces it admits.
identity_audiences:
- sts.amazonaws.com
# How many days a deleted workspace stays in the trash - its disk, snapshots and keys kept - for
# its owner to undelete, before it is erased. 0 is no trash: a delete erases at once. At most 90.
workspace_trash_days: 7
# How far behind the object store a workspace's disk may fall, in seconds, where its preset does
# not say: what a host lost without warning may lose of a workspace that writes. 300 where left
# out; 0 commits on size and snapshots alone; otherwise 60 to 86400.
volume_rpo_seconds: 300
# How a host's disk is lent. A disk holds what its workspace wrote, not its size: a host is
# charged this percent of each new disk's size until it writes more, and takes workspaces up to
# 80% of its device. 1 to 100; 100 charges every disk its full size.
disk_reserve_percent: 50
# How much of its device a host may be promised, its disks' sizes summed: what it would need if
# every one filled at once. 100 to 400; 100 promises nothing past the device.
disk_commit_max_percent: 200
# Whether the object store's garbage collection, a daily job, only says what nothing reaches
# rather than deleting it. The delete is a marker the bucket expires after its Object Lock.
storage_gc_report_only: false
# Whether a team's admins may declare egress policies of their own, for the team's workspaces:
# named and only allowing - nothing brokered. Any team may then reach past what the policies
# below allow, so it is off unless decided.
team_network_policies: false
# Whether the control plane fetches VS Code servers from Microsoft on its own: the newest stable
# release, looked for daily, and the server of a client's commit the installation has not
# released. Off, it fetches none, and workspaces get the server the image carries or one an
# administrator published.
vscode_fetch: false
# Whether the control plane fetches the agent CLIs - Claude Code and Codex - from their
# publishers on its own: the newest release of each, looked for daily. Off, it fetches none,
# and workspaces get the pair the image carries or one an administrator published.
agents_fetch: false
# The IAM role a team's secret kept in Secrets Manager or Parameter Store is read under, in a
# session tagged spin-team-id with the team; it reads only a secret or a parameter tagged the
# same. Unset, a team's secrets are sealed in the database and none is a reference.
# team_secrets_role: arn:aws:iam::123456789012:role/spin-team-secrets
# What a workspace created without a tag of its own runs under, by policy name - the
# network_policies block below, or a host's own. Unset, it is "offline", the policy every
# host has and which allows nothing: a workspace nobody chose a policy for reaches nothing
# past its own namespace. An agent creating its own workspaces is exactly the caller that
# names no tag, so this is where an installation decides what that one may reach.
#
# Declared empty it is "offline" as well: no spelling of this setting means open. An
# installation that wants such a workspace to reach the internet declares a policy that says
# so - allow: ["0.0.0.0/0"], which the node's own drops still come before - and names it
# here, so what a workspace may reach always has a name somebody chose.
default_network_tags:
- agent
# Moving a workspace off a host that stopped answering, without anybody asking.
#
# A host that *stops* says so and is emptied first. This is the other one: the machine that
# lost power, or its network, and said nothing. Its workspaces stay where they are until
# somebody runs `spin workspace recover --accept-data-loss`, which means noticing.
#
# rehome_after_seconds is how long the silence has to last; unset or zero, nothing is ever
# moved and this whole thing is off, which is what an installation starts as. A move gives
# up whatever the host had not published, so it is not a default.
#
# rehome_max_unpublished_bytes is how much of that this installation gives up on its own.
# The number is what the silent host last reported it had not sent to the object store -
# for a host that is gone, what it never will. Over it, the workspace waits for a person.
# A host that never reported at all also waits: nothing known is not the same as nothing
# to lose.
rehome_after_seconds: 900
rehome_max_unpublished_bytes: 67108864
# Where this installation keeps its logs and traces: a bucket of its own, never the volumes',
# in the same account and region as theirs. The control plane that leads runs the store over
# it; a change of bucket is applied by restarting it. How many days each index keeps what it
# holds is read again every minute; 30, 7 and 7 where a key is left out.
logs_bucket: acme-spin-logs
# Which of the cloud's costs are this installation's, and its budget, as the cloud names them:
# on AWS the value of its spin:installation tag and the budget's ARN, which spin-terraform-aws
# writes. The control plane reads the bill a few times a day for Admin → Costs; without a scope
# it reads none.
bill_scope: acme-spin
bill_budget: arn:aws:budgets::123456789012:budget/acme-spin-monthly
logs_platform_retention_days: 30
logs_workspace_retention_days: 7
traces_retention_days: 7
# Keep what every workspace writes - its machine's console, and what its own services write to
# the guest's journal - searchable by whoever may read the workspace for
# logs_workspace_retention_days. Off unless declared: that output leaves its workspace only
# when the installation decides it does. Each workspace may have logs_workspace_daily_mb of it
# kept a UTC day (100 where this says nothing); past it, the rest of the day is dropped and one
# line says so.
collect_workspace_logs: false
logs_workspace_daily_mb: 100
registration:
# open, whitelist or closed.
mode: whitelist
# Addresses and domains the whitelist admits. An entry with an @ is an address; without
# one, a domain.
emails:
- acme.com
- contractor@example.com
# What a person may choose when creating a workspace. One preset may be the default.
# burst says how a workspace of it grows: fixed (the default) runs at its size; to_preset
# starts at the floor (burst_floor_cpu, burst_floor_memory_mb; 1 and 1024 if left out) and
# grows to its size; to_host starts at the floor and grows to what its host has.
# bandwidth_mbps is what its network carries each way (left out: unshaped); ssh, the editor
# and forwarded ports are not held to it. rpo_seconds is how far behind the object store its
# disk may fall (left out: the installation's volume_rpo_seconds).
presets:
- name: small
description: For an agent doing one task at a time
cpu: 2
memory_mb: 4096
disk_gb: 20
bandwidth_mbps: 100
default: true
- name: large
cpu: 8
memory_mb: 16384
disk_gb: 100
burst: to_preset
burst_floor_cpu: 2
burst_floor_memory_mb: 4096
# Egress policies, by name. A workspace created with --network-tag agent runs under this one.
#
# Carrying a tag is the deny. A workspace with no tag reaches anything beyond the node's own
# networks; a workspace with any tag reaches what its tags allow and nothing else. So every
# policy here is an allowlist.
#
# "offline" is not declared here and cannot be: every host installs it, it allows nothing, and
# it is what default_network_tags means when it is unset.
#
# Tags compose as a union: a workspace created with two of them reaches everything either one
# allows, and the order they are named in does not change that.
#
# A policy is a list of rules, and allows what any of them allows. A rule is destinations on
# ports, each destination on each port: allow is IPv4 prefixes; allowNames are names, each
# exact or with a single leading "*.", enforced through the workspace's own lookups - the host
# opens the address its resolver answered that workspace with, for as long as the answer
# lives. ports are destination ports, "443" being TCP, which is what a bare number means; UDP
# is asked for by name ("53/udp"), because opening UDP to anywhere is the shape of a tunnel; a
# range is "8000-8100" or "8000-8100/udp"; and "icmp" is a ping - an echo request, no other
# ICMP type. A rule with no destination is its ports anywhere, and one with no port is every
# port at its destinations. Two things on two sets of ports are two rules: one rule of both
# would open each port at each destination. A workspace needs no port for DNS: its resolver is
# the host's, which answers under every policy.
#
# A policy is as exact as DNS: an address serving several names is open to all of them once
# one is allowed - unless the rule says verifySNI: true, which passes a TLS connection only when
# its hello names one of the rule's names (TCP only, 443 where it names no port). That holds a
# client that says which host it wants, not one that lies. See docs/network/README.md.
network_policies:
- name: agent
rules:
- allowNames:
- github.com
- "*.githubusercontent.com"
- registry.npmjs.org
allow:
- 140.82.112.0/20
# The web and nothing else: no address at all, so it composes with any other tag.
- name: web
rules:
- ports:
- 80
- 443
# A resolver of the workspace's own on UDP, and ping anywhere: two rules, not their product.
- name: diagnostics
rules:
- allow: [1.1.1.1/32]
ports: ["53/udp"]
- ports: [icmp]

Generated by task reference from spin/configs/spin-example.yaml at 8eee32b9d.

Documents Spinbox v20261019.02. This page as Markdown, for a model or a terminal.