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.

storage.backend selects the write authority and first read target. 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_REGISTRY_SKARBIEC_URL Server-side Skarbiec origin for the registry API verifier.
WC_REGISTRY_SKARBIEC_CONSUMER Exactly stado-registry-api-verifier.
WC_REGISTRY_SKARBIEC_TOKEN_FILE Dedicated server verifier grant; defaults to ~/.stado/stado-registry-api-verifier-skarbiec-token.
STADO_RELEASE_VERSION Required exact immutable Stado runtime version.
STADO_RELEASE_PLATFORM Required exact Stado runtime platform for dispatched agents.
STADO_ALERT_CHANNELS Explicit comma-separated optional adapters: slack, telegram, sendgrid, gcp-pubsub.
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 stado-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.
STADO_CREDENTIALS_ADMIN_URL Canonical Skarbiec origin used only for store administration and migration; defaults to the shared Skarbiec URL.
STADO_CREDENTIALS_ADMIN_CONSUMER Skarbiec bootstrap consumer used only for store administration and migration.
STADO_CREDENTIALS_ADMIN_TOKEN_FILE Owner-only bootstrap grant for the credential-store administrator.

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 items in agent.skarbiec.items. It must never contain stado-gcp, stado-azure, or stado-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.

Credential store

One selector owns every application credential, including cloud/provider credentials, service tokens, and SSH host keys:

{
  "credentials": {
    "store": "skarbiec",
    "admin": {
      "url": "http://127.0.0.1:8787",
      "consumer": "local-operator",
      "token_file": "~/.stado/local-operator-skarbiec-token"
    }
  }
}

credentials.store is the committed source of truth. STADO_CREDENTIALS_STORE is its process-level override. Supported locators are skarbiec, skarbiec://<https-origin>, and file://<absolute-path>. The file backend is an owner-only local/offline manager; Skarbiec adds encryption, scoped grants, audit, recovery recipients, and remote HTTPS access.

Changing the environment selector does not make Stado read an empty backend. It creates a fail-closed pending migration:

export STADO_CREDENTIALS_STORE=file:///secure/stado-credentials.json
stado secrets migrate

Without an environment override, stado secrets migrate --to <locator> performs the same change. Migration snapshots every active item and its type, requires an empty destination, copies and reads every value back, commits credentials.store, and only then removes the source items. A failed copy, verification, config write, or source cleanup rolls back to the previous store. Normal reads and writes remain blocked while the environment and committed selectors differ.

All stado secrets CRUD, provider reads, scoped verifier reads, and stado fleet key operations use this selector. There is no OpenSSH-file fallback for host channels. Only a backend's own bootstrap credential remains outside the selected store: putting the grant needed to unlock a manager inside that same manager would be circular. For Skarbiec, this is the owner-only admin token file named above.

Registry API access

Desktop's registry, inventory, cleanup, convergence and storage-reconciliation requests use one source-bound client bearer, not its Wisent account session. Configure the server's deployment profile with an exact client and only the actions that client should perform:

{
  "registry_api": {
    "clients": {
      "stado-desktop": {
        "item": "stado-desktop-registry-api",
        "actions": [
          "policy-read",
          "converge-read",
          "converge-apply",
          "storage-reconcile-read",
          "storage-reconcile-apply"
        ]
      }
    },
    "skarbiec": {
      "url": "http://127.0.0.1:8787",
      "consumer": "stado-registry-api-verifier",
      "token_file": "~/.stado/stado-registry-api-verifier-skarbiec-token"
    }
  }
}

The Skarbiec item must be named <client>-registry-api; its token field holds the client's raw bearer. The server's separate verifier grant reads that field. Do not put the verifier grant or an account session token in Desktop's client file. registry_api.skarbiec.url defaults to the deployment's Skarbiec origin, and the verifier grant must have its own file.

policy-read permits the projected registry and host inventory; policy-write permits policy updates. cleanup-read and cleanup-run permit the janitor's report and action. converge-read permits the Services report; converge-apply permits delivery. storage-reconcile-read permits only the storage transaction's Status request; storage-reconcile-apply independently permits Run, Resume, Rollback and Finalize. An apply-only client does not gain read permission, and a read-only client cannot deliver a release or change a storage transaction.

The storage action names and their dashboard route must be deployed together. Older Stado servers reject these names as unknown configuration instead of ignoring them. Upgrade the server before adding the two actions; before rolling back to an older server, remove them or restore that server's prior client action list. Leaving the new grants in an older profile is not a compatibility shim.

In Desktop Settings → Registry API access, save the exact source endpoint and an absolute token-file path. Desktop rereads the file on each request. The two STADO_REGISTRY_API_* environment variables override the saved fields; when either is present the settings are read-only. The default client path is ~/.stado/registry-api-desktop-token. A missing, empty or unreadable client file is reported locally; a credential assigned to another endpoint is not sent.

An absent client mapping or a missing, wrong or action-ineligible bearer returns HTTP 401. Malformed mapping or unreadable verifier state returns 503, not permission to use another credential. A completed convergence or storage reconciliation product result returns HTTP 200 with exit_code and the complete report, even when that exit code is nonzero; storage reconciliation also retains refusal. See Services and storage root authority handoff.

Registry

stado-rs/data/registry.json is an intentionally empty bundled seed. A release binary therefore knows no operator host, service, route, release key or workload until the operator creates a registry with stado config init or publishes one through the selected store. stado registry push and stado registry pull resolve that store from STADO_CONFIG, preserve generation fencing, and surface an unreachable store as failure rather than silently switching providers.

Each target entry:

{
  "name": "my-workstation",
  "kind": "local",
  "ssh": "user@host-or-ip",       // used by `wc bootstrap` to install the agent
  "gpu_type": "nvidia-tesla-t4",  // SKU label the agent broadcasts
  "vram_gb": 96,                  // total GPU VRAM
  "gpu_power_limit_watts": 300,   // persistent board power cap, reconciled by the agent
  "env_overrides": { "WISENT_DTYPE": "auto" },
  "agent_args": ["--gpu-type", "nvidia-tesla-t4"]
}

A target declares capabilities and policy, not a concurrency limit. The worker measures live CPU, RAM, disk, running jobs, and per-device VRAM every poll and admits as many compatible jobs as those resources allow. Legacy slots, max_concurrent, and WC_LOCAL_SLOTS values are discarded when a registry is read and are never written back.

gpu_power_limit_watts is valid only on local targets and must be a positive integer. Set it with stado host gpu-power-limit TARGET WATTS: Stado writes the generation-fenced declaration, applies it immediately, and the local agent reconciles it at startup and every five minutes. A declared cap that the NVIDIA driver cannot apply closes admission on that host instead of silently running new jobs outside the policy.

A coordinator entry pins the scheduling-tick driver:

{
  "name": "local-control-plane",
  "runtime": "daemon",
  "host": "https://stado.wisent.com",
  "interval_seconds": 180,
  "state_uri": "stado://system/registry",
  "active": true
}

A registry document may also carry the fleet's central enrollment and communication catalog. enrollment declares which registration paths are allowed — allow_join for machine-initiated stado fleet join/approve, allow_enroll for control-plane stado fleet enroll, and require_verified_hostname for the verified-identity contract. channels declares how machines reach the control plane. Both sections are additive: a document without them is unrestricted, which stado fleet catalog reports explicitly. The stado fleet commands enforce the catalog in their preflights, before any write.

{
  "enrollment": {
    "allow_join": true,
    "allow_enroll": true,
    "require_verified_hostname": true
  },
  "channels": {
    "control_plane": ["loopback"],
    "notes": "any address that resolves: LAN, mDNS, tailnet"
  }
}

Logical services and local resolvers

The optional top-level service_directory is the fleet routing contract. authority names the target and absolute Stado binary that serve canonical versioned snapshots and commit placement changes; other hosts use their local registry only to bootstrap that SSH path. generation is monotonic. Each logical service declares its active host, host-relative loopback origin on every eligible host, optional placement profile, and the exact consumers and capabilities allowed to resolve it.

Per-target service_resolver policy declares the loopback resolution API and stable compatibility adapters for workloads on that host:

{
  "service_directory": {
    "authority": {
      "target": "control-host",
      "command": "/opt/stado/bin/stado"
    },
    "generation": 7,
    "services": {
      "model-router": {
        "placement_profile": "router-with-credentials",
        "active_host": "control-host",
        "endpoints": {
          "control-host": {"url": "http://127.0.0.1:8080"},
          "worker-host": {"url": "http://127.0.0.1:8080"}
        },
        "consumers": {
          "application": {"capabilities": ["model-routing"]}
        }
      }
    }
  },
  "targets": [{
    "name": "worker-host",
    "kind": "local",
    "service_resolver": {
      "api_bind": "127.0.0.1:17600",
      "refresh_seconds": 5,
      "max_stale_seconds": 15,
      "adapters": [{
        "service": "model-router",
        "bind": "127.0.0.1:17601",
        "consumer": "application"
      }]
    }
  }]
}

All resolver and adapter binds must be loopback. The authority target must have registry SSH transport, and the authority command must be an absolute, component-normal path. Remote host-relative endpoints require targets[].ssh; they are never rewritten into client configuration. The resolver refuses an unknown consumer, an active placement transaction, a rolled-back directory generation, or a cache older than the configured limit.

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

The optional top-level inference section is the single desired-state and routing catalog for Stado-managed vLLM. gateway_target is the registered host running Brama; deployments run on registered local GPU targets and expose their OpenAI-compatible endpoint only on a Tailscale IPv4 address.

{
  "inference": {
    "gateway_target": "my-brama-host",
    "deployments": [],
    "routes": {}
  }
}

The lifecycle is deliberately two-step and generation-fenced:

Create the shared bearer without exposing it in argv, output, or a file:

stado inference init-credential

Bootstrap the gateway snapshot before Brama's first managed start:

stado inference route set example-client/chat/primary \
  --to openai/gpt-4.1-mini \
  --expected absent --gateway gateway-host
stado inference plan chat-primary \
  --host gpu-host \
  --image 'vllm/vllm-openai@sha256:<image-digest>' \
  --cache-dir /srv/stado/inference/chat-primary \
  --model 'example/model' \
  --gpu-mode yieldable \
  --max-model-len 16384 \
  --revision '<model-revision>'
stado inference apply <plan-id>
stado inference doctor chat-primary
stado inference verify chat-primary
stado inference route set example-client/chat/primary \
  --to chat-primary \
  --fallback openai/gpt-4.1-mini \
  --expected openai/gpt-4.1-mini \
  --gateway gateway-host

The immutable model revision and container image digest prevent silent model or runtime replacement.

kv_cache_memory_gb sets a fixed vLLM KV-cache allocation in GiB. Omitting it preserves the pinned image's own memory policy. A smaller cache releases VRAM without changing model weights or output quality, but lowers the number of tokens and long requests that can remain active concurrently.

gpu_mode defaults to exclusive, which keeps the GPU reserved for inference. yieldable makes the local Stado agent the lifecycle owner: it pauses the inference container when an eligible GPU job is queued, advertises the released capacity, and resumes inference only after queued and active GPU work has cleared. Eligibility includes provider, accelerator, host pin, centralized assignment, and capacity constraints; there is no timeout-based eviction. The ordered provider fallback remains available while the local container is yielded. route set therefore accepts a non-ready yieldable deployment only as the primary of a route with at least one ordered fallback. It still requires an exclusive primary and every local fallback to be ready, so an unavailable deployment cannot be published as the route's only destination.

If plan or apply reports an unmanaged GPU workload, inspect it through the same target-scoped host channel instead of opening an ad hoc SSH session:

stado inference blockers --host gpu-host
stado inference release --host gpu-host \
  --identity <PID:START_TICKS>

blockers reports the executable, owner, VRAM use, cgroup, and an identity made from both PID and /proc start ticks. release refuses a stale identity, sends TERM, and waits for exit. Add --force only to escalate that same verified process to KILL; it never accepts a bare PID.

A cancelled or failed pre-commit plan can leave a runtime or root-owned model cache without a registry deployment. Clean that exact saved plan through the managed channel:

stado inference plan-logs <plan-id>
stado inference abort <plan-id> --purge-cache

plan-logs reads the not-yet-committed container logs through the same managed host channel.

abort never changes the registry. It stops only the runtime described by the immutable local plan, removes its cache through the pinned container runtime, and consumes the plan after successful cleanup. Set --cache-dir during plan when the target's home filesystem is not the intended model volume.

plan inventories the host, requires Docker, NVIDIA tooling, and a live Tailscale address, then saves an immutable plan bound to the current registry digest. apply rechecks that precondition, installs the digest-pinned vLLM container under Docker's unless-stopped supervisor, waits for an authenticated readiness probe, and only then commits the deployment. A failed runtime, readiness check, or registry compare-and-swap restores the prior runtime.

The deployment and Brama use one centrally stored credential item: provider:local-openai, containing a non-empty token field. stado inference init-credential generates and stores it without printing the token, and refuses to overwrite an existing item. Deliberate rotation can use stado credentials put provider:local-openai with JSON on standard input, but must be coordinated with runtime replacement; never place the token in argv or registry data. Route changes require --expected, probe the destination first, stage an owner-only route snapshot on the gateway, compare-and-swap the registry, and then atomically commit the snapshot. Brama reloads that file per request, so cutover needs no backend restart. Ordered --fallback destinations are attempted when the primary provider fails; An external provider fallback therefore preserves the same model contract while local inference is unavailable. rollback reinstalls the recorded prior deployment; retire refuses while any primary or fallback route still selects the deployment and retains model cache unless --purge-cache is explicit.

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.

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