On this page
Self-host your storage
Any machine of the fleet with a disk can be the fleet's storage provider,
with no cloud account: its Stado keeps the objects on that disk, every other
host reaches them through the object API, and any S3 client — the AWS CLI,
rclone, an SDK, a backup tool — reaches them through the S3 API Stado serves
at /s3. Buckets and their keys are created with stado storage bucket.
1. Keep the store on the machine's disk
On the storage machine, point the store at the disk and run the object API:
stado config set storage.backend local
stado config set storage.local.path /Volumes/Data/stado-store
stado serve --api
On a fleet host the object API is part of its declared Stado service, which
serves the configured store; stado service status reads it. The store
answers wherever the object API's listener does: on loopback, behind the
host's TLS reverse proxy (which must pass Host through unchanged, because
S3 signatures cover it), and through the fleet's service directory as
stado-object-api.
2. Create buckets
A bucket is a namespace of that host's object_api.namespaces, with a grant
per prefix, and its key lives in the vault item the namespace is bound to.
One command declares both:
stado storage bucket create media --host storage-host \
--grant uploads/=get,stat,list,put,delete \
--grant public/=get,stat,list \
--reload-service com.wisent.stado
Actions are get, stat, list, put and delete; a prefix is a
top-level key or a path ending in /. The command mints a random key in the
vault item <bucket>-object-api (field token), grants Stado's verifier a
read of it, writes the declaration on the host, reads the host's
configuration back to confirm its Stado resolves the bucket, and restarts the
named unit so the object API serves it. If the declaration is refused, the
key it minted is removed again.
| Verb | Effect |
|---|---|
stado storage bucket create NAME --host H --grant P=A,… [--reload-service U] |
Declare the bucket and mint its key. Refused when the host already serves it. |
stado storage bucket list --host H |
Every bucket the host serves, with its grant. |
stado storage bucket show NAME --host H |
One bucket: its grant, key item and S3 coordinates. |
stado storage bucket update NAME --host H --grant P=A,… [--reload-service U] |
Replace the grant; the key is kept. |
stado storage bucket rotate NAME |
Replace the key; the old one is refused from the next request. |
stado storage bucket delete NAME --host H --confirm NAME [--reload-service U] |
Remove the declaration and move the key item to the vault's trash. The objects under ecosystem/NAME/ stay on the disk. |
Every verb takes --json. No answer ever carries the key: it names the vault
item and field the key is read from. The refusals name the host and what it
serves: <host> already serves bucket <name>, no bucket <name>: <host> serves: …, --confirm must repeat the bucket name "<name>"; nothing was removed, --grant "<value>" is not PREFIX=ACTION,ACTION, and
<host> declares no readable buckets: object_api.namespaces is refused: …
when the host's declaration itself does not parse.
Stado Desktop's Space panel runs the same verbs for the selected host.
3. Use it
From another host of the fleet, as the fleet's store:
stado config set storage.backend self-hosted
stado config set storage.stado.url stado://service/stado-object-api?consumer=example-host
stado config set storage.stado.namespace media
stado config set storage.stado.token_file ~/.stado/media-token
From any S3 client, with the coordinates stado storage s3-endpoint media
prints after one signed check (Stado as an S3 provider):
export AWS_ACCESS_KEY_ID=media
# AWS_SECRET_ACCESS_KEY: the `token` field of the vault item media-object-api
aws --endpoint-url https://storage-host.example.test/s3 --region stado \
s3 sync ./photos s3://media/uploads/photos/
Links that need no key and expire: stado storage url stado://media/public/report.pdf --presign get --expires 3600.
4. Keep a second copy
A self-hosted store is one disk until it is copied elsewhere. The copy is
declared on the storage machine, whose own store is local: under
storage.backup.* name another disk, or any provider of
storage providers, and every write is mirrored there. Copy
on demand with stado storage copy --from local --from-path … --to … and
compare with stado storage verify.
The hosts that reach the store through the object API (storage.backend stado) keep no replica of their own: their objects are already the storage
machine's, and a directory backup on such a host is refused, because it
would address the same objects under different names.
Source: this website