oyster-cli Quick Start
oyster-cli is a command-line tool for interacting with Oyster. It wraps
the JSON API and handles authentication, content-type detection, and
pagination for you.
Configuration
The CLI looks for a config file in this order:
- Path specified with
--config ./client.yaml(current directory)$XDG_CONFIG_HOME/oyster/client.yaml$HOME/.config/oyster/client.yaml
Contexts
client.yaml holds a map of named contexts, each pointing at a
different Oyster deployment. The top-level active_context selects which
context is used by default.
Example client.yaml:
active_context: testnet
contexts:
testnet:
url: "https://oyster.testnet.example/api/v1"
api_key: "your-api-key-here"
apps:
my-app-1:
admin_key: "<64-char hex admin key>"
my-app-2:
admin_key: "<64-char hex admin key>"
devnet:
url: "http://localhost:3000/api/v1"
api_key: "dev-key"
Important: The URL must include the
/api/v1path. The CLI appends endpoint paths (such as/buckets) directly to this URL.
Precedence for the active-context name (highest first):
--context <name>flagOYSTER_CONTEXTenvironment variableactive_contextfield inclient.yaml
If none of the three is set and the file has exactly one context, that
context is used automatically. Ad-hoc --url ... --api-key ...
invocations without any context still work for one-time commands.
You can also override individual fields with flags:
oyster --url http://localhost:3000/api/v1 --api-key "your-key" list-buckets
Global flags
| Flag | Description |
|---|---|
--config <PATH> | Path to config file |
--context <NAME> | Named context to use (overrides OYSTER_CONTEXT / active_context) |
--url <URL> | Oyster server URL (overrides the context's url) |
--api-key <KEY> | API key (overrides the context's api_key) |
--json | Output JSON instead of human-readable format |
Bucket management
Create a bucket
oyster create-bucket my-bucket
List buckets
oyster list-buckets
Limit results:
oyster list-buckets --limit 10
Delete a bucket
oyster delete-bucket my-bucket
The bucket must be empty. Delete all blobs first, or the server returns an error.
Storing and reading blobs
Upload a file
oyster store photo.png --bucket my-bucket
The key defaults to the filename (photo.png). Override it with --key:
oyster store photo.png --bucket my-bucket --key images/vacation/photo.png
Set a specific content type:
oyster store data.bin --bucket my-bucket --content-type application/x-custom
If --content-type is omitted, the CLI auto-detects it from the file
extension (see Content-Type Detection below).
Attach tags at upload time with --tag key=value (repeatable):
oyster store photo.png --bucket my-bucket --tag env=prod --tag team=platform
Tags are replaced on every upload to a key. See Blob tags for the limits and for managing tags after upload.
Download a file
oyster read hello.txt --bucket my-bucket
This prints the blob contents to stdout. Save to a file with -o:
oyster read hello.txt --bucket my-bucket -o downloaded.txt
Reading blobs does not require an API key. Reads are public.
List blobs
oyster list-blobs --bucket my-bucket
Output (human-readable):
KEY CONTENT_TYPE SIZE CREATED
hello.txt text/plain 14 2025-01-15T10:31:00Z
images/cat.png image/png 204800 2025-01-15T11:00:00Z
Delete a blob
oyster delete hello.txt --bucket my-bucket
Blob tags
The oyster tags command group manages the key=value tags on a blob. Tags
are stored in Oyster's database and shared with the
S3 object-tagging operations. Limits:
max 10 tags, key ≤ 128 bytes, value ≤ 256 bytes, set ≤ 2048 bytes, and a
restricted charset (ASCII alphanumerics plus space and + - = . _ : / @).
List tags
oyster tags list --bucket my-bucket --key hello.txt
Set a single tag
Upserts one tag (key=value):
oyster tags set --bucket my-bucket --key hello.txt env=prod
Remove a single tag
oyster tags rm --bucket my-bucket --key hello.txt env
Clear all tags
oyster tags clear --bucket my-bucket --key hello.txt
Replace vs. merge
replace sets the entire tag set, dropping any tags not listed. merge
upserts the supplied tags, leaving other existing tags untouched. Both take
repeatable --tag key=value flags:
# Full replace — the blob ends up with exactly these two tags
oyster tags replace --bucket my-bucket --key hello.txt \
--tag env=prod --tag team=platform
# Merge — adds/updates these tags, keeps the rest
oyster tags merge --bucket my-bucket --key hello.txt --tag team=storage
API key and access key management
API keys and S3 access keys are managed by operators through the Admin API, not through the CLI. See the Admin API docs for details on creating, listing, and revoking keys.
Other commands
View wallet address
oyster wallet
View resolved configuration
oyster info
Shows which config file is loaded, the server URL, and the API key prefix.
App admin-key management
Apps are first-class principals that authenticate admin app-management calls
(creating accounts, issuing API keys and S3 access keys) independently of
end-user API keys. They need a way to store the per-app admin key without
leaking it through shell history. The CLI persists admin keys under
contexts.<ctx>.apps.<app_name>.admin_key.
Import an admin key
oyster app import my-app
Prompts for the admin key without echoing it (when stdin is a tty), then
writes it to the active context's apps.my-app entry. If stdin is a pipe,
the key is read as a line instead — useful for scripts. Requires that
client.yaml already exists; the CLI does not auto-create it.
Rotation
Admin keys do not expire. Rotation is operator-driven with AWS-style two-key overlap:
# operator
oysterd app issue-admin-key <APP_ID>
# stdout: <new admin_key> (the raw bearer — the only machine-readable output)
# stderr: a `tracing` log line with the key id + prefix (needed later to
# revoke). It's an `info`-level log, so it appears with the default
# log filter but is suppressed if RUST_LOG raises the threshold above
# `info`. Capture the key id from `oysterd app list-admin-keys`.
# user — replace the local entry with the new key
oyster app import my-app
# operator — after confirming nothing still uses the old key
oysterd app revoke-admin-key <OLD_KEY_ID>
oysterd app list-admin-keys <APP_ID> shows all keys (active and
revoked), so an operator can confirm what is live. Multiple admin keys per
app are supported with no cap.
Webhook management
oyster app webhook drives the self-service webhook endpoints
(Set Webhook URL) using the active
context's admin key. When the context defines more than one app, pass
--app <name> to choose which one.
# Show the current webhook URL and public key
oyster app webhook show
# Register or rotate the webhook URL (each call mints a fresh Ed25519 keypair;
# the printed public key is needed to verify subsequent deliveries)
oyster app webhook set https://example.com/oyster/webhook
# Clear the webhook URL and discard the keypair
oyster app webhook clear
See Webhooks for the delivery signature format.
Account management
Once a context has at least one app with an admin_key, the
oyster app account subcommand tree manages the accounts that app
owns. Use it to mint accounts, rotate which account the CLI's
api_key points at, and inspect the API keys an account has issued.
--app <name> selector
Every oyster app account subcommand resolves which app to act
through using crates/oyster-cli/src/config.rs::resolve_admin:
- If the active context defines exactly one app, it is auto-selected.
- If the active context defines multiple apps, you must pass
--app <name>(or the command errors and lists the known apps). - If the active context defines zero apps, the command errors.
Import an admin key first with
oyster app import.
oyster app account list # active context has 1 app
oyster --app my-app account list # multiple apps; pick one
Subcommands
The following subcommands are available under oyster app account.
list
Tabular view of the accounts owned by the selected app. Each row
is id, name, created_at, active_api_key_count.
oyster app account list
create [--name NAME] [--note NOTE] [--activate]
Mints a fresh account plus an initial API key for it.
--name NAME: human-readable label stored on the account.--note NOTE: note attached to the issued API key (defaults to"api"server-side).--activate: atomically saves the new bearer tocontext.api_keyinclient.yaml. Without this flag, the bearer is printed once and you can wire it up yourself.
oyster app account create --name alice --activate
use <id-or-name> [--revoke <key_id> | --revoke-oldest]
Pivots the active context's api_key onto a different account. It
mints a fresh API key on the target account (note oyster-cli: activate <id-or-name>), atomically writes it to context.api_key,
and saves client.yaml.
There is a server-side cap of 3 active API keys per account
(MAX_API_KEYS_PER_ACCOUNT in
crates/oyster/src/routes/admin.rs). If use would exceed that
cap, the server returns 409 Conflict with "limit" in the
message and the CLI behavior depends on whether stdout is a TTY:
- TTY: the CLI uses
inquire::Select(inline, never alt-screen) to show the account's existing keys and ask which to revoke, then retries the mint. - Non-TTY (CI, scripts,
--json): the call fails unless you pre-select the key to revoke. Pass either:--revoke <KEY_ID>to revoke a specific key, or--revoke-oldestto revoke the oldest active key (sorted bycreated_at).
The two flags are mutually exclusive.
oyster app account use alice
oyster app account use alice --revoke-oldest
oyster app account use alice --revoke 0123abcd...
select
TTY-only inquire picker over the app's accounts. Dispatches to
use with the chosen account. Errors in non-TTY contexts.
Scripts should use use <id-or-name> directly.
oyster app account select
keys <id-or-name>
Lists API key metadata for the named account: id, note,
created_at, and revoked_at. Bearer secrets are never returned.
oyster app account keys alice
JSON output
Add --json to any command for machine-readable output:
oyster --json list-blobs --bucket my-bucket
{
"data": [
{
"key": "hello.txt",
"blob_id": "2cf24dba5fb0a30e...",
"content_type": "text/plain",
"size": 14,
"created_at": "2025-01-15T10:31:00Z"
}
],
"next_cursor": null
}
Content-type detection
When uploading without --content-type, the CLI guesses the MIME type from
the file extension:
| Extension | Content-Type |
|---|---|
.txt | text/plain |
.html, .htm | text/html |
.css | text/css |
.csv | text/csv |
.js | application/javascript |
.json | application/json |
.xml | application/xml |
.yaml, .yml | application/yaml |
.png | image/png |
.jpg, .jpeg | image/jpeg |
.gif | image/gif |
.svg | image/svg+xml |
.webp | image/webp |
.pdf | application/pdf |
.zip | application/zip |
.gz, .gzip | application/gzip |
.tar | application/x-tar |
.wasm | application/wasm |
.mp3 | audio/mpeg |
.mp4 | video/mp4 |
.webm | video/webm |
| (other) | application/octet-stream |