Revaulter
GitHub

Installing Revaulter

Revaulter is distributed as a container image and runs as a single-container service.

Requirements#

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

HTTPS is required for Revaulter because it uses WebCrypto. You can either configure Revaulter to start a HTTPS server (which requires providing TLS certificates to Revaulter directly) or use a reverse proxy that performs TLS termination, such as Caddy, Traefik, Nginx, etc.

Container images#

ImageDescription
ghcr.io/italypaleale/revaulter:2Revaulter server
ghcr.io/italypaleale/revaulter-cli:2Revaulter CLI

Both images are available for amd64 and arm64.

Configuration#

Revaulter is configured via a YAML file and/or environment variables. Inside the container, it looks for config.yaml at:

  1. The path set in the REVAULTER_CONFIG environment variable
  2. /etc/revaulter/config.yaml
  3. $HOME/.revaulter/config.yaml
  4. The same directory as the binary

Required configuration#

KeyEnv varDescription
databaseDSNREVAULTER_DATABASEDSNDatabase connection string (see Database below)
secretKeyREVAULTER_SECRETKEYInstance-wide secret for key derivation (see Secret key below)
KeyEnv varDefaultDescription
baseUrlREVAULTER_BASEURLhttps://localhost:<port>Public URL where users access the web UI. Used for webhook links and WebAuthn origin validation.
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.
webhookFormatREVAULTER_WEBHOOKFORMATplainWebhook format: plain, slack, or discord
webhookKeyREVAULTER_WEBHOOKKEYValue sent as Authorization header on webhook requests (include the scheme, e.g. Bearer abc123)
portREVAULTER_PORT or PORT8080Port to bind to
bindREVAULTER_BIND0.0.0.0Address/interface to bind to
disableSignupREVAULTER_DISABLESIGNUPfalseDisable creation of new user accounts
sessionTimeoutREVAULTER_SESSIONTIMEOUT5mSession duration before re-authentication is required (max: 1h)
requestTimeoutREVAULTER_REQUESTTIMEOUT5mDefault timeout for requests (can be overridden per-request; max: 24h)
trustedProxiesREVAULTER_TRUSTEDPROXIESComma-separated list of IPs/CIDRs to trust for X-Forwarded-* headers
forceSecureCookiesREVAULTER_FORCESECURECOOKIESfalseForce the Secure flag on cookies (set to true when behind a TLS-terminating reverse proxy)
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)

Optional WebAuthn configuration#

These settings are necessary in some scenarios only. Revaulter derives sensible defaults from baseUrl.

KeyEnv varDefaultDescription
webauthnRpIdREVAULTER_WEBAUTHNRPIDDerived from baseUrlWebAuthn Relying Party ID
webauthnRpNameREVAULTER_WEBAUTHNRPNAMERevaulterWebAuthn Relying Party display name
webauthnOriginsREVAULTER_WEBAUTHNORIGINSbaseUrlAllowed WebAuthn origins

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 single-node deployments.

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.

Secret key#

Generate a secret key:

openssl rand -base64 32

⚠️ Warning: Rotating secretKey bricks every existing account on the instance. The secret key is used to derive the WebAuthn PRF salt that every user’s in-browser key derivation is bound to. Treat this value as immutable for the lifetime of the instance. If you must rotate it, every user will need to re-register 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 this value is omitted, Revaulter generates a random signing key at startup; existing sessions are invalidated whenever the process restarts.

TLS#

Revaulter requires HTTPS for WebAuthn to work (browsers enforce a secure context). You have two options:

  1. Reverse proxy (recommended): Terminate TLS at a reverse proxy (Caddy, Traefik, Nginx, etc.) and forward plain HTTP to Revaulter.
    • Set forceSecureCookies: true in this case.
  2. Direct TLS: Provide certificates via tlsPath (a directory containing tls-cert.pem and tls-key.pem) or via tlsCertPEM/tlsKeyPEM. Revaulter watches the tlsPath directory and auto-reloads certificates.

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:

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:latest
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.
  3. After registration, the UI shows your request key: this is the per-user key that CLI requests are routed by.
  4. Optionally configure allowed IP addresses for your account from the settings page.

Account self-registration can be disabled by setting disableSignup: true after initial setup.

Edit this page on GitHub