Revaulter
GitHub

Installing Revaulter

Revaulter is distributed as a container image and runs as a single-container service. With PostgreSQL, you can also run multiple replicas.

Requirements#

  • A container runtime (Docker, Podman, etc.)
  • A database: SQLite or PostgreSQL
  • HTTPS access for the web UI
  • Optional: a webhook endpoint for notifications (including Discord, Slack, ntfy, Telegram, Teams, or any HTTP endpoint)

HTTPS is required because browsers only allow passkeys and WebCrypto on secure pages.
You can either have Revaulter serve HTTPS itself, with your TLS certificates, or use a reverse proxy that terminates TLS, such as Caddy, Traefik, or Nginx. See TLS.

Container images#

ImageDescription
ghcr.io/italypaleale/revaulter:2Revaulter server
ghcr.io/italypaleale/revaulter-cli:2Revaulter CLI
ghcr.io/italypaleale/revaulter-edit:2Revaulter local file encryption utility

Images are available for amd64, arm64 and arm/v7.

Configuration#

Revaulter reads its configuration from a YAML file, from environment variables, or both. It loads the first config.yaml it finds in:

  1. The path set in the REVAULTER_CONFIG environment variable
  2. The current working directory
  3. $HOME/.revaulter/
  4. /etc/revaulter/

In a container, mount the file at /etc/revaulter/config.yaml.

Required configuration#

KeyEnv varDescription
databaseDSNREVAULTER_DATABASEDSNDatabase connection string (see Database below)
secretKeyREVAULTER_SECRETKEYInstance-wide secret for key derivation (see Secret key below)
baseUrlREVAULTER_BASEURLPublic URL where users access the web UI, such as https://revaulter.example.com. Passkeys are registered for this address, and notifications link to it
KeyEnv varDefaultDescription
sessionSigningKeyREVAULTER_SESSIONSIGNINGKEYRandom at startupSecret used to sign session tokens. Set this in production so sessions survive restarts.
tlsPathREVAULTER_TLSPATHConfig file directoryDirectory containing tls-cert.pem and tls-key.pem. Revaulter watches for changes and auto-reloads.
tlsCertPEMREVAULTER_TLSCERTPEMPEM-encoded TLS certificate (alternative to tlsPath)
tlsKeyPEMREVAULTER_TLSKEYPEMPEM-encoded TLS key (alternative to tlsPath)

Optional configuration#

KeyEnv varDefaultDescription
webhookUrlREVAULTER_WEBHOOKURLWebhook endpoint for notifications. Leave unset to disable webhook notifications. A notification that fails is retried up to 2 more times, unless the request isn’t pending anymore.
webhookFormatREVAULTER_WEBHOOKFORMATplainWebhook format: plain, slack, discord, ntfy, telegram, googlechat, or teams
webhookKeyREVAULTER_WEBHOOKKEYValue sent as Authorization header on webhook requests (include the scheme, e.g. Bearer abc123)
webhookAllowPrivateAddressesREVAULTER_WEBHOOKALLOWPRIVATEADDRESSESfalseAllow sending webhook notifications to private addresses (e.g. those in the LAN), which are refused by default. Enable it only if the receiver is on a private network, for example a self-hosted ntfy, Gotify, or alert router on your LAN
portREVAULTER_PORT or PORT8080Port to bind to
bindREVAULTER_BIND0.0.0.0Address/interface to bind to
disableSignupREVAULTER_DISABLESIGNUPfalseDisable creation of new user accounts
oidcAllowPrivateAddressesREVAULTER_OIDCALLOWPRIVATEADDRESSESfalseAllow fetching the discovery documents and JWKS of trusted OIDC issuers from private, loopback, and link-local addresses, which are refused by default. Enable it only if your issuers are on a private network, and you trust every user who can configure issuers
sessionTimeoutREVAULTER_SESSIONTIMEOUT5mSession duration before re-authentication is required (max: 1h)
requestTimeoutREVAULTER_REQUESTTIMEOUT5mDefault timeout for requests. Each request can set its own timeout, up to 24h
trustedProxiesREVAULTER_TRUSTEDPROXIESList of IPs or CIDRs of proxies whose X-Forwarded-* headers are trusted (comma-separated in the env var)
trustedRequestIdHeaderREVAULTER_TRUSTEDREQUESTIDHEADERHeader to trust as request ID (e.g. X-Request-ID, CF-Ray)
logHealthChecksREVAULTER_LOGHEALTHCHECKSfalseInclude /healthz requests in the request logs
logLevelREVAULTER_LOGLEVELinfoLog level: debug, info, warn, error
logAsJsonREVAULTER_LOGASJSONautoEmit JSON logs (defaults to true when no TTY is attached)
francisPeerAddressREVAULTER_FRANCISPEERADDRESS127.0.0.1:7571Address (host and port) the other replicas use to reach this one when running multiple replicas
francisBindPortREVAULTER_FRANCISBINDPORTPort of francisPeerAddressUDP port this replica listens on for the other replicas, if it’s different from the port in francisPeerAddress
francisDatabaseDSNREVAULTER_FRANCISDATABASEDSNDerived from databaseDSNWhere short-lived data is stored

Optional WebAuthn configuration#

These settings are necessary in some scenarios only. By default, Revaulter derives them from baseUrl.

KeyEnv varDefaultDescription
webauthnRpIdREVAULTER_WEBAUTHNRPIDDerived from baseUrlWebAuthn Relying Party ID
webauthnRpNameREVAULTER_WEBAUTHNRPNAMERevaulterWebAuthn Relying Party display name
webauthnOriginsREVAULTER_WEBAUTHNORIGINSAdditional origins allowed to use passkeys. baseUrl is always allowed

Database#

Revaulter supports SQLite and PostgreSQL. The backend is detected automatically from the DSN:

DSN formatBackend
postgres://user:pass@host:5432/dbnamePostgreSQL
sqlite:///path/to/file.dbSQLite
/path/to/file.db (no scheme)SQLite

SQLite requires no external dependencies and is a good default for a single server. To run multiple replicas, use PostgreSQL.

For PostgreSQL, use a standard connection string, for example:

postgres://revaulter:password@db.example.com:5432/revaulter

⚠️ Warning: When using SQLite, the database file must not be stored on a networked filesystem, like NFS or SMB.

Short-lived data#

Revaulter keeps short-lived data, including pending requests, in an embedded Francis actor host, which stores it separately from the main database:

Main databaseFrancis data storage
PostgreSQLA francis schema in the same database, created automatically
SQLite, e.g. /data/revaulter.dbA file in the same folder, e.g. /data/revaulter.francis.db

Set francisDatabaseDSN to store it elsewhere. Like the main SQLite file, this database must not be stored on a networked filesystem.

The data stored in Francis is ephemeral and never included in backups.

Secret key#

Generate a secret key:

openssl rand -base64 32

⚠️ Warning: Changing secretKey makes every existing account on the instance unusable. The secret key is used to derive the WebAuthn PRF salt, which every user’s keys depend on.
Treat this value as immutable for the lifetime of the instance. If you must change it, every user will need to register again from scratch.

📝 Note: secretKey is not used to encrypt anything stored in the database. All request payloads and responses are end-to-end encrypted in the browser; the server only stores opaque envelopes.

Session signing key#

Generate a session signing key:

openssl rand -base64 32

Set sessionSigningKey in production so session tokens remain valid across restarts. If it’s not set, Revaulter generates a random signing key at startup, and existing sessions end whenever the process restarts.

When running multiple replicas, sessionSigningKey is required, and it must be the same on all of them.

TLS#

Passkeys only work over HTTPS. You have two options:

  1. Reverse proxy (recommended): terminate TLS at a reverse proxy, such as Caddy, Traefik, or Nginx, and forward plain HTTP to Revaulter. Set trustedProxies to the proxy’s address, so Revaulter sees the clients’ real IP addresses, which it uses for IP restrictions, rate limiting, and the audit log.
  2. Direct TLS: provide certificates with tlsPath (a directory containing tls-cert.pem and tls-key.pem), or with tlsCertPEM and tlsKeyPEM. Revaulter watches the tlsPath directory and reloads certificates automatically when they change.

Docker Compose example#

services:
  revaulter:
    image: ghcr.io/italypaleale/revaulter:2
    ports:
      - "8080:8080"
    volumes:
      - ./config.yaml:/etc/revaulter/config.yaml:ro
      - revaulter-data:/data
    restart: unless-stopped

volumes:
  revaulter-data:

The container runs as a non-root user (UID 65532). If you mount a host directory at /data instead of a volume, make that user its owner, for example with sudo chown 65532:65532 ./data. On hosts with SELinux enforcing, also add :Z to the mount.

With a config.yaml:

databaseDSN: "/data/revaulter.db"
secretKey: "<your-secret-key>"
sessionSigningKey: "<your-session-signing-key>"
baseUrl: "https://revaulter.example.com"

To enable Discord notifications, add:

webhookUrl: "https://discord.com/api/webhooks/your-webhook-id/your-webhook-token"
webhookFormat: "discord"

If you want Revaulter to handle TLS directly:

services:
  revaulter:
    image: ghcr.io/italypaleale/revaulter:2
    ports:
      - "443:8080"
    volumes:
      - ./config.yaml:/etc/revaulter/config.yaml:ro
      - ./tls:/etc/revaulter/tls:ro
      - revaulter-data:/data
    restart: unless-stopped

volumes:
  revaulter-data:

And add to config.yaml:

tlsPath: "/etc/revaulter/tls"
port: 8080

Place tls-cert.pem and tls-key.pem in the ./tls directory.

Podman Quadlet example#

First, store the config file as a Podman secret:

podman secret create revaulter-config ~/.config/revaulter/config.yaml

Create a container unit file at ~/.config/containers/systemd/revaulter.container (rootless) or /etc/containers/systemd/revaulter.container (rootful):

[Unit]
Description=Revaulter
After=network-online.target

[Container]
Image=ghcr.io/italypaleale/revaulter:2
PublishPort=8080:8080
Secret=revaulter-config,target=/etc/revaulter/config.yaml
Volume=revaulter-data.volume:/data
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target

Create a volume unit file at the same location, revaulter-data.volume:

[Volume]

Then reload and start:

# Rootless
systemctl --user daemon-reload
systemctl --user enable --now revaulter

# Rootful
systemctl daemon-reload
systemctl enable --now revaulter

To update the config later, recreate the secret and restart the service:

podman secret rm revaulter-config
podman secret create revaulter-config ~/.config/revaulter/config.yaml
systemctl --user restart revaulter

User setup#

After starting Revaulter:

  1. Open the web UI at your configured baseUrl.
  2. Create an account: registration uses WebAuthn, so you’ll need a passkey-capable authenticator with PRF support.

    Finish creating the account within 24 hours of registering the passkey, or you’ll need to start over.

  3. After registration, the UI shows your request key, which the CLI uses to send you requests. You can also find it under Settings → Request auth.
  4. Optionally, restrict the IP addresses that can use your account under Settings → Firewall.

After creating your accounts, you can turn off self-registration by setting disableSignup: true.

Edit this page on GitHub