s3s

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 6, 2026 License: MIT

README

s3s

A fast, keyboard-driven terminal browser for S3-compatible object storage — think k9s, but for your buckets.

Release CI Go Report Card Go version

Point it at Ceph RGW or MinIO, switch clusters like kubectl contexts, and walk millions of keys, inspect metadata, and preview files without ever leaving the terminal. Read-only by default — safe to point at production — with opt-in writes (--write) for the small, growing set of mutating operations, and a per-context readonly flag that keeps protected environments untouchable.

⚠️ Alpha. Usable day-to-day but rough edges remain; flags, config, and the UI may change. Feedback and issues welcome.

The interface borrows from two tools we love: k9s (bordered resource tables, fast navigation, context switching) and Claude Code (warm color palette, a compact multi-line status footer).

Screenshots

Coming soon — a recording of context switching, tree navigation, and previews.

Features

  • kubectl-style contexts — define clusters, users, and contexts in one YAML file; switch between them live (no restart) or jump by number (19).
  • Read-only by default, runtime write toggle — s3s starts read-only. Arm write at runtime with a hotkey (w) — arming takes a deliberate confirmation, disarming is instant — and while armed a loud, high-contrast [RW] badge shows on every screen so you can never mutate production thinking you were safe. --write just starts armed; a context marked readonly: true can never be armed. Mutations (create folder, delete, upload, copy, move/rename, recursive delete, bulk delete/copy) keep the two-tier confirmation (simple y/N vs typed target/count) and are confined to the storage layer by a CI guard.
  • Download & bulk operations — pull a full object to local disk (a read — works read-only, against production); multi-select objects (space) and act on the batch: bulk download (mirrors the key hierarchy into local subdirs), bulk delete, bulk copy, each with a truthful per-item succeeded/failed summary.
  • Storage analytics (du) — analyze a bucket or prefix and see the total size, object count, and a ranked largest-first breakdown of its immediate children (an ncdu-style view) with live progress and drill-down — what's eating space, in-TUI.
  • Sortable lists — sort any level by name, size, or last-modified and toggle direction (s / S); the sort persists across navigation.
  • Secure credential sources — a context resolves its secret from exactly one of: the OS keychain, an external command (pass, Vault, 1Password, sops…), an AWS shared profile, or the classic ${ENV} reference — with a secure no-echo prompt fallback. Stop exporting a secret into every shell; the secret never lives on disk in plaintext. Manage keystore secrets with s3s cred set|rotate|rm <context>.
  • Tree navigation — walk the key namespace by the / delimiter with on-demand pagination; never loads a whole bucket up front. Per-session cache with manual refresh.
  • Combined object view — press Enter on an object to see its metadata and content side-by-side in one screen (no separate steps).
  • Inline previews — scrollable text and visual images (ANSI half-block, works in any 24-bit terminal), bounded to the first 5 MiB with a truncation notice; safe summary for binaries.
  • Fast filter & search — filter buckets by name instantly; server-side prefix search within a level (debounced, complete results — not just what's loaded).
  • Non-blocking UI — every backend call runs off the event loop; superseded loads are cancelled, in-flight loads show a spinner and can be cancelled.
  • Secrets never leak — credentials are redacted everywhere; logs go to a file only (the TUI owns the terminal). ${ENV} references keep keys out of config.

Installation

Homebrew (macOS)
brew install danchupin/tap/s3s
Scoop (Windows)
scoop bucket add danchupin https://github.com/danchupin/scoop-bucket
scoop install s3s
Debian / Ubuntu (.deb)

Grab the latest .deb from the Releases page, then:

sudo dpkg -i s3s_*_linux_amd64.deb
RHEL / Fedora (.rpm)
sudo rpm -i s3s_*_linux_amd64.rpm
Alpine (.apk)
sudo apk add --allow-untrusted s3s_*_linux_amd64.apk
Go
go install github.com/danchupin/s3s/cmd/s3s@latest
Prebuilt binaries

Download a .tar.gz / .zip for your OS/arch from the Releases page, extract, and put s3s on your PATH.

Building From Source

Requires Go 1.25+.

git clone https://github.com/danchupin/s3s
cd s3s
make build   # -> bin/s3s

Configuration

Config lives at $XDG_CONFIG_HOME/s3s/config.yaml (default ~/.config/s3s/config.yaml); override with --config <path>.

Generate it interactively
s3s config init                       # write to the default XDG path
s3s config init --config ./my.yaml    # custom path

The wizard asks for the endpoint, addressing/TLS, credentials, and context name, then merges into any existing config. The secret is stored as a ${ENV} reference (never written to disk) and the wizard prints the export line to set it.

Or write it by hand
apiVersion: s3s/v1
clusters:
  - name: minio-local
    endpoint: http://127.0.0.1:9000
    region: us-east-1
    pathStyle: true          # path-style; false => virtual-host/domain style
    tlsSkipVerify: false     # explicit opt-in, https only
users:
  - name: dev
    accessKeyId: admin
    secretAccessKey: ${S3S_DEV_SECRET}   # ${ENV} resolved at load; never logged
  - name: public
    anonymous: true          # public buckets, no signing
contexts:
  - name: local
    cluster: minio-local
    user: dev
current-context: local
chmod 600 ~/.config/s3s/config.yaml
export S3S_DEV_SECRET=password

Active-context precedence: --context <name> > S3S_CONTEXT env > current-context.

Credential sources

A non-anonymous user names exactly one secret source (more than one is a config error). The secret never lives on disk in plaintext and need not be exported into every shell:

users:
  - name: prod                    # OS keychain (store via: s3s cred set prod)
    accessKeyId: AKIAPROD
    keychain: true
  - name: vault                   # external command — owner-only config required
    accessKeyId: AKIAVLT
    cmd: "vault kv get -field=secret s3/prod"
  - name: aws                     # ~/.aws/credentials profile (static keys)
    awsProfile: prod
  - name: ci                      # classic ${ENV} — still works for automation
    accessKeyId: AKIACI
    secretAccessKey: ${S3S_CI_SECRET}

s3s cred set|rotate|rm <context> manages a context's secret in the OS keystore only. If no source resolves, s3s prompts securely (no echo) at startup and offers to save to the keystore. A group/world-readable config triggers a warning; a cmd: source is refused on a group/world-writable config (it would let a tampered file run a command).

Running

s3s                  # uses current-context (read-only by default)
s3s --context local  # explicit context
s3s --write          # START in write mode (toggle at runtime with `w`; readonly contexts stay protected)
s3s --version        # print version
A local MinIO to try it
docker run -p 9000:9000 -p 9001:9001 \
  -e MINIO_ROOT_USER=admin -e MINIO_ROOT_PASSWORD=password \
  minio/minio server /data --console-address ":9001"

Key Bindings

Arrow keys are the primary, advertised navigation; the vim aliases (h/j/k/l, g/G) still work and are listed in the help overlay (?). The write operations and refresh live behind a single contextual action menu opened with a — the footer advertises just a actions, not a wall of per-op keys.

Key Action
/k, /j move selection
/l/Enter enter bucket/dir, or open an object (metadata + content)
/h/Esc back to parent (or clear an active filter/search); cancels an in-flight load
g/Home, G/End jump to top / bottom
/ filter buckets / search a level by prefix; Esc clears
space mark/unmark an object for multi-select (bulk via the action menu)
s / S cycle the sort column (name/size/modified) · toggle direction
w arm/disarm write at runtime (confirm to arm; instant to disarm)
a action menu — contextual operations for the selection (see below)
c switch context · 19 jump to a context by number
? help (full keymap, incl. vim aliases, + connection details) · q / Ctrl+C quit

The action menu (a) lists only what applies to the current selection and context. Download and analyze (du) are reads — offered even read-only; bulk delete/copy and the single-object write ops appear only while write is armed:

Menu item Notes
download object selected — a read; works read-only
analyze bucket / folder / current level — du, a read; works read-only
download selected (N) bulk download of the marked objects — a read; mirrors the key hierarchy
delete selected / copy selected bulk over the marked objects — write mode
refresh reload the current list (always available, incl. the bucket list)
new folder · upload here write mode
delete · copy · move / rename object selected — write mode (delete/move typed confirm)
recursive delete folder selected — write mode; typed confirm

In a read-only context the menu offers reads only (download, analyze, refresh). The footer stays at most three rows — a compact identity line (● context [RW|RO] · cluster), one contextual hint row (capped at six, with a ? more cue when narrow), and a status line.

Images render as ANSI half-block by default. Terminal graphics protocols (kitty/iTerm2) are available behind S3S_IMAGE_PROTOCOL=kitty|iterm2|auto but are experimental — Bubble Tea's cell renderer doesn't reliably pass them through.

Logs: $XDG_STATE_HOME/s3s/s3s.log (or ~/.local/state/s3s/s3s.log).

Roadmap

Larger items on the horizon (full list in ROADMAP.md):

  • Full-quality image preview via an external viewer.
  • Richer previews — syntax highlighting and a hex view for binaries.
  • Copy key / S3 URI / ETag to the clipboard.
  • Presigned URLs; bucket administration (policy/lifecycle/encryption/CORS); object versioning management; incomplete-multipart-upload cleanup.

Development

make test               # unit tests (fake storage) — no Docker needed
make test-integration   # + real MinIO via testcontainers (needs Docker)
make fmt vet lint       # formatting, go vet, golangci-lint
make check-readonly     # structural read-only guard

Integration tests t.Skip automatically when Docker is unreachable.

Architecture
  • internal/storage — read-only Storage interface + aws-sdk-go-v2 impl (the only importer of service/s3) + an in-memory fake for unit tests.
  • internal/config — kubectl-style YAML loader, ${ENV} resolution, validation, and the config init wizard.
  • internal/cache — per-session, TTL-free level cache (manual refresh only).
  • internal/preview — text/image/binary classification and image rendering.
  • internal/logging — file slog handler + a redacting Secret type.
  • internal/ui — Bubble Tea (v2) model; depends only on the storage interface.
  • cmd/s3s — wiring: load config → build storage → run the TUI.

Built with Bubble Tea, Lip Gloss, and aws-sdk-go-v2.

License

MIT © Daniil Chupin

Directories

Path Synopsis
cmd
s3s command
Command s3s is a read-only, keyboard-driven TUI for browsing S3-compatible object storage (Ceph RGW, MinIO).
Command s3s is a read-only, keyboard-driven TUI for browsing S3-compatible object storage (Ceph RGW, MinIO).
internal
cache
Package cache holds a per-session, TTL-free cache of loaded tree levels.
Package cache holds a per-session, TTL-free cache of loaded tree levels.
config
Package config loads and validates the kubectl-style YAML config (~/.config/s3s/config.yaml): clusters, users, contexts, and current-context.
Package config loads and validates the kubectl-style YAML config (~/.config/s3s/config.yaml): clusters, users, contexts, and current-context.
localfs
Package localfs is a small, UI-agnostic reader of the local filesystem used by the upload file browser.
Package localfs is a small, UI-agnostic reader of the local filesystem used by the upload file browser.
logging
Package logging provides a file-based slog logger and a redacting Secret type.
Package logging provides a file-based slog logger and a redacting Secret type.
preview
Package preview classifies and renders bounded object content for the preview pane: scrollable text, visual images, and a safe summary for binary.
Package preview classifies and renders bounded object content for the preview pane: scrollable text, visual images, and a safe summary for binary.
secret
Package secret resolves a context's S3 secret from one of several pluggable, secure sources (OS keychain, an external command, an AWS shared profile, an env-resolved inline value) so the secret never needs to live in the shell environment or on disk in plaintext (005 US6).
Package secret resolves a context's S3 secret from one of several pluggable, secure sources (OS keychain, an external command, an AWS shared profile, an env-resolved inline value) so the secret never needs to live in the shell environment or on disk in plaintext (005 US6).
storage
Package storage is the sole boundary between the UI and S3-compatible backends.
Package storage is the sole boundary between the UI and S3-compatible backends.
ui
Package ui is the Bubble Tea (v2) TUI layer.
Package ui is the Bubble Tea (v2) TUI layer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL