Skip to content

Getting started

Varlatch is self-hosted. One person on your team runs an installation, and everyone else connects to it with the CLI and a passkey. This page takes you from nothing to varlatch run:

  1. Get the CLI, on the installation’s host and on every developer’s machine.
  2. Run an installation, once per team.
  3. Set up a project, once per repository.
  4. Invite your team, and join as a teammate.
  5. Connect CI and other machines.

Already invited to an installation? Get the CLI, then go to Join a project.

Using a coding agent? It can do most of this for you, from installing the CLI to moving your .env files in: Set up with a coding agent.

  • For the installation: a Linux host, amd64 or arm64, with Docker and Docker Compose 2.24 or newer, and a way for people to reach it: a domain name pointing at the host, a Tailscale network, or your own reverse proxy. Run an installation explains the choice, and Host requirements lists memory, disk, and ports.
  • For each person: Node.js 22, 24 or 26 with its current security fixes for the CLI (Node.js version), and a browser that supports passkeys. Varlatch has no passwords: you sign in with a passkey kept on your device, in a password manager, or on a security key.

The CLI is one file, varlatch-cli-<version>.cjs, attached to every release. It needs only Node.js: Node.js version. Use the version your installation runs: the CLI and the installation are released together.

Terminal window
V=0.21.0 # the release your installation runs
curl -fLO https://github.com/varlatch/varlatch/releases/download/v$V/varlatch-cli-$V.cjs
curl -fLO https://github.com/varlatch/varlatch/releases/download/v$V/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS

Releases after 0.15.0 also sign SHA256SUMS: check that signature first, as Verifying a release describes. On macOS, check the file with grep " varlatch-cli-$V.cjs\$" SHA256SUMS | shasum -a 256 -c -.

Then put it on your PATH as varlatch:

  • On your own machine, a directory you own, so that varlatch self-update can replace it without sudo:

    Terminal window
    mkdir -p ~/.local/bin
    install -m 755 varlatch-cli-$V.cjs ~/.local/bin/varlatch
    varlatch --version
  • On the installation’s host, root-owned, as the backup runbook’s Install the operator CLI describes. Replace it after every upgrade.

  • On Windows, a directory you own, with a small varlatch.cmd that starts it: On Windows below.

varlatch self-update installs a newer release the same way: it checks the file against SHA256SUMS, and the signature when the release is signed and cosign is installed.

The CLI runs on Node.js 22, 24 or 26, from a release with that line’s security fixes: at least 22.23.4 on 22, 24.22.1 on 24, or 26.12.1 on 26. These are the Node.js security releases of 21 October 2026, which fixed, among others, a flaw in decompressing Brotli data received over the network (CVE-2026-111497).

On an older release, on a line that no longer receives security fixes (such as 20, 23 or 25), or on a prerelease (a nightly or release candidate, whose fixes its version cannot show), every command first prints one warning on stderr, and otherwise works as before: its output and exit status do not change. Only varlatch agents hook, which a coding agent’s guardrail calls on every tool call, stays quiet. Update Node.js to clear the warning. If your Node.js carries those fixes under an older version number, as a distribution’s backport can, set VARLATCH_NODE_CHECK=off to hide the warning.

Install a current Node.js LTS release (Node.js version), for example with winget:

Terminal window
winget install OpenJS.NodeJS.LTS

In a new PowerShell window, so that it finds node, download the CLI and SHA256SUMS, and check the file against its line in SHA256SUMS:

Terminal window
$V = "0.21.0" # the release your installation runs
curl.exe -fLO "https://github.com/varlatch/varlatch/releases/download/v$V/varlatch-cli-$V.cjs"
curl.exe -fLO "https://github.com/varlatch/varlatch/releases/download/v$V/SHA256SUMS"
$Line = Get-Content SHA256SUMS | Where-Object { $_ -like "* varlatch-cli-$V.cjs" }
$Hash = (Get-FileHash -Algorithm SHA256 "varlatch-cli-$V.cjs").Hash
if ($Line -and $Hash -eq ($Line -split " ")[0]) { "OK" } else { "MISMATCH: do not install this file" }

It must print OK. Releases after 0.15.0 also sign SHA256SUMS: check that signature first, as Verifying a release describes.

Then install it as %LOCALAPPDATA%\Programs\Varlatch\varlatch.cjs, with a varlatch.cmd next to it that runs it with Node.js, and add that directory to your user PATH:

Terminal window
$Dir = "$env:LOCALAPPDATA\Programs\Varlatch"
New-Item -ItemType Directory -Force -Path $Dir | Out-Null
Copy-Item "varlatch-cli-$V.cjs" "$Dir\varlatch.cjs"
Set-Content -Path "$Dir\varlatch.cmd" -Value '@node "%~dp0varlatch.cjs" %*' -Encoding Ascii
$UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
if (-not $UserPath) { $UserPath = $Dir } elseif (($UserPath -split ";") -notcontains $Dir) { $UserPath = "$UserPath;$Dir" }
[Environment]::SetEnvironmentVariable("Path", $UserPath, "User")
$env:Path = "$env:Path;$Dir"
varlatch --version

New terminals find varlatch on their own; the second-to-last line makes this one find it too. varlatch self-update replaces varlatch.cjs in place, without administrator rights, because the directory is yours. Varlatch for Windows, the tray app, installs the CLI the same way.

Skip this if someone already runs one for you.

On the host, download the deployment bundle of the same release, check it, and unpack it where the installation will live:

Terminal window
curl -fLO https://github.com/varlatch/varlatch/releases/download/v$V/varlatch-compose-$V.tar.gz
sha256sum --ignore-missing -c SHA256SUMS
tar -xzf varlatch-compose-$V.tar.gz
cd varlatch-$V
varlatch setup

Run it as a user who can use Docker. varlatch setup first asks how people will reach Varlatch:

Choice Address You need
public https://vault.example.com, with a certificate a bundled Caddy obtains the name resolving to this host in public DNS, and ports 80 and 443 reachable from the internet
tailnet https://<machine>.<tailnet>.ts.net, for members of your tailnet only a Tailscale auth key, and MagicDNS and HTTPS certificates enabled in the Tailscale admin console
external whatever your reverse proxy serves; also for a LAN-only installation a proxy forwarding to 127.0.0.1:8787, WebSocket upgrades included

Choose carefully: the address is where passkeys are registered, so changing it later means everyone registers their passkey again.

Then setup does the rest, and tells you what it is doing:

  1. It generates every key and password the installation needs, as files in ./secrets, and starts the services.
  2. It prints a one-time link. Open it and create the first administrator’s passkey. Setup waits until you have.
  3. It walks you through keeping the two recovery keys off this host: the Root KEK, which every stored value depends on, and the backup key. Without a copy of the Root KEK, a backup cannot be restored. Do not skip this.
  4. It finishes with varlatch doctor, a read-only health check you can run on the host at any time.

If setup stops halfway, for a failed download, a reboot, or a cancelled passkey prompt, run it again: it continues where it stopped and never replaces a key it already made.

Next, schedule backups: Backup and recovery covers where they go and how to test a restore. The deployment guide covers every setup option, running on Coolify, and upgrades.

Varlatch keeps configuration in organizations, which contain projects, which contain environments such as development and production. A project usually matches one repository. Its contract lists the items the application needs, with a type for each and whether it is a Secret, and Varlatch checks every environment against it.

Sign in from your machine. The CLI opens your browser, or prints the address to open, and you sign in with your passkey:

Terminal window
varlatch login --server https://vault.example.com

Create an organization and a project, either in the dashboard, which walks you through the same steps, or with the CLI:

Terminal window
varlatch org create acme "Acme" --server https://vault.example.com
varlatch project create api --org acme --server https://vault.example.com

A project created with the CLI takes its contract from the repository. Pass --managed to project create to edit the contract in the dashboard instead.

Then, in the repository:

Terminal window
varlatch init --org acme --project api --server https://vault.example.com
varlatch env-create development --tier development
varlatch env-create production --tier production

varlatch init writes varlatch.toml, which names the server, organization, project, and default environment (development). It holds no credentials: commit it, so everyone working on the repository uses the same project. It also writes the files coding agents read, described in Coding agents; --no-agent-files skips them.

If the project already has a .env file, import it. The CLI reads the file itself, so values never appear on the command line or in its output:

Terminal window
varlatch import .env --dry-run # the plan: names, types, Secrets; stores nothing
varlatch import .env --contract --delete-source # store the values, then delete the file
varlatch contract activate <revision> # the revision import printed

--contract adds the items the contract does not have yet, each as a Secret unless you name it with --plain <NAME>. The values go to the default environment; fill another one the same way, for example with varlatch import .env.production -e production. Importing has the details.

Describe the items in a .env.schema file in the repository:

# @required
# @type=url
DATABASE_URL=
# @public
PORT=8080

Then push and activate it, and set the values:

Terminal window
varlatch contract push --schema .env.schema
varlatch contract activate <revision> # the revision push printed
varlatch values set DATABASE_URL # a hidden prompt asks for the value

Items are Secrets unless marked @public. The .env.schema format lists everything a contract can say.

Terminal window
varlatch validate # does the environment satisfy the contract?
varlatch run -- npm run dev # the default environment
varlatch run -e production -- node server.js

varlatch run gives the values to your command as environment variables. No file of secrets is written to disk. To go further:

  • varlatch types --out src/config.ts (or app/varlatch_config.py) generates typed access to the configuration: Type generation.
  • varlatch scan --install-hook adds a pre-commit hook that stops you from committing one of your Secrets: Secret scanning.

Invite each person from the dashboard (Access, then Members), or from the repository with the CLI:

Terminal window
varlatch invite "Ada Lovelace" # a member
varlatch invite "Grace Hopper" --role admin # an organization admin

Each invitation is a one-time link: send it to that person. No email is involved. Opening it creates their account and passkey.

Access is denied unless something allows it. A member can read and change development environments, read staging environments, and see only the names of production items. An admin can do everything in the organization. For anything else, such as a member who deploys to production, add a grant in the dashboard under Access.

When someone leaves the team, an admin removes them from the dashboard (Access, then Members, then the person’s menu), or with the CLI:

Terminal window
varlatch member remove "Ada Lovelace" --confirm "Ada Lovelace"
varlatch member role "Grace Hopper" member # admin to member, or back with admin
varlatch member leave --confirm acme # leave an organization yourself

From their next request they reach nothing in the organization: the Grants given to them directly are revoked, and they leave every group and team. Their sign-in itself is not affected. What they set up, such as integrations, Connections, webhooks and machine credentials they were shown, keeps working: removal lists it for you to review. The last admin of an organization cannot be removed or leave.

Then see what they received, from the dashboard (Former members, then What they received) or the CLI:

Terminal window
varlatch member exposure "Ada Lovelace" # --json for one record per line

The report lists each version of each value they received, how, and whether Varlatch still serves it, with what they could reach when they were removed and what they set up. Rotate what is still served, at the provider too: a new value in Varlatch does not revoke the old one there. Then mark each version as revoked at the provider; the report never does that on its own. It needs permission to read the audit log, and never shows a value.

When you have been invited:

  1. Open the link you were sent and create your passkey.

  2. Get the CLI.

  3. In your copy of the repository, which already has varlatch.toml:

    Terminal window
    varlatch login --server https://vault.example.com
    varlatch run -- npm run dev

On a machine without a browser, such as a server over SSH or a container, sign in with a code instead, approving it with your passkey on any other device:

Terminal window
varlatch login --server https://vault.example.com --start # prints an address and a code
varlatch login --server https://vault.example.com --wait # after you approved it

varlatch status shows where you are signed in and when the credential expires.

Your passkeys are listed in the dashboard under your account, then Security. Keep at least two, for example a laptop and a security key, so losing one device never locks you out.

Each passkey is either trusted or unconfirmed:

  • Trusted: a passkey you created from a link: setting up the installation, an invitation, or a link from whoever runs the server (varlatch admin reenroll, or admin recover for an Installation Admin). Passkeys that existed before Varlatch began recording this are trusted too.
  • Unconfirmed: a passkey you added in the dashboard while signed in. To make it trusted, choose Confirm with another passkey and use one of your trusted passkeys with its PIN, fingerprint or face.

Why the difference: someone who stole your signed-in browser session could add a passkey of their own. They cannot confirm it without one of your trusted passkeys.

Signing in and approving a CLI sign-in work with any of your passkeys, and you can remove any of them, as before. What only a trusted passkey can do is approve a change that needs your approval, such as turning an organization’s security control down or off (Security controls and approvals). Once security controls can be enforced, an organization will also be able to require a sign-in with a trusted passkey that verified you.

If none of your passkeys is trusted, you cannot confirm one yourself: ask whoever runs the server for a link (varlatch admin reenroll --identity <your identity id>). The passkey you create with it is trusted.

Organization admins can see on the organization’s Security page whether every admin has a trusted passkey, along with the security controls and what each would do (The Security page).

Machines never use a person’s passkey. Create a machine identity in the dashboard, under Access, then Machines, and grant it the environments it needs: like a person, it starts with no access.

  • CI that issues OIDC tokens, such as GitHub Actions, needs no stored secret at all. Give a ci identity an OIDC binding: the token issuer (filled in for GitHub Actions), an audience you choose, and the subject your jobs’ tokens carry, such as repo:acme/api:*. In the job, get the CLI as above, then sign in and run; on GitHub Actions, the job needs the id-token: write permission:

    Terminal window
    varlatch login --server https://vault.example.com --oidc --org acme --audience <audience>
    varlatch run -e production -- ./deploy.sh

    On another CI system, pass its token with --oidc-token.

  • Servers and other workloads use a credential shown once when you create the identity. Store it where the machine keeps its secrets, and sign in with varlatch login --server <url> --token-stdin < credential-file.

    Programs on one host can share an identity and its grants, each with a credential of its own that you revoke on its own. Issue another one in the machine’s Credentials panel in the dashboard, or with the CLI, which writes the token only to a new file that only you can read, never to its output:

    Terminal window
    varlatch credential issue <identity-id> --name "backup job" --out backup-job.token
    varlatch credential list <identity-id> # every credential, by name
    varlatch credential revoke <identity-id> <credential-id>

    To find credentials that may no longer be needed, run varlatch security report (--json for the whole document). It lists the organization’s machine credentials that were never used, have not been used for 30 days, or never expire, each with its identity and when it was last used. It only reports: nothing is revoked or retired for you. It needs identity.manage, as credential list does. Short-lived credentials from OIDC sign-ins and agent-safe runs expire within an hour and are left out.

    To replace a credential, issue the new one, move the program to it, then revoke the old one.

    A credential issued with an expiry (--ttl) warns ahead: in its last 7 days, however long it was issued for, the machine’s Credentials panel marks it, every API response to it carries a Varlatch-Credential-Expires header, and the CLI, given it in VARLATCH_TOKEN, prints a warning on stderr (never on stdout). Credential expiry warnings has the details.

    A program can ask Varlatch which identity its credential belongs to: GET /v1/me answers with the identity, its organization, and the credential’s own name, and needs no grant. On the command line, varlatch whoami prints the same for the credential the CLI would use (VARLATCH_TOKEN, else the stored one). A program that relies on the answer should use another way only when the server lacks the identity.whoami capability. A refused credential, a server error, or an identity it does not expect (another organization, a name outside its convention: identities can be renamed) is an error to report. Each call spends a use of a use-limited credential, so ask once and keep the answer.

To see what a machine can reach, open it in Access, then Machines, or ask the CLI:

Terminal window
varlatch identity reach deployer # an identity ID or name; --json for the whole document

It lists the machine’s state and, per project and environment, what its grants allow. Reading values where a tailnet requirement applies shows the requirement as a condition, such as needs tailnet example.ts.net: tag:prod: a request must come from a device that meets it. The list checks no device and never shows a value. It needs permission to read identities and policy.

Varlatch can also push an environment’s values to GitHub Actions, Coolify, or Convex, for platforms that keep their own copy: add an integration on the environment’s Integrations tab. Integrations lists the credential each platform needs and where to find every field.

Varlatch lets a coding agent use a credential without being able to read it. varlatch init already wrote the instructions agents read. Then:

  • Coding agents: the skill, guardrails, and the agents Varlatch was evaluated with.
  • Assisted mode: an agent driving the CLI with your sign-in, with every Secret masked in what it sees.
  • Agent-safe runs: an agent that gets placeholders instead of Secrets.
  • The MCP server, for MCP hosts without a shell.

A change that needs your approval (such as turning a security control down) never goes through on an agent’s word: the agent stops and shows you an address, and you approve with your passkey. See Security controls and approvals.

Deleting a project erases every value of its environments at once, with no undo. Deleting an organization is a request: it freezes the organization at once, and runs after a waiting period (7 days unless you choose 1 to 30) and, when the organization has more than one admin, another admin’s approval. Any admin can cancel it until then. Both are in the dashboard (a project’s menu; Settings, then Delete organization), or:

Terminal window
varlatch project delete api --confirm api
varlatch org delete acme --confirm acme --after 14
varlatch org deletion acme # the open request, with its ID
varlatch org delete acme --approve <request-id> # another admin
varlatch org delete acme --cancel <request-id>

Deletion erases data from the live database only. Backups taken before it keep the data until their retention removes them, values pushed to platforms stay there, and copies people revealed or downloaded stay where they are: rotate those at their provider. A GitHub App stays installed on GitHub until you uninstall or delete it there. After an organization is deleted, the people who could read its audit log export it with varlatch audit export --deleted-org <organization-id>.

  • CONTEXT.md: the vocabulary, from planes and identities to grants and contracts.
  • Scripting: JSON output and exit statuses.
  • Threat model: what Varlatch guarantees, and what it does not.
  • CHANGELOG.md: what each release changed, and how to upgrade.