Install locksql
Every release ships a static binary for Linux and macOS (amd64, arm64), Linux packages, a checksums.txt and its cosign signature. No Go toolchain is needed.
locksql is a pre-release (v0.8.x). It supports Linux and macOS, on amd64 and arm64. On Windows it runs inside WSL2 (see below); native Windows is not supported. A Docker image runs it with nothing else installed.
Install script (Linux, macOS)
Downloads the archive for your OS and architecture, checks it against checksums.txt (and the signature when cosign is on PATH), then installs /usr/local/bin/locksql, with sudo if needed:
curl -fsSLO https://raw.githubusercontent.com/DavidGodefroid/locksql/main/scripts/install.sh
less install.sh # read it first
sh install.sh # LOCKSQL_VERSION=v0.8.0 to pin a version
Or, in one line:
curl -fsSL https://raw.githubusercontent.com/DavidGodefroid/locksql/main/scripts/install.sh | sh
LOCKSQL_INSTALL_DIR=~/.local/bin installs without sudo, but the binary is then owned by your account and locksql doctor warns: in separated mode, the console must run a binary the agent cannot replace (sudo locksql install copies it to /usr/local/bin). To upgrade, run locksql update from your own account: it checks the cosign signature and the checksum of the new release, and asks sudo only for the final copy. locksql console and locksql doctor tell you when a newer release exists (at most one check a day; LOCKSQL_NO_UPDATE_CHECK=1 turns it off).
Debian, Ubuntu, Fedora, RHEL, Alpine
Packages install /usr/bin/locksql, owned by root. Copy-paste, the latest version and your architecture are detected:
V=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/DavidGodefroid/locksql/releases/latest); V=${V##*/v}
A=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
U=https://github.com/DavidGodefroid/locksql/releases/download/v$V/locksql_${V}_linux_$A
# Debian, Ubuntu
curl -fsSLO $U.deb && sudo apt install ./locksql_${V}_linux_$A.deb
# Fedora, RHEL
sudo dnf install $U.rpm
# Alpine
curl -fsSLO $U.apk && sudo apk add --allow-untrusted ./locksql_${V}_linux_$A.apk
To upgrade, run the same lines again; to remove, sudo apt remove locksql (or dnf remove, apk del).
By hand, with signature check
Download locksql_<version>_<os>_<arch>.tar.gz, checksums.txt and checksums.txt.sigstore.json from the releases page, then:
cosign verify-blob --bundle checksums.txt.sigstore.json \
--certificate-identity-regexp '^https://github.com/DavidGodefroid/locksql/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checksums.txt
sha256sum --ignore-missing -c checksums.txt # macOS: shasum -a 256 --ignore-missing -c checksums.txt
tar -xzf locksql_<version>_<os>_<arch>.tar.gz locksql
sudo install -m 0755 locksql /usr/local/bin/locksql
From source
go install github.com/DavidGodefroid/locksql/cmd/locksql@latest (Go 1.26 or later). To build from a checkout:
CGO_ENABLED=0 go build ./cmd/locksql
Requires Go 1.26 or later. See CONTRIBUTING.md for building and testing.
After installing: set up
One command: locksql setup
From your account (the one your agents run as), in the project:
locksql setup [--project DIR] [--client USER] [--trust-binary] [--yes]
It wires every installed agent for your account, creates the locksql console account with sudo once you confirm, asks for the database profile (engine, host, user, the TLS mode of a remote host, and where the password comes from: typed at console start, the console account’s keychain or HashiCorp Vault; never the password itself) and writes it to the project’s .locksql/config.toml, gives the console account access to the project, and prints the command that starts the console. Run again, it changes nothing already in place.
Log out and in again after the first setup: your account joins the locksql-clients group. On macOS, setup prints the root commands for an administrator to run, then you run setup again. Without a terminal (CI, scripts), setup needs --yes and the profile as flags:
locksql setup --yes --engine sqlite --path demo.db
locksql setup --yes --profile uat --engine postgres --host db.internal --user reporting
locksql setup --yes --profile prod --engine postgres --host db.internal --credentials vault --vault-path database/creds/ro
locksql profile add, with the same profile flags or its questions, adds another profile to the project without the other steps.
The steps below are what setup does, one by one. locksql setup in the usage guide.
Wire your agents
Setup, and bare locksql in a terminal, wire every installed agent (Claude Code, Codex, Gemini CLI, Cursor) for your user account, so that they work from any directory. Later runs of bare locksql only wire agents installed since. Run in the service account, bare locksql starts the console.
What is written for each agent, and how to undo it: Agent wiring in the usage guide.
Separate the console from the agent
The console runs only in an account of its own: an agent in the console’s account could read its terminal, type into it and read its keychain session. locksql console refuses to start without this setup. On Linux and macOS, locksql install sets it up once:
locksql install [--client USER] [--user locksql] [--group locksql-clients] [--print] [--trust-binary] [--yes]
It prints the root script and, on Linux, runs it with sudo after you confirm (--yes runs it without asking, and without a terminal; --print only prints; on macOS it always only prints). It creates the locksql account, the locksql-clients group, a root-owned /usr/local/bin/locksql and /etc/locksql/system.toml, and the socket directory.
Then set a password for the console account, log out and in again (the group applies at login), open a separate login session as locksql (switch user, preferably Wayland) and start the console there with --project pointing at the agent’s project directory.
The console writes .locksql/pii.toml by replacing it, so its account needs the .locksql/ directory itself: sudo chown locksql <project>/.locksql, mode 0755. On Linux the socket directory /run/locksql is mode 2710 (setgid); an install made before v0.1.3 leaves it 0710 and the console stops: run sudo locksql install again. locksql doctor checks both.
Describe your databases
Profiles live in .locksql/config.toml at the project root (found by walking up from the current directory) and in <user config dir>/locksql/config.toml. A project profile wins over a user profile of the same name. No file ever holds a secret: keys named like password, passwd, pwd, secret or token, and DSNs with an embedded password, are refused.
A user config is seen by one account only, so it cannot hold the profiles used by the separated console: use the project config.
[profiles.uat]
engine = "mariadb" # mariadb | mysql | postgres | sqlite
host = "db.uat.example.com" # or path = "app.db" for sqlite
port = 3306 # default 3306, or 5432 for postgres
user = "" # empty: asked at console start
database = "" # empty: chosen per query (--db)
credentials = "ask" # ask | keychain | vault
vault_path = "" # with vault: "database/creds/<role>" or a KV secret
tls = "verify-full" # disable | prefer | require | verify-ca | verify-full
tls_ca = "" # optional PEM bundle replacing the system roots
tls_cert = "" # optional client certificate (PEM)
tls_key = "" # its private key (PEM, unencrypted, mode 0600)
tls_server_name = "" # optional: the name verify-full checks instead of host
credentials_ttl = "20m" # optional: ask for the secret again
tier = "read" # read | write | ddl | admin
production = false
detectors = ["email", "phone", "iban", "card", "be_niss"]
[profiles.uat.limits]
statement_timeout = "30s"
explain_rows_warn = 100000
explain_rows_refuse = 1000000
max_rows = 200
max_cell_chars = 200
max_output_bytes = 65536
k_anonymity = 5
explain_cost_refuse = 0 # engine cost units; 0 = off
A remote host defaults to tls = "verify-full": the server certificate must verify, against tls_ca for a private CA; tls_server_name names the certificate’s host when host is an IP address or an alias. tls_cert and tls_key go together, need require or stronger, and present a client certificate when the server asks for one (PostgreSQL clientcert/cert in pg_hba.conf, MySQL REQUIRE X509); keep both files in the console account’s home, the key readable by that account only. On MariaDB and MySQL, a remote host whose certificate is not verified answers no request for the password in clear: connect with verify-ca or verify-full, or use an account with mysql_native_password or ed25519. TLS to the database.
A database reachable only from a bastion gets an ssh table: the console opens the SSH connection itself, with no local listening port, and checks the bastion against the console account’s ~/.ssh/known_hosts, asking on first use and refusing a changed key. host is then the database as seen from the bastion.
[profiles.prod]
engine = "postgres"
host = "db.internal" # as seen from the bastion
tls = "verify-full"
production = true
[profiles.prod.ssh]
host = "bastion.example.com"
user = "deploy"
auth = "key" # key | agent | password
key = "~/.ssh/id_ed25519" # in the console account's home
Check the machine
locksql doctor [--profile P] [--fix [--yes]]
Run it from both accounts. Each check prints ✓ (ok), ▲ (warning) or ✗ (failed), with a remediation under each one that is not ✓, then a count of each; the exit code is 1 when any check fails.
For each profile it checks that tls_ca, tls_cert and tls_key are readable PEM files and not about to expire, and, for a profile without ssh, fetches the server’s certificate with a TLS handshake that sends no user name and no secret: it reports a chain or a name that would not verify and a certificate that expires within 30 days.
--fix then makes the small, idempotent repairs the checks offer (the agents’ wiring, the client group, the socket directory, the project’s access for the console account, config file modes), shows the root ones as one script and runs it with sudo after you confirm (at once with --yes), and runs the checks again. A binary or system.toml that root does not own, a secret in the agent’s keychain and sudo rules are never touched. More on locksql doctor.
Start the console
In a terminal you keep in view, in the locksql session (switch user on the desktop, or log in on a text console with Ctrl-Alt-F3):
locksql console [--profile dev] [--project DIR] [--auto] [--allow-unmask] [--show-results]
Never in a terminal of your account, where the agents run and could read what you type: run from there, locksql console prints the command for the locksql session instead of starting. When your agents run under an account of their own, locksql console from yours opens the console as locksql through sudo -u, machinectl shell or su -l.
Docker
The image runs locksql with nothing installed on your machine but Docker. It holds the separated setup that sudo locksql install makes on a host: the console runs as locksql, the only account with the database credentials; the agent’s MCP server runs as agent, a member of locksql-clients; the socket directory is a named volume, locksql-run, shared by both, and the kernel checks every peer as on a host.
Build the image (from a clone, or straight from GitHub):
docker build -t locksql https://github.com/DavidGodefroid/locksql.git
Mount the project at its own path: the socket name is derived from it, so the console and the agent must see the same one. In the project directory, write the agent files, then add a database profile with the wizard (it asks for no password):
docker run --rm --user "$(id -u):$(id -g)" -e HOME=/tmp \
-v "$PWD:$PWD" -w "$PWD" locksql init claude
docker run -it --rm --user "$(id -u):$(id -g)" -e HOME=/tmp \
-v "$PWD:$PWD" -w "$PWD" locksql profile add
Inside a container localhost is the container: the wizard offers host = "host.docker.internal" for a database on your machine (on Linux, add --add-host=host.docker.internal:host-gateway to the console’s docker run) with tls = "prefer", and never the keychain, which the container does not have: the password is typed at console start, or read from Vault.
For credentials = "vault", build the image with the vault CLI (docker build --target vault; the default image leaves it out, it adds some 550 MB), add -e VAULT_ADDR=… to the console’s docker run and log in once as the console account: the token stays in the locksql-home volume.
Start the console in a terminal you keep in view:
docker run -it --rm --name locksql-console --user locksql \
-v "$PWD:$PWD" \
-v locksql-run:/run/locksql \
-v locksql-home:/home/locksql \
locksql console --project "$PWD"
The locksql-home volume keeps the approved policy, the audit log and known_hosts between runs. The agent starts the MCP server in a throwaway container; for Claude Code:
claude mcp add --scope user locksql -- \
sh -c 'exec docker run -i --rm -v "$PWD:$PWD" -w "$PWD" -v locksql-run:/run/locksql locksql mcp'
init writes locksql mcp as the MCP command: replace that entry with the one above. Codex, Gemini CLI and Cursor take the same command and arguments.
What it does not separate: whoever can run docker can attach to the console and type an approval, exec into it or open its volume, and an agent running shell commands in your account usually can. Docker mode keeps the credentials out of the agent’s reach and the approval in your terminal, but it is no boundary against an agent that runs commands of its own: for that, use sudo locksql install on the host, or run the agent in a container without the Docker socket. Running locksql in Docker has the details.
Windows: inside WSL2
locksql does not run natively on Windows: the separation relies on Unix accounts, socket peer credentials and terminals, and the Windows build refuses every command but version and help. Run it inside a WSL2 distribution instead: install the Linux release there and everything on this page applies inside it.
- Turn on systemd in
/etc/wsl.conf, keep the project in the distribution’s own file system (/home/..., not/mnt/c), and start the console from a Windows terminal withwsl.exe -d <distro> -u locksql, not withsuorsudo -u. - Recommended: run the agent inside WSL too, as your WSL account, with WSL interop turned off. The Linux kernel then separates the agent from the console, as on a Linux host.
- An agent on Windows can start
locksql mcpthroughwsl.exe, but any Windows process of your account can become root orlocksqlin the distribution: like Docker mode, that layout keeps the credentials out of the agent’s process but is no boundary against an agent that runs commands of its own.
The guide has not yet been verified end to end on Windows 11. Windows (WSL2) in the usage guide.