- Go 98.2%
- Makefile 1.1%
- Shell 0.7%
Map INVALID_WORKSPACE_ID / WORKSPACE_NOT_FOUND / WORKSPACE_ACCESS_DENIED / AMBIGUOUS_PROJECT_SLUG, require membership before saving workspace use, and surface conflict/not-found messages for share slugs and non-member access. |
||
|---|---|---|
| .forgejo/workflows | ||
| cmd/btx | ||
| docs | ||
| internal | ||
| protos@cde5ae3af5 | ||
| scripts | ||
| .gitignore | ||
| .gitmodules | ||
| buf.gen.yaml | ||
| buf.yaml | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
btx — Binary Transfer CLI
CLI for Binary Transfer. gRPC handles all daily operations; authenticate with a personal access token (PAT). File bytes go direct to S3 via presigned URLs.
Requirements
- Go 1.26+
protoc35.1 +protoc-gen-gov1.36.11 +protoc-gen-go-grpcv1.6.2 (for stub regeneration)- Git submodules initialized (
protos/)
Quick start
git submodule update --init --recursive
make btx
export BTX_TOKEN=btx_pat_... # create PAT in the web UI
btx auth set-token btx_pat_... # or rely on BTX_TOKEN / keyring
btx workspace use my-team
btx upload ./file.pdf # workspace-visible (shared with team)
btx upload ./secret.pdf --private # owner-only
btx upload ./file.bin --project demo # upload into a project
btx upload ./v2.pdf --replace SHARE_KEY # new version, same share link
btx files ls # includes teammates' workspace-visible files
btx files download 42 -o file.bin
btx cp 42 ./file.bin # download alias
btx shares ls
btx project ls # EDIT flag; --wide also shows MANAGE
btx whoami
Uploads are workspace-visible by default (discoverable by all members of the current workspace). Use --private / -p for owner-only files. Set workspace context with btx workspace use SLUG or BTX_WORKSPACE_ID.
btx workspace use only accepts workspaces you belong to (slug or UUID). Bad / unknown / non-member X-Workspace-Id values are rejected by the API (INVALID_WORKSPACE_ID, WORKSPACE_NOT_FOUND, WORKSPACE_ACCESS_DENIED) — they no longer silently unscope. Happy path: send a workspace you’re a member of, or omit the header / leave workspace unset when the command allows it.
Projects & permissions: workspace members inherit edit on workspace projects (upload, rename/patch, move, upload-version). Delete share/file needs manage (or file ownership). Only the project owner (is_admin) can delete a project (ADMIN_REQUIRED otherwise). btx project create requires a workspace (WORKSPACE_REQUIRED if unset). The personal project is me (btx project get me); it is not included in workspace project lists. Prefer a workspace context for slug lookups — without one, an ambiguous slug may return AMBIGUOUS_PROJECT_SLUG (HTTP 409). Non-member project download/access may surface as not found (404) rather than 403. Custom share slugs that collide with another share’s key are rejected by the API.
btx project members ls merges the workspace roster (inherited access) with ProjectMember overrides (elevations, usually manage). Use --overrides for override rows only. btx project members add elevates a workspace member to manage — not read/edit.
# optional env aliases
export BTX_WORKSPACE_ID=<uuid>
Authentication
The CLI is PAT-only — no login, OIDC, or token creation in the CLI.
- Create a PAT in the Binary Transfer web UI
- Provide it via
BTX_TOKEN,btx auth set-token, or config/keyring - Daily commands use gRPC metadata:
authorization: Bearer …,x-workspace-id: …
btx auth token list and btx auth token revoke manage existing tokens over gRPC (they do not create tokens).
Public (no auth): btx share get KEY, btx share download KEY
Command tree
btx
├── logout
├── auth
│ ├── set-token
│ ├── logout
│ └── token list|revoke
├── doctor
├── whoami
├── workspace|ws list|get|use|current|create|update|members …
├── settings expiration show|set / preferences show|set
├── upload PATH [project[/folder]] # --private / --replace SHARE_KEY / --project
├── cp SRC DST # upload local→project or download ID→local
├── sessions|sess list|in-progress|status|watch|resume|parts|sync-plan|check|teardown|refresh-token
├── files|file ls|get|download|mv|lock|unlock
├── shares|share ls|get|edit|rm|upload-version|download|tags …
├── project|proj ls|get|create|update|delete|move|fav|unfav|files|shares|download|tags|pin|unpin|pins|members …
└── download KEY|FILE_ID|URL
See docs/rpc-coverage.md for the full RPC ↔ command matrix.
Global flags
| Flag | Description |
|---|---|
--host |
API/gRPC host |
--grpc-port |
gRPC port (default 50051 dev / 60051 prod) |
--workspace |
Workspace UUID (x-workspace-id) |
--output |
table | json | yaml | quiet |
-v / --verbose |
Verbose output |
--no-progress |
Disable progress bars |
--no-color |
Disable Unicode/color in tables |
BTX_DEBUG=1 |
Log gRPC method + latency |
List table flags
Many list commands (files ls, shares ls, sessions list, project ls, …) support:
| Flag | Description |
|---|---|
--columns id,name,... |
Show/reorder columns |
--wide |
Show all columns |
--limit N / --offset N |
Client-side pagination |
--project SLUG |
Filter by project (client-side on files/shares) |
--search TEXT |
Filter rows by filename/key (where supported) |
--sort FIELD / --sort-order asc|desc |
Client-side sort (where supported) |
Examples:
btx files ls --columns id,owner,visibility,access,filename
btx files ls --visibility private
btx shares ls --wide --project team
btx files ls demo/inbox --limit 20
btx sessions list --status in_progress
Positional project/folder targets:
btx files ls demo/inbox
btx upload ./report.pdf demo/archive
btx upload ./notes.pdf --private
btx upload ./report-v2.pdf --replace abc123
btx shares upload-version abc123 ./report-v2.pdf
btx project create --name Demo --slug demo # needs workspace context
Configuration
~/.config/btx/config.yaml (or $BTX_CONFIG):
grpc_addr: "localhost:50051"
tls: false
token: ""
workspace_id: ""
default_project: ""
default_output: "table"
parallel_parts: 4
part_size_mb: 8
Environment:
| Variable | Maps to |
|---|---|
BTX_TOKEN |
PAT |
BTX_HOST |
grpc_host |
BTX_WORKSPACE / BTX_WORKSPACE_ID |
workspace_id |
BTX_GRPC_ADDR |
grpc_addr |
BTX_UPLOAD_CONCURRENCY |
parallel_parts |
BTX_ENV=dev |
local dev defaults |
Config key alias: upload_concurrency → parallel_parts
Shell completion
The completion script is multi-line. Always quote command substitution in zsh/bash:
# zsh (~/.zshrc)
eval "$(btx completion zsh)"
# bash (~/.bashrc)
eval "$(btx completion bash)"
# fish
btx completion fish | source
Wrong (breaks the script — completions will not load):
eval $(btx completion zsh) # missing quotes
Protos
Protos live in the protos/ git submodule. Generated Go stubs under gen/ are not committed — make build, make test, and CI run make generate (alias make proto-go) from the submodule.
git submodule update --init --recursive
make btx # generates gen/ then builds
Local requirements: protoc 35.1, protoc-gen-go v1.36.11, protoc-gen-go-grpc v1.6.2 (see Makefile for install hints).
To bump API definitions: update the protos/ submodule ref only — no separate gen/ commit.
Development
make test
make lint
make btx
Integration tests (requires API docker-compose):
BTX_INTEGRATION=1 go test -tags=integration ./internal/integration/...
Architecture
btx ──gRPC──► Transfer / File / Share / Project / Workspace / User / Settings
│
└──HTTP PUT/GET──► S3 presigned URLs (User-Agent: btx/version on share download)
CI
| Workflow | Trigger | Purpose |
|---|---|---|
ci.yaml |
push, PR | vet, test, build |
integration.yaml |
manual | optional integration tests |
release.yaml |
tag v* |
cross-compile + release |
Install
GOPROXY=https://git.binarycollective.de/api/packages/binary/go \
go install git.binarycollective.de/binary/binary-transfer-cli/cmd/btx@latest
Or make cross for platform binaries in dist/.