Skip to content
sloop

Reference

Security

The guarantee, expanded — and every part of it written so you can check it yourself rather than take it on trust.

Your credentials never leave your machine. No telemetry, no analytics, no update pings. cargo tree on this repository shows no HTTP client in the dependency graph — there is nothing in the binary that could phone home.

That is a claim about a dependency graph, which makes it a claim anybody can check. So the first section is how, and it takes about thirty seconds.

Check it yourself

Copy this. It clones the repository and greps the whole dependency graph:

thirty seconds
$ git clone https://github.com/DBSloop/sloop && cd sloop/cli$ cargo tree --edges normal,build,dev --target all --all-features \$ --prefix none | awk 'NF{print $1}' | sort -u \$ | grep -Ei 'reqwest|hyper|ureq|isahc|curl|ssh2|russh|thrussh'# nothing. That is the whole result.

It prints nothing, and nothing is the result. 269 crates go into that binary and not one of them can open a socket — --edges normal,build,dev so a build-dependency cannot hide there, and --target all because something behind [target.'cfg(windows)'.dependencies] is invisible to a plain cargo tree run on Linux, which is precisely where it would hide.

The same check is a script in the repository, and it runs on every push. It fails the build rather than printing a warning:

on every push
$ sh ci/no-http-client.shno-http-client: 269 crates in the graph, not one of them opens a socket.

SSH clients are denied too, and that is not an afterthought: ssh2, libssh2-sys, russh, thrussh, async-ssh2-*, ssh-rs and makiko fail the build beside reqwest and hyper. An embedded SSH client can open a socket to anywhere, so the promise above would stop being true while a check that only looked for HTTP clients still passed. A claim that holds only because the check is narrow is not worth making — which is why sloop runs your own ssh instead.

A registry holds a route, never a value

There is no spelling of a password a registry will accept. It stores which of four routes to take, and the value is fetched at the moment it is needed — which is what makes it impossible to put a password there by accident.

--keyring
Credential Manager, the Keychain, or the Secret Service.
The default, and right unless one of the other three applies.
--encrypted-file
A file encrypted with XChaCha20-Poly1305, its key derived with Argon2id.
A headless Linux box with no keyring running.
--env <VARIABLE>
An environment variable, read at the moment it is needed.
CI, and automation generally.
--password-from <COMMAND>
Whatever that command prints. Read through a pipe, never an argument.
A team password manager.

sloop db list shows the route beside every database and cannot show a password, because there is not one there to show. Databases and registries has that listing.

Nothing in argv, so nothing in ps

A password reaches a child process through a pipe, or through that one child's environment — never as a command-line argument. Anybody running ps on a shared machine while a backup is going sees the command and not the credential.

Which leaves one hole sloop cannot close: a password you typed into a URL was already in your shell's history and in ps before sloop ever ran. It says so rather than quietly accepting it, and then keeps its own half of the bargain:

named, not hidden
$ sloop db add leaky --url 'postgres://app:hunter2@db.internal:5432/orders' \$ --log-file run.log▲ The password came from the URL it was visible in `ps` and is in your shell history while thatcommand line lives. sloop has filed it and will not write it anywhere —--password-stdin avoids the exposure next timeregistered leaky in the project registry, password from the OS keyring postgres://app@db.internal:5432/orders $ cat run.log▲ The password came from the URL it was visible in `ps` and is in your shell history while thatcommand line lives. sloop has filed it and will not write it anywhere —--password-stdin avoids the exposure next timeregistered leaky in the project registry, password from the OS keyring postgres://app@db.internal:5432/orders ← no password, anywhere

A log you could paste into a public issue

--log-file writes what the run printed, without colour and with anything that reads like a credential removed. That holds for two reasons, and only one of them is a filter.

The structural half: the one line that carries a real password goes through a function that has no logging in it at all. sloop does not redact its own passwords out of its logs — it never writes them.

The net under it is for credentials that arrive inside something else: a URL with a password in it, a key=value pair whose key says what it is, or a word after something that announces a secret. Like a token inside a --password-command you wrote, which sloop cannot see coming:

terminal, then log
# the terminal shows the command the user typed$ sloop db add vaulted --url … --log-file run2.log \$ --password-from "curl -H 'Authorization: Bearer sk-live-abc123' https://vault/pw"note: … password from the command `curl -H 'Authorization: Bearer sk-live-abc123' https://vault/pw` # the log does not$ cat run2.lognote: … password from the command `curl -H 'Authorization: Bearer *** https://vault/pw`

A redactor is a net, not a design

Note the terminal above shows the token in full — it is the command you typed, and hiding it from you would be theatre. It is the log that is scrubbed, because the log is the thing that ends up in an issue. And what keeps sloop's own passwords out of logs is not this filter; it is that they are never printed.

Config is parsed, never evaluated

Real passwords contain $, backticks, >, #, quotes, pipes, backslashes and spaces. Nothing sloop reads is ever sourced or eval'd, so none of that is interpreted. Here is a PostgreSQL role whose password is nothing but metacharacters:

and it just works
# the role's real password on the server:# p@ss$HOME `whoami` >out #1 "q" |pipe \slash$ sloop db add awkward --url postgres://app@127.0.0.1:5449/orders \$ --password-stdin --test # piped in, never an argument✓ postgres 17.9 not encryptedregistered awkward in the project registry, password from the OS keyring # and a later run, reading it back out of the keyring:$ sloop db test awkwardawkward postgres://app@127.0.0.1:5449/orders project✓ postgres 17.9 not encrypted ← it connected

Registered through a pipe, stored in the keyring, and read back on a later run to open a real connection. $HOME did not expand, the backticks did not execute, the > did not redirect and the # did not start a comment.

A \r is the one character sloop removes — Windows editors add them, and the authentication failure that follows is invisible.

What this is not

None of it is a substitute for the database's own access control. sloop runs as you, with the credentials you gave it, and can do exactly what those credentials allow — the privileges a backup role needs is the page for keeping that set small. And an encrypted backup is only as safe as the key: lose it and the backups are unreadable, leave it beside them and they are not protected.

Securing the machine and the network is still the operator's job. What sloop guarantees is narrower and checkable: it cannot send your credentials anywhere, because it cannot send anything anywhere.

sloop — a database operations CLI for PostgreSQL, MySQL and MariaDB. MIT or Apache-2.0.