top shows what is running. bottom shows what ran.
Bottom is a read-only process lifecycle flight recorder for transient commands, restart loops, and process ancestry. It records start, exec, stop, churn, and capture-gap events to the terminal, JSONL, CSV, or an interactive timeline.
This is the lifecycle recorder at donomii/bottom, not the current-state system monitor at ClementTsang/bottom. Release archives use the bottom-events package name while retaining the bottom command.
Tagged releases publish checksummed Linux, macOS, and Windows binaries for amd64 and arm64 at https://github.com/donomii/bottom/releases.
Install with Homebrew:
brew install donomii/tap/bottom-eventsInstall with Scoop:
scoop bucket add donomii https://github.com/donomii/scoop-bucket
scoop install bottom-events
Install from source with Go 1.25 or newer:
go install github.com/donomii/bottom@latestThe executable is installed in GOBIN, or in the bin directory under the path reported by go env GOPATH when GOBIN is unset. Add that directory to PATH to invoke bottom by name.
From a checkout:
./run.shrun.sh accepts the same options and commands as bottom.
Print process history to the terminal:
bottomWatch interactively while recording to JSONL:
bottom -tui -format jsonl -output bottom.jsonlFind short-lived processes that exited with code 1 during the last 15 minutes:
bottom query -input bottom.jsonl -events stop -since 15m -max-duration 5s -exit-code 1Trace only a command and its descendants, then create a Perfetto timeline:
bottom trace -output build.jsonl -perfetto build-trace.json -- make testSummarize, replay, or compare recordings:
bottom report -input bottom.jsonl
bottom replay -input bottom.jsonl -tui -speed 4
bottom compare -before previous.jsonl -after current.jsonl- Best available process identity, PID, parent PID, executable, command, owner, first observation, and OS start time; PID-only fallbacks are identified in the platform table.
- Parent ancestry captured before short-lived parents disappear.
- Linux UID, TTY, session, cgroup, systemd unit, and container identity when visible.
- Exit status when supplied by the backend.
- Structured capture gaps and backend transitions so detected losses are explicit.
- Versioned JSONL and CSV metadata for session, host, boot, sequence, schema, source timestamp, and observation timestamp.
- Semantic restart churn grouped by executable, parent, owner, service, and container rather than volatile arguments.
Text output uses full timestamps and one event per line:
2026-07-09T12:00:01.123-07:00 start session=... seq=1 backend=poll process=421:... pid=421 ppid=22 uid=1000 user=jer exe="/usr/bin/compiler" cwd="/work" unit="" container="" cmd="compiler --input main.go" parent=22:shell
2026-07-09T12:00:01.206-07:00 stop session=... seq=2 backend=poll process=421:... pid=421 ppid=22 duration=83ms exit=0 uid=1000 user=jer exe="/usr/bin/compiler" unit="" container="" cmd="compiler --input main.go"
2026-07-09T12:00:03.000-07:00 churn session=... seq=9 backend=poll count=5 window=10s exe="/usr/bin/compiler" unit="builder.service" container="" cmd="compiler --input main.go"
Continuously record process lifecycle events. bottom watch is an alias.
Capture and output:
-backend autochooses the native event source for the current platform and otherwise uses native snapshot polling. Explicit values areauto,poll,linux-proc-connector,windows-etw, andmacos-endpoint-security.-poll 100mssets the polling fallback interval.-format textselectstext,jsonl, orcsvoutput.-output PATHappends to an owner-only output file; empty writes to stdout.-tuishows the interactive timeline. When-outputis set, the same events are recorded simultaneously.-recorder-buffer 1024bounds queued writes; a full queue stops with an accurate backpressure error rather than losing events silently.-rotate-size 0rotates text, JSONL, or CSV after this many bytes; zero disables size rotation.-rotate-interval 0rotates text, JSONL, or CSV after this duration; zero disables time rotation.-redact TEXTreplaces exact matching text with[REDACTED]before any output; repeat it for multiple values. The default performs no rewriting.
Filtering:
-events allkeepsstart,exec,stop,churn,gap,all, or the backward-compatiblebothalias.-include TEXTkeeps events whose searchable fields contain the text; repeat values are ORed.-exclude TEXTremoves matching events; repeat values are ORed.-include-regex EXPRESSIONand-exclude-regex EXPRESSIONapply case-sensitive regular expressions to the original-case searchable fields.-user USERmatches a user name or numeric UID.-ppid PIDmatches the immediate parent.-ancestor-pid PIDmatches any captured ancestor.-cwd TEXT,-exe TEXT,-container TEXT, and-unit TEXTfilter their corresponding fields.-min-duration 0and-max-duration 0limit stop events by lifetime; zero leaves that bound disabled.
Capture-gap records always reach live outputs even when an ordinary process or event-kind filter would reject them, so filtering cannot make coverage appear complete.
Restart-loop detection:
-churn-window 10ssets the sliding restart window.-churn-threshold 5sets the number of short lifetimes needed for a report.-churn-cooldown 10ssets the minimum interval between sustained reports for the same group.-churn-max-keys 4096bounds retained process groups and evicts the oldest group when full.-churn-max-life 5ssets the longest lifetime considered a restart; zero counts starts without waiting for stops.
Triggered recording:
-ring-buffer 0retains this many pre-trigger events instead of writing immediately; zero disables triggered recording.-trigger churnselectschurn,gap,failed-exit, orregex:EXPRESSION.-post-trigger 10srecords this much activity after a trigger. Later triggers extend the window.
Trigger decisions see the unfiltered event stream. Only events accepted by the output filters, plus mandatory capture gaps, reach the recording.
Utilities:
-hprints help for recording, trace, query, replay, report, or compare and exits successfully;bottom trace -hdoes not require a--command boundary.-versionprints the build version, source commit, and build date, then exits.-testruns deterministic built-in checks and exits.
Tagged archives inject all version fields. Source-installed and checkout builds fall back to the Go module version and VCS revision when available; their build date remains unknown unless injected by the builder.
Runs the command after the required --, records only that process and discovered descendants, and never acts on unrelated processes.
-format jsonland-output bottom-trace.jsonlselect the recording by default.-poll 10mssets descendant discovery frequency.-tail 2slimits observation of surviving descendants after the root command exits; Bottom records a gap if the tail ends while descendants remain.-perfetto PATHwrites a new owner-only Perfetto-compatible JSON timeline. Empty disables export and its in-memory event retention. The recording and Perfetto paths must resolve to different files.-redactand-recorder-bufferbehave as in recording mode.
Trace rejects -tui because the traced command shares the terminal. Record to a file and use bottom replay -tui afterward. Once the command starts, Bottom waits for its natural exit even if recording fails; it does not alter the command or surviving descendants.
These commands stream -input bottom.jsonl without changing it. An output path that resolves to the input file is rejected.
-sinceand-untilaccept RFC3339 timestamps or durations before now such as15m.-exit-code CODEmatches an exact exit code, including zero.- The recording filters listed above are available for event kind, text, regex, owner, ancestry, context, and lifetime.
-limit 0bounds matching events when non-zero.- Query supports
-format text,jsonl, orcsvand optional-output. - Replay uses
-speed 1,-max-delay 1s, and optional-tui. A zero maximum delay preserves the full recorded timing. - Report shows event totals, coverage gaps, failures, top executables, parent fan-out, ancestry edges, and shortest lifetimes.
-before before.jsonl and -after after.jsonl are opened read-only and compare process fingerprints, ancestry, counts, failures, and average lifetimes. -output appends the report to an owner-only file; empty writes to stdout and paths resolving to either input are rejected.
TUI navigation keys act immediately:
ppauses or resumes rendering while collection continues.kandjmove toward older and newer events./edits a search for event kind, process identity, command, executable, working directory, owner, context, PID, message, and captured ancestry fields. Return applies it, Escape cancels it, Backspace deletes, and Ctrl-U clears the draft.xclears the active search.dtoggles details for the selected event.ccycles command, context, and executable column layouts.scycles timeline, duration, PID, and command ordering.- Ctrl-C or Ctrl-D stops Bottom itself without acting on any monitored process.
?toggles control help.
The display adapts its rows and command width to terminal resizing. If raw terminal input is unavailable, the same controls remain available as line commands followed by Return; use columns and sort for those two cycles.
The status line reports the active backend, capture gaps, pause state, search, scroll position, columns, and ordering.
Process-supplied terminal control bytes are displayed as visible hexadecimal escapes rather than executed by the terminal.
| Platform | Default source | Stable identity | Command line | Owner | Native event stream |
|---|---|---|---|---|---|
| Linux | Process connector with direct /proc fallback |
Yes | Yes | UID and resolved name | Yes when the connector is available |
| macOS | Endpoint Security with native sysctl fallback |
Yes | Yes when visible | UID and resolved name | Yes when the signed binary has Apple's entitlement, Full Disk Access, and required privilege |
| Windows | ETW with native Tool Help fallback | Yes when creation time is readable; PID fallback otherwise | Yes when visible; executable fallback | SID and resolved account name | Yes when ETW session creation is permitted |
| Other Unix | ps snapshot fallback |
PID only | Yes | Yes | No |
Native event modes record direct lifecycle events, exact exit status when supplied, detected losses, and periodic snapshot reconciliation. If native setup fails in auto mode, Bottom writes a structured backend-transition gap and continues with platform-native polling. The macOS requirements and signing procedure are documented in docs/endpoint-security.md.
Snapshot backends can miss a process that starts and exits entirely between snapshots. After capture starts, they emit structured gaps when a snapshot itself fails, but that cannot prove complete coverage between successful snapshots. An initial snapshot failure stops before capture. Use the Linux connector when complete short-process capture matters.
JSONL and CSV contain versioned session start/end records plus event and gap records. JSONL is the canonical format for query, replay, report, and comparison workflows.
New output files use mode 0600 on Unix. Existing destination permissions are preserved. Command arguments can contain credentials; use repeated -redact values when exact sensitive text must not be stored. Structural fields such as event kind, backend, and session identifiers are not rewritten because doing so would break queries and coverage classification.
| Tool category | Primary question |
|---|---|
top, htop, and current-state dashboards |
What is running now and consuming resources? |
| pspy | What transient commands appeared on Linux? |
| forkstat | What fork, exec, and exit activity occurred on Linux? |
| Bottom | What ran over time, who spawned it, how long did it live, where are the gaps, and how do two recordings differ? |
Bottom deliberately does not duplicate CPU, RAM, disk, or network dashboards.
Read-only means Bottom does not terminate, suspend, inject into, or otherwise alter monitored processes. Recording modes can write the output files requested by the user, and trace mode starts only the command explicitly supplied after --.
./build.sh
./test.sh
./benchmark.sh
./demo.sh
./install.shbuild.shcreates thebottombinary.test.shruns the Go tests and built-in checks.benchmark.shmeasures burst snapshot diffing and bounded high-cardinality churn handling.demo.shtraces a finite self-test, writes JSONL and Perfetto recordings, and prints an ancestry report plus the artifact paths.install.shinstalls the command withgo install.
GitHub Actions tests Linux, macOS, and Windows, runs natural-exit lifecycle smoke checks, the Go race detector, and a portable release/SBOM snapshot. Tags matching v* run the release workflow and produce bottom-events archives with checksums, SPDX JSON SBOMs, and GitHub provenance attestations.
See CONTRIBUTING.md, SPEC.md, CHANGELOG.md, SECURITY.md, and docs/repository-settings.md.
