Skip to main content

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. Once installed, the tool is invoked as oyster (for example, oyster list-buckets); the operator-side counterpart used for app admin keys is oysterd.

Configuration​

The CLI looks for a config file in this order:

  1. Path specified with --config
  2. ./client.yaml (current directory)
  3. $XDG_CONFIG_HOME/oyster/client.yaml
  4. $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 the CLI uses 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"
warning

The URL must include the /api/v1 path. The CLI appends endpoint paths (such as /buckets) directly to this URL.

Precedence for the active-context name (highest first):

  1. --context <name> flag
  2. OYSTER_CONTEXT environment variable
  3. active_context field in client.yaml

If you set none of the three and the file has exactly one context, the CLI uses that context 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​

FlagDescription
--config <PATH>Path to config file
--context <NAME>Named context to use (overrides OYSTER_CONTEXT or active_context)
--url <URL>Oyster server URL (overrides the context's url)
--api-key <KEY>API key (overrides the context's api_key)
--jsonOutput JSON instead of human-readable format

Bucket management​

Use the following commands to create, list, and delete buckets.

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​

Use the following commands to upload, download, list, and delete 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 you omit --content-type, 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

Each upload to a key replaces its tags. 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. Oyster stores tags in its database and shares them 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​

Operators manage API keys and S3 access keys through the Admin API, not through the CLI. See the Admin API docs for details on creating, listing, and revoking keys.

Other commands​

Use the following commands to view the wallet address and the resolved configuration.

View wallet address​

oyster wallet

View resolved configuration​

oyster info

Shows which config file the CLI loads, 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 CLI reads the key as a line instead, which is 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. Each app supports multiple admin keys 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, the CLI auto-selects it.
  • 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 to context.api_key in client.yaml. Without this flag, the CLI prints the bearer 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-oldest to revoke the oldest active key (sorted by created_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. The command never returns bearer secrets.

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:

ExtensionContent-Type
.txttext/plain
.html, .htmtext/html
.csstext/css
.csvtext/csv
.jsapplication/javascript
.jsonapplication/json
.xmlapplication/xml
.yaml, .ymlapplication/yaml
.pngimage/png
.jpg, .jpegimage/jpeg
.gifimage/gif
.svgimage/svg+xml
.webpimage/webp
.pdfapplication/pdf
.zipapplication/zip
.gz, .gzipapplication/gzip
.tarapplication/x-tar
.wasmapplication/wasm
.mp3audio/mpeg
.mp4video/mp4
.webmvideo/webm
(other)application/octet-stream

Where does the --app flag go?​

--app <name> is not one of the top-level global flags (--config, --context, --url, --api-key, and --json). It is scoped to the oyster app account subcommand tree. Use it only with oyster app account commands, and only when the active context defines more than one app.

How do you get an API key?​

Your Oyster administrator creates your account and issues you an initial API key (a Bearer token). Add that key as api_key in your context. With that key you can create additional API keys, manage buckets and blobs, and generate S3-compatible access keys.

If your context already has an app with an admin_key, you can mint an account and its initial API key yourself. The --activate flag saves the new bearer to context.api_key in client.yaml:

$ oyster app account create --name alice --activate