mirror of
https://github.com/atuinsh/atuin.git
synced 2026-10-11 22:29:51 +00:00
5.8 KiB
5.8 KiB
Atuin
Shell history tool. Replaces your shell's built-in history with a SQLite database, adds context (cwd, exit code, duration, hostname), and optionally syncs across machines with end-to-end encryption.
Workspace crates
atuin CLI binary + TUI (clap, ratatui, crossterm)
atuin-client Client library: local DB, encryption, sync, settings, client-facing domain types
atuin-common Low-level cross-crate utilities and API models (not a home for client-facing domain types)
atuin-daemon Background gRPC daemon (tonic) for shell hooks
atuin-dotfiles Legacy read-only alias/var listing via record store
atuin-history Sorting algorithms, stats
atuin-kv Key-value store (synced)
atuin-scripts Script management (minijinja)
atuin-server HTTP sync server (axum) - lib + standalone binary
atuin-server-database Database trait for server
atuin-server-postgres Postgres implementation (sqlx)
atuin-server-sqlite SQLite implementation (sqlx)
Two sync protocols
- V1 (legacy): Syncs history entries directly. Being phased out. Toggleable via
sync_v1_enabled. - V2 (current): Record store abstraction. All data types (history, KV, aliases, vars, scripts) share the same sync infrastructure using tagged records. Envelope-encrypted with PASETO V4 and per-record CEKs.
Encryption
- V1: XSalsa20Poly1305 (secretbox). Key at
~/.local/share/atuin/key. - V2: PASETO V4 Local (XChaCha20-Poly1305 + Blake2b). Envelope encryption: each record gets a random CEK wrapped with the master key. Record metadata (id, idx, version, tag, host) is authenticated as implicit assertions.
Databases
- Client: SQLite everywhere. Separate DBs for history, record store, KV, scripts. All use sqlx + WAL mode.
- Server: Postgres (primary) or SQLite. Auto-detected from URI prefix.
- Migrations live alongside each crate. Never modify existing migrations, only add new ones.
- Exception: the AI session sidecar's migrations (
atuin-client/src/ai_session/migrations) were squashed into one0001_init.sqlbefore release, and the sidecar file was renamed (ai_harness_sessions.db→ai_session_sidecar.db) so no old sidecar meets the new history. Don't add back compatibility with the old migrations; from here on, add new ones as usual.
- Exception: the AI session sidecar's migrations (
Hot paths
history start, history end, and init skip database initialization for latency. Don't add DB calls to these without good reason.
Conventions
- Crate placement: anything client-facing -- domain types users' code touches and the stores over
them -- lives in
atuin-client.atuin-commonis for low-level, cross-crate utilities only; do not put client-facing domain types there. - Rust 2024 edition, toolchain 1.98.0.
- Errors:
eyre::Resultin binaries,thiserrorfor typed errors in libraries. - Derive boilerplate:
derive_more(workspace dep) forDisplay,From,Into,AsRef,Deref,Debugon newtypes and simple enums. Preferderive_moreover manualimplwhen the formatting/conversion is a straight delegation. Usethiserror(notderive_more) for error types. Use#[as_ref(str)]on string newtypes forAsRef<str>. - Async: tokio. Client uses
current_thread; server usesmulti_thread. #![deny(unsafe_code)]on client/common,#![forbid(unsafe_code)]on server.- Clippy:
pedantic+nurseryon main crate. CI enforces-D warnings, on both the default targets and--tests. - Rustdoc: CI runs
cargo doc --document-private-items --no-deps --workspacewithRUSTDOCFLAGS=-D warnings. Broken intra-doc links fail the build. - Format:
cargo +nightly fmt..rustfmt.tomluses nightly-only options, so formatting requires the nightly toolchain even though the project builds on stable 1.97.0. - IDs: UUIDv7 (time-ordered), newtype wrappers (
HistoryId,RecordId,HostId). - Serialization: MessagePack for encrypted payloads, JSON for API, TOML for config.
- Storage traits:
Database(client),Store(record store),Database(server) -- allasync_trait. - History builders:
HistoryImported,HistoryCaptured,HistoryFromDbwith compile-time field validation. - Feature flags:
client,sync,daemon,clipboard,check-update.
Testing
- Unit tests inline with
#[cfg(test)]. Userstestfor every test —#[rstest], never a bare#[test](async:#[rstest]+#[tokio::test]); migrate plain#[test]s in files you touch. - Lean on
#[fixture]s for shared setup and compose them; when a test needs teardown, return an RAII guard from the fixture (e.g. a temp dir removed onDrop) rather than cleaning up by hand. - Parametrize with
#[case(...)](input/expected tables) and#[values(...)](cross-products of independent parameters) instead of near-duplicate tests. - Reach for
proptestwhen a property holds across many inputs — round-trips (encode/decode, serde, parse/display), invariants, idempotence; keep targeted#[case]s for known edge cases and regressions. - Server integration tests in
crates/atuin-server/tests/need Postgres (ATUIN_DB_URIenv var). - E2e tests use temporary homes and private daemon sockets. Add shell setups in
crates/atuin/tests/shells/*.toml. Seecrates/atuin/tests/README.mdfor dependencies and how to run them. - Use
rstestfor tests, especially when they can be made simpler usingcases andfixtures. - Use
":memory:"SQLite for unit tests needing a database. - Runner:
cargo nextest. - Benchmarks:
divaninatuin-clientandatuin-history, tracked in CI by CodSpeed. Run them locally withcargo codspeed build && cargo codspeed run, or with plaincargo bench.
Build and check
cargo build
cargo test
cargo clippy -- -D warnings
cargo clippy --tests -- -D warnings
cargo +nightly fmt --check
RUSTDOCFLAGS="-D warnings" cargo doc --document-private-items --no-deps --workspace