Beta

The CLI Manual

opake is the reference client. Everything the web app does maps back to a command here, and a few things only the CLI does today: shell completions, the background maintenance daemon, and the scripting hooks you'd want for pipelines and cron jobs.

Installing #

The CLI is written in Rust. For now it only ships as source; pre-built binaries and a crates.io release are on the roadmap. Needs Rust 1.75 or later and Git.

git clone https://github.com/Opake-at/Opake
cd opake && cargo install --path apps/cli

cargo install drops the binary at ~/.cargo/bin/opake. Confirm with opake --version.

Shell completions #

Every modern shell gets tab-completion for commands, flags, and subcommands. Pick yours:

opake completions bash > ~/.local/share/bash-completion/completions/opake
opake completions zsh > ~/.zfunc/_opake
opake completions fish > ~/.config/fish/completions/opake.fish

How the CLI thinks #

Three concepts orient the rest of this page.

Paths look like file paths: report.pdf, projects/Q4/notes.md, /. Internally Opake identifies records by AT-URI (at://did:plc:xxx/at.opake.document/yyy), and most commands accept either — the friendly path when you're at a prompt, the URI when you're piping URIs around between commands.

Workspaces are a separate namespace from your personal cabinet. A bare opake ls lists the cabinet; opake ls --workspace family-photos lists that workspace. Every file-touching command takes the same --workspace flag.

Accounts stack. opake account login adds one; opake account list shows the stack; --as alice.bsky.social runs a single command against a specific account without flipping the default. Useful when you keep a personal account and a work account side by side and don't want to remember which is currently primary.

Two more globals worth knowing up front: -v increases log verbosity (-vv debug, -vvv trace), and --help works on every subcommand.


1. Signing in #

OAuth through your PDS is the default. The first opake account login on a fresh machine also generates your identity keys and prints a 24-word seed phrase — write it down before you close the window. It's the only thing that can bring your identity back if you lose every device you're signed in on.

opake account login alice.bsky.social

If your PDS is somewhere other than the Bluesky cluster, point at it:

opake account login alice.example.com --pds https://pds.example.com

For PDSes that don't speak OAuth, fall back to app-password auth:

opake account login alice.example.com --legacy

Coming back to an existing identity #

Two ways to sign in on a new machine without generating a fresh identity: pair off an existing device, or restore from the seed phrase.

opake pair request      # on the new device
opake pair approve      # on the existing device

Pairing hands the new device a wrapped copy of your identity keys through your PDS. Both sides delete their half of the handshake as soon as the transfer is complete.

Device pairing. Both devices coordinate through your PDS; neither side ever transmits your identity keys in the clear.

The other way back in is the seed phrase, in case no paired device is available:

opake recover                    # interactive; prompts for all 24 words
opake recover -f seed-backup.txt

Managing the account stack #

opake account list
opake account set-default bob.other.com
opake ls --as alice.bsky.social

--as accepts a handle or a DID. Handles get resolved fresh on each invocation, so if someone moved providers recently the CLI still finds them.


2. Files #

opake upload puts a file in your cabinet. Destination paths are optional; omit them and the file lands at the root.

opake upload report.pdf
opake upload report.pdf --dir projects/Q4
opake upload report.pdf --description "Quarterly financial summary"
opake upload report.pdf --workspace team

Reading them back #

opake ls
opake ls projects/Q4 -l               # long format, sizes + mime types
opake ls --tag finance
opake tree
opake download report.pdf -o ~/Downloads/
opake cat notes.md                    # decrypts to stdout, pipe-friendly

opake cat is opake download --stdout with a shorter name, and it composes with the rest of your shell:

opake cat grocery-list.txt | grep -i bread

Moving, renaming, deleting #

Rename is a metadata change. Move is structural.

opake metadata rename report.pdf quarterly-report.pdf
opake move report.pdf projects/Q4/
opake mkdir archive
opake rm old-draft.md
opake rm -r archive/

Editing metadata #

Document metadata is the part Opake still lets you see in the clear — locally. Names, tags, and descriptions are encrypted on the wire but rendered in your terminal.

opake metadata show report.pdf
opake metadata describe report.pdf "Q4 financial summary"
opake metadata tag add report.pdf finance
opake metadata tag remove report.pdf draft

3. Sharing #

One file, one person #

opake share new secret.pdf bob.bsky.social

Opake resolves the handle, fetches Bob's public key from his PDS, wraps the file's content key against it, and publishes a grant record. Bob sees the file appear in his inbox the next time his client syncs.

opake share list                      # grants you've issued
opake share inbox                     # grants issued to you
opake share revoke at://did:plc:123/at.opake.grant/tid456

Revoking deletes the grant record. It doesn't un-decrypt any copy Bob has already downloaded — that ship sailed the moment he opened the file. What revocation does buy you is: Bob can't re-fetch, can't get future updates, and anyone else browsing his repo won't see the grant.

Grab the URI of a specific grant from opake share list.

A group of people #

Workspaces are shared folders with a shared encryption key. Adding someone to the workspace gives them access to every file in it; removing them doesn't re-encrypt history, but it does cut them off from future changes.

opake workspace create family-photos
opake workspace add-member family-photos alice.bsky.social
opake workspace ls -l
opake upload vacation.jpg --workspace family-photos
opake workspace remove-member family-photos alice.bsky.social
opake workspace leave family-photos

4. Looking someone up #

Before you ship a grant to a handle you got over email or Signal, it's sometimes worth verifying who's on the other end:

opake resolve bob.bsky.social

Prints Bob's DID, his PDS, and his X25519 encryption public key. Ask Bob to read his key fingerprint to you out-of-band; if it matches what opake resolve shows, you've confirmed the identity the grant will actually target.


5. Background sync #

The daemon runs maintenance tasks that don't need to be interactive: the SSE subscription for live updates, proactive token refresh, and re-encryption passes after a key rotation. You don't strictly need it for day-to-day use — every interactive command triggers the minimum sync it needs before running. The daemon just means you don't eat that latency each time.

opake daemon run                      # foreground, for systemd / launchd units
opake daemon list-tasks               # what's queued and what's running
opake daemon install                  # generate + install a launchd or systemd unit
opake daemon uninstall

opake daemon install picks the right service format for your platform (launchd on macOS, systemd on Linux) and writes the unit file where the service manager expects it. The service starts automatically on login.


6. Starting over #

Three escalating levels of clean slate.

opake account logout alice.bsky.social

Drops local session credentials for one account. Your encrypted files stay on your PDS untouched; signing back in picks them right up.

opake rm -r /

Empties your cabinet of every document and directory but keeps your identity, keys, and workspace memberships intact. Good for when you want to repopulate from scratch without re-pairing or regenerating anything.

opake purge --dry-run
opake purge --force

The real nuke. Deletes every at.opake.* record from your PDS — files, grants, workspaces, published encryption key, the lot. Your atproto identity itself survives (that belongs to the PDS, not Opake), but every trace of Opake having been there is gone. --dry-run lists what would go without touching anything.