# 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

| Image | Description |
|-------|-------------|
| `ghcr.io/italypaleale/revaulter:2` | Revaulter server |
| `ghcr.io/italypaleale/revaulter-cli:2` | Revaulter 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

| Key | Env var | Description |
|-----|---------|-------------|
| `databaseDSN` | `REVAULTER_DATABASEDSN` | Database connection string (see [Database](#database) below) |
| `secretKey` | `REVAULTER_SECRETKEY` | Instance-wide secret for key derivation (see [Secret key](#secret-key) below) |

### Recommended configuration

| Key | Env var | Default | Description |
|-----|---------|---------|-------------|
| `baseUrl` | `REVAULTER_BASEURL` | `https://localhost:<port>` | Public URL where users access the web UI. Used for webhook links and WebAuthn origin validation. |
| `sessionSigningKey` | `REVAULTER_SESSIONSIGNINGKEY` | Random at startup | Secret used to sign session tokens. Set this in production so sessions survive restarts. |
| `tlsPath` | `REVAULTER_TLSPATH` | Config file directory | Directory containing `tls-cert.pem` and `tls-key.pem`. Revaulter watches for changes and auto-reloads. |
| `tlsCertPEM` | `REVAULTER_TLSCERTPEM` | | PEM-encoded TLS certificate (alternative to `tlsPath`) |
| `tlsKeyPEM` | `REVAULTER_TLSKEYPEM` | | PEM-encoded TLS key (alternative to `tlsPath`) |

### Optional configuration

| Key | Env var | Default | Description |
|-----|---------|---------|-------------|
| `webhookUrl` | `REVAULTER_WEBHOOKURL` | | Webhook endpoint for notifications. Leave unset to disable webhook notifications. |
| `webhookFormat` | `REVAULTER_WEBHOOKFORMAT` | `plain` | Webhook format: `plain`, `slack`, or `discord` |
| `webhookKey` | `REVAULTER_WEBHOOKKEY` | | Value sent as `Authorization` header on webhook requests (include the scheme, e.g. `Bearer abc123`) |
| `port` | `REVAULTER_PORT` or `PORT` | `8080` | Port to bind to |
| `bind` | `REVAULTER_BIND` | `0.0.0.0` | Address/interface to bind to |
| `disableSignup` | `REVAULTER_DISABLESIGNUP` | `false` | Disable creation of new user accounts |
| `sessionTimeout` | `REVAULTER_SESSIONTIMEOUT` | `5m` | Session duration before re-authentication is required (max: `1h`) |
| `requestTimeout` | `REVAULTER_REQUESTTIMEOUT` | `5m` | Default timeout for requests (can be overridden per-request; max: `24h`) |
| `trustedProxies` | `REVAULTER_TRUSTEDPROXIES` | | Comma-separated list of IPs/CIDRs to trust for `X-Forwarded-*` headers |
| `forceSecureCookies` | `REVAULTER_FORCESECURECOOKIES` | `false` | Force the `Secure` flag on cookies (set to `true` when behind a TLS-terminating reverse proxy) |
| `trustedRequestIdHeader` | `REVAULTER_TRUSTEDREQUESTIDHEADER` | | Header to trust as request ID (e.g. `X-Request-ID`, `CF-Ray`) |
| `logHealthChecks` | `REVAULTER_LOGHEALTHCHECKS` | `false` | Include `/healthz` requests in the request logs |
| `logLevel` | `REVAULTER_LOGLEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` |
| `logAsJson` | `REVAULTER_LOGASJSON` | auto | Emit 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`.

| Key | Env var | Default | Description |
|-----|---------|---------|-------------|
| `webauthnRpId` | `REVAULTER_WEBAUTHNRPID` | Derived from `baseUrl` | WebAuthn Relying Party ID |
| `webauthnRpName` | `REVAULTER_WEBAUTHNRPNAME` | `Revaulter` | WebAuthn Relying Party display name |
| `webauthnOrigins` | `REVAULTER_WEBAUTHNORIGINS` | `baseUrl` | Allowed WebAuthn origins |

## Database

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

| DSN format | Backend |
|------------|---------|
| `postgres://user:pass@host:5432/dbname` | PostgreSQL |
| `sqlite:///path/to/file.db` | SQLite |
| `/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:

```bash
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:

```bash
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

```yaml
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`:

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

To enable Discord notifications, add:

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

If you want Revaulter to handle TLS directly:

```yaml
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`:

```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:

```bash
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):

```ini
[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`:

```ini
[Volume]
```

Then reload and start:

```bash
# 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:

```bash
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.

