On this page
Configuration
First-run config and deployment profiles
stado config init creates only a schema-versioned local queue profile:
local compute, local primary and backup stores, one deployment identity, and a
loopback dashboard. It contains no Wisent service routes, production clients,
cloud locators, or credentials. Existing legacy files migrate explicitly with
stado config migrate; the exact prior file is preserved beside the migrated
document.
STADO_CONFIG selects the operator-owned deployment profile. Stado ships no
company deployment profile: stado config init creates a neutral local profile,
and an operator extends that file with provider order, explicit provider fences,
storage, object/release/service verifiers, workload grants and service routes.
Those decisions remain outside the public source tree and cloud CLI state.
A selection is used or refused, never replaced: a STADO_CONFIG that names a
file that does not exist stops every command that reads the profile with
STADO_CONFIG names <path>, which does not exist, and one that is not text
with STADO_CONFIG cannot be read: <error>, instead of running on another
profile or on none. Only config init and the credential-store migration
write the selected file before it exists.
config init writes to a nonempty STADO_CONFIG, expanding a leading ~
against HOME. Without that selector it writes ~/.stado/config.json.
It refuses an existing destination with config file already exists: <path>;
it does not overwrite that profile or fall back to the default destination; a
selection that is not text is refused with STADO_CONFIG cannot be read: <error>.
STADO_CONFIG="$PROFILE" stado config init
The initial local registry is seeded under HOME/.stado/local-storage, and an
existing registry is retained. Selecting a different profile filename alone
does not isolate the local stores. A lifecycle test must also select its own
home directory inside the checkout's ignored build directory:
HOME="$TEST_HOME" STADO_CONFIG="$TEST_PROFILE" stado config init
storage.backend selects the write authority and first read target: a
self-hosted store (stado, alias self-hosted, or minio, garage,
seaweedfs, ceph), a hosted one (gcs, azure, s3, cloudflare,
backblaze, wasabi and the other providers
storage providers lists), any other
S3 API endpoint (s3-compatible), or local. Every S3 API store reads its
bucket, region, account, endpoint and credential role from
storage.s3_compatible, each only where its provider asks for it.
storage.backup configures a disaster-recovery mirror: a primary mutation
commits first and is mirrored afterwards. Ordinary storage clients may retry a
failed primary read against that mirror; authority-sensitive readers, including
the object API server, use primary-only mode instead. A successful primary
answer that an object is absent never falls through to the backup. A null or
empty storage.backup.backend disables the Stado-managed mirror. Changing the
write authority still requires an explicit fenced migration or the fixed-root
reconciliation described in disaster recovery;
configuration and read fallback never promote the backup.
Only route-local or process-local values should be overridden:
| Var | Purpose |
|---|---|
STADO_CONFIG |
Readable deployment profile path. |
STADO_API_URL |
Operator-selected canonical HTTPS Stado origin, including /api/release/object; overrides api.url. Plain HTTP is accepted only on loopback. |
STADO_RELEASE_ORIGIN |
Object-service origin used by the stado-landing gateway. Public callers still use STADO_API_URL; the gateway's upstream is selected independently. |
STADO_API_TOKEN |
Dedicated caller token for its mapped object namespace. |
STADO_MACHINE_API_TOKEN |
Machine submit/status/cancel token. |
STADO_SERVICE_API_TOKEN |
Caller-specific deployer token; accepted only for mapped service names/actions. |
STADO_REGISTRY_API_URL |
Desktop's local source and the endpoint assigned to its registry API credential. |
STADO_REGISTRY_API_TOKEN_FILE |
Desktop's owner-only raw registry client bearer file; defaults to ~/.stado/registry-api-desktop-token. |
WC_REGISTRY_API_CLIENTS |
Exact registry API client mapping; overrides registry_api.clients. |
WC_SKARBIEC_URL |
Stado's Skarbiec service origin for credential CLI, coordinator and API field reads. |
WC_SKARBIEC_CONSUMER |
Stado's Skarbiec consumer: stado. |
WC_SKARBIEC_TOKEN_FILE |
Owner-only Stado bearer file, normally ~/.stado/stado-skarbiec-token. |
STADO_RELEASE_VERSION |
Required exact immutable Stado runtime version. |
STADO_RELEASE_PLATFORM |
Required exact Stado runtime platform for dispatched agents. |
STADO_AGENT_POLL_SECONDS |
Required whole seconds a dispatched cloud worker (stado serve --standalone --worker --idle-shutdown) waits between queue polls that started nothing (config key agent.poll_seconds); dispatch refuses to create a machine without it. |
STADO_SSH_KEEPALIVE_SECONDS, STADO_SSH_KEEPALIVE_COUNT_MAX |
How often an idle native SSH session asks the server whether it is still there, and how many unanswered asks end it (config keys ssh.keepalive_seconds, a whole number above zero, and ssh.keepalive_count_max). A command session runs without keepalive when neither is declared, as OpenSSH does by default. A reverse forward is long-lived and only keepalive tells it its peer is gone, so it is refused until both are declared: SSH keepalive is not declared: a reverse forward needs both ssh.keepalive_seconds (env STADO_SSH_KEEPALIVE_SECONDS) and ssh.keepalive_count_max (env STADO_SSH_KEEPALIVE_COUNT_MAX); declare them with stado config set. Declaring one without the other is refused the same way. |
WC_EMAIL_TO, WC_ALERT_SKRZYNKA_MAILBOX, WC_ALERT_SKRZYNKA_ORGANIZATION |
The e-mail alert destination and the Skrzynka mailbox and organization the skrzynka channel sends from (config keys alerts.email_to, alerts.skrzynka_mailbox, alerts.skrzynka_organization); see How the operator is asked. Which channels page is the registry's operator_contact, set with stado alerts preferences set. |
WC_BUCKET |
The queue bucket. The stado serve --watchdog role uploads its diagnostics there unless --watchdog-bucket names another; with neither, the role is refused with the watchdog has no bucket: pass --watchdog-bucket or declare WC_BUCKET. |
STADO_HOST_HEALTH_API_URL |
Authenticated Stado host-health origin. |
STADO_HOST_HEALTH_SKARBIEC_URL |
Skarbiec origin for the route-only host-health publisher. |
STADO_HOST_HEALTH_SKARBIEC_CONSUMER |
Exactly stado-host-health-beacon. |
STADO_HOST_HEALTH_SKARBIEC_TOKEN_FILE |
Owner-only grant scoped only to the item playing role host-health-api. |
STADO_CREDENTIALS_STORE |
Requested credential backend (skarbiec, skarbiec://<https-origin>, or file://<absolute-path>). A value different from credentials.store is a pending migration. |
What each bearer opens, and how every token is verified against its own Skarbiec item: security.
The public origin is independent of host-control routes and network providers. Changing the selected HTTPS endpoint does not require changing a release client. A gateway's upstream is selected by its deployment configuration, not by detecting a tunnel program.
Web products select their edge separately. A product with edge: "stado"
requires web_api.edge with target, address and contact; stado web edge declare records that selection. Without a valid declaration, stado web status
returns edge-unconfigured, the exact edge_error, and a non-zero exit code.
It does not treat an unknown expected address as a healthy public connection.
Cloud-provider locators and credentials are not caller overrides. An enabled
provider adapter receives its exact profile and provider-plugin identity; a
workload-agent grant contains only the provider-neutral application roles in
agent.skarbiec.roles. It must never contain cloud-gcp, cloud-azure, or
cloud-aws, and no bootstrap, health, recovery, or release path invokes
gcloud, gsutil, or az.
Product data enters through stado://<namespace>/<key> and the authenticated
Stado object boundary. Immutable artifact manifests may additionally reference
provider-native az://, gs://, and s3:// locations, plus hf:// and
HTTPS; access still resolves through authenticated provider adapters, and
embedded credentials or sensitive query parameters are rejected.
How the gateway resolves a namespace and authorizes each key and action: object store.
Optional alerts are disabled when alerts.channels is absent or empty. Enabling
a channel authorizes only its own credential lookup and network route. The
Pub/Sub topic and SendGrid recipient are inert unless their adapters are also
enabled. An alert failure is isolated from scheduling, execution, health, and
the other channels.
Mobile egress request boundary
The loopback proxy requires the deployment's header budget. It includes the request line, header fields and terminating blank line, not the request body or the bytes carried by a CONNECT tunnel.
stado egress mobile serve --interface "$INTERFACE" --port "$PORT" \
--max-header-bytes "$HEADER_BYTES"
HEADER_BYTES must be a positive whole number chosen by the deployment.
A missing, zero or invalid value is refused by the CLI before a listener
starts. Managed service declarations must include both arguments
--max-header-bytes and their value in the service's argument list.
There is no built-in byte budget.
A header ending exactly at the declared boundary is accepted. A larger one
closes the client connection before opening an upstream connection; the service
log names --max-header-bytes, the declared limit and the observed byte count.
EOF before the terminating blank line is reported as an incomplete request.
Bodies and CONNECT payload bytes that arrive with the header are forwarded
without truncation. Readiness reports the actual bound socket address.
The real qualification journey is
node tests/egress/headers.mjs in the Stado checkout, with STADO_BIN naming
the candidate executable, STADO_TEST_SOURCE_REVISION its exact committed
source revision and STADO_EGRESS_TEST_INTERFACE a real interface with a
non-loopback IPv4 address on the qualification host. It launches an isolated
proxy and TCP receiver, checks a boundary-sized header with a body, oversized
and incomplete refusals, and CONNECT payload preservation. It keeps binary and
test hashes, commands, exit statuses, received bytes and the verdict under
.build/egress-headers-*. Missing prerequisites do not pass.
API request boundaries
How an API listener bounds every request head, body, operator console request and fleet join report before reading it, and where those bounds are declared: API request boundaries.
The scan load ceiling
host.scan_load_ceiling (WC_HOST_SCAN_LOAD_CEILING for one process) is the
one-minute load average above which the host process skips its scans of the
host's own disk — a preview of the janitor's build-cache walk, the beacon's
launchd event reads, the inventory walk of a space report on its own host —
and publishes the skip in the beacon's scans block
(Beacon). A janitor pass on a volume at the rule's
full threshold walks the build caches whatever the load, at background
priority, because that walk is what frees the disk; its reading is recorded
as an admission:
stado config set host.scan_load_ceiling "$LOAD_CEILING"
stado config get host.scan_load_ceiling
The value is a load average: a number, not negative, not infinite; anything
else refuses the scan with host.scan_load_ceiling = "<value>" is not a load average and the beacon carries the refusal. A host that declares nothing is
bounded by its logical core count, measured by the process that scans, and
the beacon names that source as measured_cores.
Machine source archive boundaries
machine.source_limits declares archive_bytes, extracted_bytes, members
and trailing_bytes for machine submissions that carry a source archive.
WC_MACHINE_SOURCE_LIMITS supplies the same document for one process. There is
no built-in size or member budget; stado config validate checks a stored
declaration, and source admission checks the effective declaration before
reserving a request. CLI and Desktop configuration use the same key. See
Source archive bounds for the
command, refusal semantics and real qualification journey.
Credential store
Where Stado keeps and reads its credentials: Credential store.
Registry API access
Which clients may call the registry API, with which bearer and actions: Registry access and the registry document.
Registry
The registry document and how Stado reads it: Registry access and the registry document.
Logical services and local resolvers
How a logical service is declared and reached through a host's local resolver: Logical services and local resolvers.
Signed product release control
The optional top-level release_control object owns product release trust and
desired state. trusted_keys contains Ed25519 public keys only. Each product
binds one logical service, exact archive paths, schema versions, a blue-green
policy, and registered host policy. desired and previous are written only by
stado release promote|rollback; their platform references point beneath the
same immutable stado://releases/<product>/<version>/<platform>/ coordinate.
Each target uses two private candidate ports and one loopback stable bind. Its state, runtime, log, and install roots are absolute. A legacy launchd label and plist are optional one-time migration inputs: the agent starts and proves the candidate before disabling the legacy service, then owns the stable port.
Registry validation rejects unknown products/services/targets, non-loopback stable binds, reused ports, unsafe paths, untrusted desired keys, incomplete platform sets, non-monotonic zero generations, and rollback windows shorter than drain.
A block that declares trusted_keys and an empty products map is accepted.
It states what the document trusts and nothing about rollout, which is exactly
what stado scratch create emits into a lease's registry: the fleet's
schema_version, generation and trusted_keys, no products. A block that
declares neither is the only one refused, as
registry.release_control must declare trusted_keys or products.
Local inference
How a host's local inference is declared and served: Local inference.
Quotas
The provider-neutral config/quotas.json object contains reservation overlays,
not invented capacity. A reservation subtracts operator-owned capacity from a
live provider limit before dispatch. Missing or unreachable live quota data is
reported as unavailable; Stado does not reinterpret it as zero usage or
unlimited capacity.
GCP quota reads are part of the preview compute adapter and use the Rust provider boundary. Azure supports configured reservations with incomplete live VM-family coverage. Live AWS quota management is planned and unavailable.
Optional GCP adapter prerequisites
The GCP compute adapter is preview. An operator must explicitly enable it and supply an already provisioned project, canonical store, scoped managed identity or Skarbiec service account, quota overlay, network, image, ownership labels, cost policy, and immutable Stado release. Runtime compute and storage operations stay inside the Rust provider adapters; install, bootstrap, release, health, and recovery paths do not invoke a cloud CLI.
The image and the geography are required bindings: GCP_IMAGE and
GCP_IMAGE_PROJECT (config keys image and image_project), GCP_ZONES
(zones, the zones a machine is created in, in preference order) and
GCP_REGIONS (regions, whose quota is read). None is built in: a machine
create without them is refused with GCP compute bindings are not declared: … naming the missing ones, and a quota read or request without regions says
so. A zone that does not carry the machine type answers GCE's own error and
the next declared zone is tried. The Azure provider likewise needs its resource
group, locations, vnet, subnet, NSG, image URN and VM user declared, and the
AWS provider its region, security group, IAM profile and AMI; each refusal
names the bindings that are missing.
Zone candidates and machine compatibility belong to the selected deployment profile. Resource exhaustion may advance to the next allowed candidate; an authorization, ownership, invalid configuration, or ambiguous provider error must not. GCP is not stable until a release-scoped live test creates an owned VM, boots the pinned agent, runs and collects a workload, exercises cancellation and recovery, and reaps all paid resources.
The active deployment workflow publishes immutable native releases and installs the Rust coordinator. It has no Cloud Function scheduler, mutable package upgrade, provider CLI authentication, or ambient credential fallback.
Workload runtime ownership
Stado does not pin Python, CUDA framework, Hugging Face, NumPy, or application package versions as part of the control-plane contract. A workload declares and owns its runtime, image, command, source revision, artifacts, and verification hook. Provider bootstrap templates may install prerequisites required by a specific workload profile, but those pins are deployment data and do not become dependencies of the Rust agent or local onboarding path.
Source: this website