ebman 0.3.4

k9s-style TUI for AWS Elastic Beanstalk
ebman-0.3.4 is not a library.
Visit the last successful build: ebman-0.29.2

ebman

A k9s-style terminal UI for AWS Elastic Beanstalk.

Browse environments, drill into events / instances / metrics / queue / config, stream CloudWatch logs, edit env vars / option settings, deploy new versions — all without leaving the terminal.

Highlights

  • Live env table with sort, filter, group-by-app, health sparkline (trend window auto-labelled), severity tints, mouse support. Tab cycles between Envs and Apps scope.
  • Drill-down per env — opens to a Health rollup tab (drillable: j/k walks items, Enter jumps to source). Other tabs: Events (regex-searchable), Instances (with health causes + embedded SSM shell), Metrics (CloudWatch line charts; custom metrics with arbitrary dimensions), Queue (Worker tier; main + DLQ stats with viewer), Logs (one-shot snapshot or real tail -f from CW Logs), Config (tags + env vars read-only, plus cost estimate).
  • Red-env triage:why (or ! on the selected env) opens a four-section diagnostic overlay: recent events, alarms, instance health, recent deploys. Worker envs also get a main + DLQ peek. Updating envs are labelled with the kind of update in flight (deploy / config / scale); an env with active CW alarms no longer renders as plain green "Ready". Worker envs with a non-empty DLQ flip Red even when EB calls them healthy.
  • Daily-driver write surface — env vars (:env set/unset), tags (:tag/:untag), version deploy from existing label or local zip / S3 (:deploy --from, with --preview for a side-by-side current-vs-candidate overlay), saved-config CRUD, CloudWatch alarms CRUD, log streaming toggle, notifications endpoint, managed-update window, ALB scheme, instance type, key pair, IAM roles, deployment policy, rolling-update settings, health-check URL, capacity (:capacity — Min / Max / Instance type / Cooldown in one modal), plus a generic :set-option NAMESPACE OPTION VALUE escape hatch.
  • Worker / SQS workflow — DLQ viewer with per-message resend (r), strict-typed purge (p), bulk delete (x), peek-and-tail with long-polling.
  • Multi-region / multi-account:region all fans across every configured region in parallel; :account NAME switches via sts:AssumeRole (configure accounts.NAME in config.toml); :accounts lists AWS-Organizations child accounts; :find-env and :org-health both fan across ~/.aws profiles and configured AssumeRole accounts.
  • Bulk opsspace multi-selects; :batch-rebuild, :batch-restart, :batch-deploy LABEL, :batch-tag KEY VALUE, :batch-untag KEY, :batch-set-option NAMESPACE OPTION VALUE all fan out in parallel with per-env audit + pending pill rows.
  • Safety--read-only CLI flag or :readonly on disables every write surface; destructive actions (Terminate, DLQ purge) require typed-name confirmation; pre-flight dry-run shows impact (N instances across M AZs) + last 3 events before authorising; recent-change / mid-deploy traffic warnings in the confirm modal.
  • Audit log — every dispatched action and its outcome are appended to ~/.cache/ebman/audit.log; rotates at 1 MiB.
  • Power-user ergonomics — fuzzy command palette (Ctrl-K) across commands / envs / saved views / plugins, named filters + saved views, custom keybindings (F1-F12 and uppercase letters via ~/.config/ebman/keys.toml), plugin commands (~/.config/ebman/commands.toml), in-app :loglevel reload, :diff between envs.
  • Headless / scriptable--control-socket PATH exposes a Unix-socket interface; ebman ctl <op> is a one-shot client (screen / state / key <spec> / cmd <:cmd>).
  • Non-interactive CLIebman envs [--json] / ebman action rebuild --env NAME / ebman ctl ... for scripts and CI.

Install

Homebrew (macOS / Linux):

brew tap tombaldwin/tap
brew install ebman

Pre-built binary:

Download the tarball for your platform from the GitHub Releases page, verify the *.sha256 next to it, extract, and put ebman on your PATH.

Cargo:

cargo install ebman

Tested on Rust 1.91+. macOS (Apple Silicon + Intel) and Linux x86_64. AWS SDK uses the standard credentials chain (AWS_PROFILE / AWS_REGION env, ~/.aws/credentials, instance role, etc.).

Fonts (optional, for the prettier glyph set)

Ebman runs fine in any terminal with the default icons = "unicode" config. For the Powerline-style pill chain, tab ribbon, and per-tab MDI icons (icons = "powerline" or icons = "auto"), your terminal needs a Nerd Font installed — vanilla Powerline fonts give you the triangles but tofu/boxes where the tab icons should be.

1. Install a Nerd Font:

brew install font-meslo-lg-nerd-font           # Powerlevel10k crowd; safe default
brew install font-jetbrains-mono-nerd-font     # modern monospace, no ligature surprises

2. Set your terminal's font to one of the Mono variants — they're sized for fixed-width TUIs (e.g. MesloLGS Nerd Font Mono, JetBrainsMono Nerd Font Mono):

  • iTerm2: Preferences → Profiles → Text → Font
  • Terminal.app: Preferences → Profiles → Font → Change
  • Ghostty / Alacritty / WezTerm: font-family in the relevant config file
  • VS Code / Cursor terminal: terminal.integrated.fontFamily in settings

3. Tell ebman to use the new glyphs — either run :settings in ebman and pick auto (or powerline) from the Icons field, or add this to ~/.config/ebman/config.toml:

icons = "auto"   # probes the terminal at startup; falls back to "unicode"

Restart ebman (or use ebman ctl reload if you're driving via the control socket) so the startup probe runs against your new font. icons = "powerline" skips the probe and forces the Nerd glyph set unconditionally.

Without a Nerd Font, stick to icons = "unicode" (the default) — everything still works, you just don't get the per-tab MDI icons.

Quickstart

ebman                                  # launch the TUI
ebman --read-only                      # disable all write surfaces (audit-friendly)
ebman --control-socket ~/.cache/ebman/control.sock   # expose the ctl interface
ebman envs --json                      # non-interactive: print env list as JSON
ebman action rebuild --env myenv --yes # non-interactive: dispatch a rebuild
ebman ctl screen                       # dump the current frame from a running instance
ebman --version
ebman --help

Once running, press ? for a per-context keymap (Detail, DLQ, Action menu, Saved-configs overlay all have scoped help).

Keys

Normal mode (env table)

Key Action
j / k / wheel Move selection
g / G Top / bottom
19 Jump to position
' Name-jump (type prefix)
Enter Drill into env
Tab / Shift-Tab Switch scope (Envs ↔ Apps)
a Actions menu
b Open env in AWS console
D Describe overlay (raw env JSON)
space Multi-select
* Pin / unpin
! :why overlay (Red-env diagnostic)
/ Filter
: Command bar
^K Command palette
s / S Cycle sort key / reverse
^G Toggle group-by-application
^E Toggle events panel
^] Cycle focus (table ↔ events panel)
^D Cycle view mode (default / compact / spacious)
^X Toggle redact mode
^Y Yank filtered table as TSV
^W Yank equivalent aws elasticbeanstalk describe-environments
y / Y Yank CNAME / name
f Freeze / unfreeze auto-refresh
r / p Switch region / profile
^R / F5 Force refresh
? Help
q / ^C Quit

Detail view

Tabs cycle with Tab / Shift-Tab (or l / h). ^R re-fetches the active tab; R toggles per-tab auto-refresh; a opens the env actions menu; b opens the env in the AWS console.

Tab Per-tab keys
Health (default) j/k walk items, Enter drill into source tab (Events / Instances / Queue)
Events / filter, n/N next/prev match
Instances Enter / i info overlay, b EC2 console, s embedded SSM shell, y yank id, x terminate (Y/N)
Metrics [ / ] cycle range (15m → 24h), mouse hover for value-at-cursor
Queue j/k Main ↔ DLQ, Enter opens viewer, d quick-open DLQ
Logs ^R one-shot snapshot, s open live CW Logs streaming overlay, / filter
Config scrollable; tags + env vars + cost estimate read-only

DLQ viewer

j/k move, Enter view body, r resend (DLQ → main), x delete, p purge (typed-name confirm), m toggle Main ↔ DLQ, ^R re-peek.

Command reference

Type : to open the command bar. Tab-completion is not implemented, but Ctrl-K fuzzy-searches every command + env + view + plugin.

Navigation / inspection

  • :region NAME / :region all — switch region, or fan out across every configured region.
  • :profile NAME — switch AWS profile.
  • :account NAME — switch to a configured AssumeRole account (accounts.NAME in config.toml). Falls back to :profile NAME aliasing when no accounts. entry exists.
  • :accounts — list child accounts in the active AWS organization; rows matching a configured accounts.NAME get a :account NAME switch hint.
  • :sort KEY [desc] — set sort (name/app/status/health/version/age).
  • :group on|off — toggle group-by-application.
  • :redact on|off — toggle redact mode.
  • :events on|off — toggle events panel.
  • :filter NAME / :f NAME — load a saved filter.
  • :save NAME / :drop NAME / :filters — manage named filters.
  • :save-view NAME / :view NAME / :views / :view-drop NAME — saved views (filter + sort + grouping + scope).
  • :cols list|hide NAME|show NAME|reset — manage columns.
  • :pin — pin / unpin selected env.
  • :alias NAME LABEL / :alias-drop NAME — local env aliases.
  • :minimap on|off — corner mini-map of env health.
  • :loglevel LEVEL — live-reload the tracing filter.

Per-env inspection

  • :why — Red-env diagnostic overlay (recent events / alarms / instance health / recent deploys; main + DLQ peek for Worker envs). Bound to ! on the env table.
  • :diff NAME — side-by-side env comparison.
  • :resources / :resDescribeEnvironmentResources dump.
  • :alarms — CloudWatch alarms referencing the env.
  • :versions — application versions (deployed marker, total count, deploy hint).
  • :saved-configs / :configs — saved configuration templates (interactive: a apply, i inspect, x delete, c create).
  • :custom-platforms / :platforms — custom EB platforms.
  • :plugins — list user plugin commands.
  • :history — recent status / error log.
  • :pending / :in-flight — overlay of dispatched actions + outcomes.
  • :whatsnew — embedded changelog.
  • :about / :credits — version, license, attributions.
  • :update — show (and yank to clipboard) the upgrade command for whichever install channel (Homebrew / cargo-bin / tarball) ebman was installed from.
  • :settings — interactive form to edit ~/.config/ebman/config.toml; writes back on submit and live-applies theme / icons / refresh interval.

Write — env state

  • :rebuild / :restart / :terminate — action menu shortcuts (Terminate requires typed-name confirm).
  • :deploy LABEL — ship an existing application version to the selected env.
  • :deploy LABEL --preview — open a side-by-side overlay of the currently-deployed version vs the candidate (label, description, S3 source, timestamp + rollback / traffic warnings) without dispatching.
  • :deploy --from PATH [--label L] [--describe D] [--no-deploy] — upload a local .zip (or --from s3://bucket/key), register a new version, optionally deploy.
  • :upgrade [ARN] — list compatible platforms; with ARN, dispatch the migration.
  • :clone NEWNAME — clone the selected env.
  • :scale N / :stop / :start — set ASG min=max=N / 0 / 1.
  • :capacity — modal form to edit Min / Max / Instance type / Cooldown in one shot (pre-filled from DescribeConfigurationSettings).
  • :swap TARGET — swap CNAMEs (Y/N confirm).
  • :abort — abort an in-flight env update.

Write — env config

  • :env list | set KEY VAL | unset KEY — application env-var editor.
  • :tag KEY VALUE / :untag KEY — env tag editor.
  • :set-option NAMESPACE OPTION VALUE / :unset-option NAMESPACE OPTION — generic option-settings escape hatch.
  • :instance-type TYPE — EC2 instance type (e.g. t3.medium).
  • :keypair NAME / :service-role ARN / :instance-profile NAME — security tab.
  • :public-ip on|off / :elb-scheme public|internal — network tab.
  • :subnets / :elb-subnets / :security-groups — MultiSelect picker forms pre-filled with the env's current selection (lists available subnets / SGs from the VPC).
  • :deployment-policy AllAtOnce|Rolling|RollingWithAdditionalBatch|Immutable|TrafficSplitting — deploy policy.
  • :rolling-update on|off — ASG rolling-update policy.
  • :health-check-url /path — HTTP health-check path.
  • :logs-stream on|off [--retention DAYS] — toggle CW Logs streaming.
  • :logs-tail [LOG_GROUP] — open a live streaming overlay for a CW Logs group (auto-picks web.stdout.log).
  • :notify EMAIL_OR_SNS_ARN | off — notification endpoint.
  • :managed-window DAY HOUR | off — managed-platform-updates window.

Write — application versions / configs / alarms / platforms

  • :delete-version LABEL [--force] — delete an application version (with optional source-bundle removal).
  • :config-save NAME / :config-apply NAME / :config-delete APP NAME / :config-inspect NAME — saved-configuration templates.
  • :alarm-create NAME KIND THRESHOLD [OP] — CloudWatch alarm (KIND: health, 4xx, 5xx, latency).
  • :alarm-delete NAME — remove a CW alarm.
  • :custom-platform-delete ARN — delete a custom EB platform.
  • :metric add LABEL NAMESPACE NAME [STAT] [DIM=VAL,...] / :metric remove LABEL / :metric list — custom Metrics-tab charts.

Multi-account / multi-region

  • :region all — fan across extra_regions + current.
  • :account NAME — switch to a configured AssumeRole account (see Configuration).
  • :accounts — list AWS-Organizations child accounts; switch hints rendered for those with a configured accounts.NAME.
  • :find-env SUBSTRING — scan every profile in ~/.aws/{config,credentials} and every configured AssumeRole account in REGION.
  • :org-health — aggregate env / red counts per profile + per configured AssumeRole account.

Multi-env

  • space — toggle multi-select.
  • :batch-rebuild / :batch-restart — dispatch a non-destructive action across the selection.
  • :batch-deploy LABEL — deploy the same version to every selected env in parallel.
  • :batch-tag KEY VALUE / :batch-untag KEY — fan a tag write across the selection.
  • :batch-set-option NAMESPACE OPTION VALUE — fan an option-settings write across the selection.
  • :deselect / :select-clear — clear selection.

Output / yank

  • :export — yank filtered view as TSV.
  • :json — yank filtered view as JSON array.
  • :report / :markdown — yank filtered view as a Markdown table.

Read-only mode

  • :readonly on|off — toggle. --read-only on the CLI also locks every write surface.

Configuration

~/.config/ebman/config.toml:

# Refresh interval in seconds (default 15).
refresh_interval_secs = 15

# Extra regions to expose in the region picker, comma-separated.
extra_regions = ""

# Theme: "dark" (default), "light", or "high-contrast".
theme = "dark"

# Glyph set: "unicode" (default), "ascii" for low-feature terminals,
# "powerline" (alias "nerd") for Powerline-patched / Nerd Fonts, or
# "auto" to probe the terminal at startup and pick powerline if its
# support is detected (one-cell U+E0B0 advance), unicode otherwise.
icons = "unicode"

# Per-profile theme override — pin a theme per AWS profile so the screen
# itself says "you're in prod" without reading the breadcrumb. Format:
# "PROFILE:THEME,PROFILE:THEME". Theme names match the `theme = ...` key.
profile_themes = "prod:high-contrast,staging:dark"

# Start with these toggles on (state.toml takes precedence after first run).
redact_default = false
grouped_default = false

# Notification bell on increase in Red-env count.
notify_bell = false

# Tag policy — flag envs missing any of these tags in the Config tab.
required_tags = "Owner,Project"

# Webhook URL to POST to when an env transitions to Red.
webhook_url = ""

# AssumeRole targets reachable via `:account NAME`. One stanza per
# account. `source_profile` carries the base creds for the
# sts:AssumeRole call. `external_id` and `region` are optional.
# The temporary credentials build a fresh SdkConfig carrying only the
# assumed-role identity — source-profile creds never leak into request
# signing once the switch lands.
accounts.prod.role_arn = "arn:aws:iam::111122223333:role/EbmanReadOnly"
accounts.prod.source_profile = "default"
accounts.prod.region = "eu-west-2"
# accounts.prod.external_id = "..."

~/.config/ebman/keys.toml (optional) — custom keybindings:

# Aliases must be F1-F12 or an uppercase A-Z; map to a :command.
F1 = "refresh"
F2 = "region us-east-1"
Q = "history"

~/.config/ebman/commands.toml (optional) — user plugin commands. Each :NAME substitutes {name} / {cname} / {application} / {tier} / {region} / {profile} placeholders and yanks the rendered command to the clipboard.

[commands.tunnel]
template = "aws ssm start-session --target $(aws ec2 describe-instances --filters Name=tag:elasticbeanstalk:environment-name,Values={name} --query 'Reservations[].Instances[].InstanceId' --output text) --profile {profile}"
description = "Yank a tunnel command into clipboard"

~/.config/ebman/state.toml is managed by the app — filter / sort / cursor position / named filters / saved views / pinned envs / custom metrics live there.

Headless interface (--control-socket)

Launch ebman with --control-socket PATH to expose a Unix-socket interface. A second binary, ebman ctl <op>, is the one-shot client (defaults to ~/.cache/ebman/control.sock).

ebman ctl state                   # JSON: mode, profile, region, account, envs, selected, ...
ebman ctl screen                  # plain-text dump of the current frame
ebman ctl key Down                # synthesise a keypress
ebman ctl key Ctrl+R              # … or a combo
ebman ctl cmd ':region eu-west-2' # run a : command

Useful for integration tests, screenshot capture, scripted workflows.

What's stored locally

  • ~/.config/ebman/config.toml — user configuration (see above).
  • ~/.config/ebman/keys.toml — optional custom keybindings.
  • ~/.config/ebman/commands.toml — optional plugin commands.
  • ~/.config/ebman/state.toml — persisted UI state: profile, region, filter, sort, grouping, redact, selected env, named filters, saved views, pinned envs, aliases, hidden columns, custom metrics. No credentials.
  • ~/.cache/ebman/ebman.log — application log; rotates as needed. Set RUST_LOG=debug for verbose output.
  • ~/.cache/ebman/audit.log — every dispatched action and outcome (account, profile, region, action, target). Rotates at 1 MiB to audit.log.1.
  • ~/.cache/ebman/crash-*.log — panic backtraces (10 most recent kept; 30-day TTL).
  • Clipboard — y / Y / ^Y / ^W write via arboard.

Safety model

  • Read-only mode (--read-only or :readonly on) disables every write surface: action menu, DLQ resend / purge, all :-commands that mutate state. A green READ-ONLY pill in the header makes it visible.
  • Strict-typed confirm for irreversible actions: typing the env name is required to Terminate; typing the literal string to Purge.
  • Pre-flight checks in the confirm modal: DescribeInstancesHealth impact count, last 3 events, traffic warnings for env-in-deploy / recently-changed / currently-Red.
  • Audit log records dispatch + outcome of every action.

Distribution

  • Cargo: cargo install ebman from crates.io; cargo install --path . from a checkout.
  • GitHub Releases: tagging v<X.Y.Z> triggers .github/workflows/release.yml, which builds release binaries for x86_64-unknown-linux-gnu, aarch64-apple-darwin, and x86_64-apple-darwin and attaches tarballs + SHA-256 checksums to a draft release.
  • Homebrew: tap lives at tombaldwin/homebrew-tap. Per-release: bump the version + 3 platform SHAs in Formula/ebman.rb in both this repo (for brew install --formula PATH) and the tap.

Development

cargo build
cargo test
cargo fmt --all
cargo clippy --all-targets -- -D warnings

See BACKLOG.md for in-flight and planned work. See CLAUDE.md for the AI-assisted-contributor rules (the project has been developed heavily with Claude Code).

License

Dual-licensed under MIT or Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.