On this page

Grant

A Skarbiec grant authorizes one consumer to perform named actions on exact items and, for field reads, exact fields. The grant is not the credential item. Stado holds no ambient cloud login; it asks Skarbiec for the field an operation needs.

What it is

A grant records a consumer, capabilities, audience, expiry and a bearer hash. The opaque bearer lives in an owner-only regular file on the consuming host. An empty file, symlink, or file readable by group or others is refused before a field read. Skarbiec checks the bearer and each action, item and field. A valid bearer without the requested capability still gets a refusal.

Stado's coordinator, APIs, credential CLI, signing and publication use consumer stado and the secrets.skarbiec endpoint and bearer file. They share an identity, not unlimited access: the grant lists each item and field separately. stado credentials grant show --host TARGET stado --token-file ABSOLUTE_PATH --json reports both the recorded capabilities and whether the host file matches. A service request through its declared route proves whether the consumer works, beyond the grant check.

The agent on a rented machine uses a separate scoped *-agent grant; the coordinator bearer is never delivered there. The host-health beacon likewise has its scoped publisher grant stado-host-health-beacon, which reads only the token of the item playing role host-health-api. Both the beacon and the dashboard that verifies it select that item by its stado:role:host-health-api tag, never by id; no item, or two items, in the role refuses publication with the stado credentials item retag command that tags one. These are host-side publishers, not alternative identities for Stado's coordinator and API readers. Service-specific consumers declared in service_directory are different products' grants, not Stado's own reader identities.

Who declares it

Stado configuration stores coordinates, never bearer values: secrets.skarbiec.url, .consumer (stado) and .token_file. Remote agent roles are declared in agent.skarbiec.roles; the agent grant must not contain provider control-plane roles. The beacon publisher names its own STADO_HOST_HEALTH_SKARBIEC_URL, _CONSUMER and _TOKEN_FILE in its managed unit. The product's stado credentials token mint writes a new owner-only bearer file on a selected host, with capabilities and audience declared to Skarbiec.

The old credentials.admin and API-specific Skarbiec identity sections are retired. stado config validate names each stale section rather than selecting a different identity. stado credentials grant consolidate --host TARGET --from OLD_CONSUMER --token-file ABSOLUTE_STADO_FILE verifies the current Stado bearer, merges the source capabilities with Stado's current grant, and reads the result. Repeat --from for more old consumers. It refuses a missing grant, invalid bearer, and acquire or lifecycle capabilities that Skarbiec forbids combining with direct reads. Source grants remain until their readers have cut over. stado config migrate-identities removes retired local settings atomically, preserves the prior document and refuses a missing bearer or invalid result. See Skarbiec and Stado for the migration order.

Who keeps it alive

The workload agent's own grant is issued until revoked. On every tick the agent looks at it until it sees that lifetime, and re-issues a grant the vault still records with an end (one issued before until-revoked grants) at the authoritative vault; a replica writes through its bond owner, then pulls the re-issued vault. stado credentials grant renew [--force] [--json] runs the same re-issue; --force re-issues a grant that already lives until revoked, which is how an operator's skarbiec grant revoke is undone. This concerns the agent grant, not the coordinator's Stado grant. A host without a vault authority cannot mint its own grant and reports that missing authority.

Every consumer grant the service directory declares (service_directory.services.<service>.consumers.<consumer>.grants, with the bearer file its product reads on the service's active host) is kept alive and bound to that bearer by the agent on the host that holds the owner vault. On every tick it lists the vault's grants. For a declared direct grant whose service is placed on this host, it asks the vault whether the declared file opens the grant the way a read would (skarbiec grant verify on the grant's first capability); when the grant has an end, lapsed or not, or the file does not open it, it re-issues the grant until revoked against that file with the capabilities and audience it holds, so the vault reads the file and no bearer is rotated, then asks again. A declared file missing on its own host is a failure (… its declared bearer file <path> is not a file on <host>, the service's host, so its product has no bearer to present). For a declared grant whose service is placed on another host, the path is never read here, since it could be another file; a grant with an end is kept until revoked through skarbiec grant keep, same capabilities and bearer hash, and the log says the bearer is checked where it lives (stado credentials token sync --check). A direct grant with an end that no declaration owns keeps its end and is named once (declared grants: <consumer> has an end and no service directory declaration owns it, so it keeps its end); an acquire grant keeps the lifetime it lends. A host without the owner vault, or a replica, does nothing, and a host the registry names no target for cannot tell which bearers live on it and says so. Each change is logged once: declared grants: kept <consumer> until revoked, with its capabilities and bearer, declared grants: re-bound <consumer>: <file>: …, and a failure as declared grants: renewing or binding the grant failed for <consumer>: <cause>, where the cause is the vault's refusal, the missing file, or it was re-issued against <file>, and that bearer still does not open it. stado credentials grant renew runs the same pass after the agent grant and exits 1 on such a failure or when the registry or the vault's grants cannot be read.

Vault synchronization copies grants, including revocations, but not the raw bearer files. stado credentials token sync stado --from-host SOURCE --host TARGET --source-token-file PATH --token-file PATH --check --json checks custody without changing a grant; removing --check delivers an existing bearer when both hosts' owner and complete grant match. A copied grant with an old local bearer is not a working consumer. Verify a real read through the selected route after delivery.

Who observes it

stado credentials grant show lists exact capabilities, expiry and bearer match. stado credentials vault list --host TARGET --json reports the declared vault authority. stado host inventory TARGET reports owner-only vault-file metadata without decrypting values. A refused beacon publication leaves the prior beacon untouched and names its failed read.

A worker's own grant decides which queued jobs it can take. A job that names a secret the host's agent grant cannot resolve is declined and stays queued for a host whose grant can; the worker does not fail it. Each claim scan publishes its declines in the host's capacity diagnostics as claim_declined, one {job_id, reason} per job, and they are not counted in eligible_count. The same list carries a release build the worker leaves queued because a build of the same product and platform is already running there and holds the Cargo build directory it would compile into; a host claims one build per build cache at a time. stado host gates TARGET prints one declined: <job>: <reason> line per waiting job the host turned down, and --json carries the same sentence as waiting_jobs[].declined:

waiting:  1 pinned job(s) this host is not taking: <job-id> (7d in queue)
declined: <job-id>: secret <ENV>: no item visible here carries stado:role:<role>; store the secret for role <role> with `stado credentials item put --host <vault owner> --role <role>`

claiming: yes above such a line is still true: the host takes every job it can resolve. The job waits because of the grant, and stado fleet doctor names the grant's drift from the host's declared agent.skarbiec.secret_fields under agent-grant-drift. A declined value of null means the host published no decline for that job, not that it has no reason.

Where it lives

Stado's bearer is an owner-only file on each consuming host, normally ~/.stado/stado-skarbiec-token. The scoped host-health publisher uses its own file. Neither the bearer value nor decrypted item values belong in a registry, release manifest, unit argument, log or repository. The path and the permitted item coordinates may be declared.

Commands

stado credentials grant show --host TARGET stado --token-file /absolute/stado-bearer --json
stado credentials grant consolidate --host TARGET --from OLD_CONSUMER --token-file /absolute/stado-bearer --json
stado config migrate-identities
stado config validate
stado credentials grant add --host TARGET stado --role ROLE --field FIELD --token-file '~/.stado/stado-skarbiec-token' --json
stado credentials grant rebind --host TARGET --token-file /absolute/stado-bearer --json
stado credentials grant revoke --host TARGET OLD_CONSUMER --json
stado credentials grant renew --json
stado credentials token sync stado --from-host SOURCE --host TARGET --source-token-file '~/.stado/stado-skarbiec-token' --token-file '~/.stado/stado-skarbiec-token' --check --json

token mint creates an owner-only file when absent and reuses its bearer when present. Changing an existing grant's capabilities requires --replace-capabilities. Its receipt contains path and metadata, not the bearer. A symlink, empty file, invalid basename or incompatible output mode is refused. In Stado Desktop, Hosts → Credentials → Available operations… exposes grant add, inspection, consolidation, rebind, revocation, renewal and bearer delivery with the same review and refusals.

Not to be confused with

  • An item: the encrypted value is an item; the grant is permission to act on it. Rotating one does not rotate the other.
  • A host-channel key: fleet-held Ed25519 keys authorize host transport; a Skarbiec bearer authorizes an item action.
  • A product API bearer: the item playing a product's <product>-release-publisher role holds the API bearer in its token field. Stado's Skarbiec bearer reads that field and is not itself the API bearer.

Source: this website