# Atuin: SQLite shell history with encrypted sync

> Atuin replaces the flat history file with a SQLite database that records exit codes, durations and directories, and optionally syncs an encrypted copy between machines. The trade-off is that history leaves your shell and becomes a service you have to run or trust.

**atuinsh/atuin** — Making your shell magical. Additionally, it provides optional and _fully encrypted_ synchronisation of your history between machines, via an Atuin server.

- Repository: https://github.com/atuinsh/atuin
- Website: https://atuin.sh
- Stars: 31,812 · Forks: 975
- Language: Rust
- License: MIT
- Published: 2026-08-04 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/atuinsh-atuin

## What Atuin changes about shell history

A shell history file is a list of strings. It does not know whether the command succeeded, how long it took, which directory you were in, or which terminal session produced it. Atuin replaces that file with a SQLite database and records those fields alongside the command text. The README states the project logs exit code, cwd, hostname, session and command duration.

That extra structure is the point. A plain history search can only match text. Atuin can filter on the metadata it stored, which is why the README's own example looks like this:

```bash
atuin search --exit 0 --after "yesterday 3pm" make
```

The example returns successful make invocations recorded after 3pm the previous day. That query is not expressible against a flat history file, and it is the kind of thing you reach for when a build worked yesterday and does not today.

The audience is people who live in a terminal across more than one machine or more than one long-lived session. If you open one shell, run five commands and close it, the database buys you nothing.

## The SQLite database, the hooks, and the sync path

The repository is a Rust workspace. Cargo.toml lists members including atuin-client, atuin-common, atuin-daemon, atuin-history, atuin-server and atuin-pty-proxy, all versioned together at 18.22.0 in the workspace manifest. That split matters when you read the project: the client you install and the server you might host are separate crates, and the Dockerfile builds only atuin-server with cargo build --release --bin atuin-server.

On the client side, Atuin works by rebinding keys and installing shell hooks. The README lists rebinding ctrl-r and up to a full screen search UI, with ctrl-r cycling filter modes: current session, current directory, or global. Enter executes a command from the list, tab edits it instead. Alt-<num> quick-jumps to a previous item.

Sync is a separate, optional path. The README describes it as optional and fully encrypted history synchronisation via an Atuin server, and says you may use the hosted server, host your own, or skip sync entirely. The claim in the README is that because sync is encrypted, the author could not read your data. That is a design claim about the protocol, not something you can verify from the README alone; the encryption details live in the crates and the docs site, not in the top-level readme.

One detail worth noting: the README says the old history file is not replaced. Atuin imports from it and keeps it around.

## Installing Atuin and running a first filtered search

The README's quickstart installs the binary with a shell script and then signs you up for the hosted sync service. The script is fetched over HTTPS with a pinned TLS version:

```bash
curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh
```

After that, the README walks through registration and import. The import step pulls in your existing history automatically:

```bash
atuin register -u <USERNAME> -e <EMAIL>
atuin import auto
atuin sync
```

Replace the placeholders with your own username and email. The README then says to restart your shell. If you do not want the hosted service, the README points at the docs for an offline setup and a self-hosted server, and says you can skip sync entirely. The install page is at docs.atuin.sh, which is where the platform-specific instructions live rather than in the repository readme.

Once the shell has restarted, the ctrl-r binding opens the search UI. To exercise the metadata rather than the text matching, run the search example from the README and confirm you get results:

```bash
atuin search --exit 0 --after "yesterday 3pm" make
```

If that returns nothing, it is most likely because the command has not been run since the import, not because the search is broken.

Bash users get a specific caveat in the README. The quickstart sets up bash-preexec for the necessary hooks, and the README notes that bash-preexec has limitations and links to the shell plugin documentation for details. That is the one place the readme flags a platform as second-class, and it is worth reading before you commit on a bash-only machine.

## Where Atuin is the wrong tool

Atuin inserts itself between you and every command you run. That is the cost of the metadata. If the hooks misbehave, or if a shell integration is incomplete, the failure is not confined to the search UI; it is in the path of your normal typing. The README's own bash-preexec note is an admission that this path is not uniform across shells.

The sync server is the second boundary. The README offers three modes: hosted, self-hosted, or none. Choosing none means you get the local database and the search UI but lose the cross-machine story, which is half the pitch. Choosing hosted means trusting an operator with ciphertext and with the metadata that surrounds it. Choosing self-hosted means you now run a service. The Dockerfile shows what that entails: a multi-stage build, a non-root atuin user, a /config directory owned by that user, ATUIN_CONFIG_DIR set to /config, a healthcheck hitting /healthz on port 8888 by default, and RUST_LOG defaulting to atuin_server=info. That is a real deployment, not a one-liner.

There is also a scope question. If your problem is fuzzy-finding in a single terminal, fzf-style matching answers it without a database, a daemon, or a server. Atuin's value appears when history is fragmented across machines and sessions, and disappears when it is not.

## Atuin against fzf-style history search

The comparison people reach for is fzf. The difference in approach is where the state lives. An fzf-style binding reads your existing history file and pipes it into a fuzzy matcher. Nothing is written, nothing is migrated, and the only new dependency is the matcher itself. Atuin writes commands into SQLite as they run, and the search UI queries that database.

That difference shows up in what you can ask. A fuzzy matcher ranks strings by similarity; it has no notion of exit status or duration because the history file never recorded them. Atuin's search takes --exit and --after because those columns exist. A fuzzy matcher also cannot tell you that the same command ran on a different host, because there is no host column to read.

The cost runs the other way too. fzf-style search has no sync story, no server to operate, and no schema to migrate when the tool updates. Atuin's database is a schema, and schemas change; the workspace version in Cargo.toml is 18.22.0 while the most recent release listed is 18.20.1, which tells you the client and server move together and that upgrades are a routine event rather than a one-time install.

## Maintenance, releases and the MIT licence

The repository is not archived, and the last push was on 2026-08-27. The most recent release in the list is v18.20.1, dated the same day, with v18.20.0 two days earlier. That is a project that ships often, and the workspace version in Cargo.toml sits ahead of the released tag, which is normal for a main branch between releases.

Frequent releases are not free for the user. Atuin is a binary that hooks your shell, and it has a client and a server that need to agree. A self-hosted deployment has to track client releases closely enough that the protocol still matches. The Dockerfile pins the Rust toolchain from rust-toolchain.toml rather than using the image's, which is the kind of detail that keeps a build reproducible but also means a toolchain bump is a deliberate change.

The licence is MIT, stated in Cargo.toml and in the LICENSE file, and the Dockerfile labels the image with org.opencontainers.image.licenses="MIT". MIT is permissive: you can use, modify and redistribute the code, including in closed products, provided the copyright notice and permission notice are preserved. That is a description of the licence text, not legal advice; if you are redistributing Atuin inside a product, read the LICENSE file and get your own counsel.

One practical note on the hosted service: the README's quickstart signs you up for the Atuin Cloud sync server, and the licence on the client code says nothing about the terms of that service. Those are separate questions.

## Conclusion

Adopt Atuin if you work across several machines or sessions and want history that carries exit codes, durations and directories, and if you are comfortable either running the sync server yourself or trusting the hosted one with ciphertext. Do not adopt it if you only need a better ctrl-r in one terminal, or if you cannot accept that the hooks it installs sit in the path of every command you type. Before committing, run atuin import auto and check that your old history file is still on disk, then decide between the cloud signup and a self-hosted server. Verify the sync server's TLS configuration and the age of the last push before you point it at history you would not want public.

## FAQ

### What is Atuin?

Atuin replaces your existing shell history with a SQLite database and records extra context for each command, such as exit code, cwd, hostname, session and duration. It also offers optional, fully encrypted synchronisation of history between machines through an Atuin server.

### Is Atuin safe?

The README states that all history sync is encrypted and that the author could not access your data even if he wanted to, and that you can host your own server or skip sync entirely. The top-level README does not document the encryption scheme itself, so the details are in the crates and the docs site rather than there.

### How do I install Atuin?

The README's quickstart fetches an install script over HTTPS with curl and pipes it to sh, then runs atuin register with a username and email, atuin import auto, and atuin sync, followed by a shell restart. The docs site covers offline setups and self-hosted servers.

### What is Atuin written in?

Atuin is written in Rust. The repository is a Cargo workspace with members including atuin-client, atuin-common, atuin-daemon, atuin-history and atuin-server, and the workspace manifest sets rust-version to 1.95.0.

### Is Atuin free?

The source is released under the MIT licence, so the code is free to use, modify and redistribute under that licence. The README's quickstart signs you up for the hosted Atuin Cloud sync server, and the repository does not state the terms of that service.

### How do I set up Atuin sync?

The README gives atuin register with a username and email, then atuin sync, against the hosted server, and says you may instead host your own server or not use sync at all. The Dockerfile builds only atuin-server and runs it as a non-root atuin user with ATUIN_CONFIG_DIR set to /config and a healthcheck on /healthz.

## Sources

- [Official documentation](https://atuin.sh)
- [Official README](https://github.com/atuinsh/atuin#readme)
- [Project repository](https://github.com/atuinsh/atuin)
- [Release notes](https://github.com/atuinsh/atuin/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/atuinsh-atuin
