This score renders Vaultwarden authentication activity on the Tempo timeline: failed and successful logins, admin-panel access, vault exports, new users, invitations, and a brute-force burst signal, plus whether the server is reachable. It is read-only. The actions open the vault or admin in your browser and copy a few fields. Nothing writes back to Vaultwarden, and the admin token and vault data never leave the host that runs the watcher.

Vaultwarden events on the Tempo timeline

How it works

Vaultwarden has no native outbound webhook. Its authentication events only appear in its log, so this integration is log-driven. A small companion helper runs on the Vaultwarden host, tails the container log, and POSTs a structured event to Tempo on each auth-relevant line.

Vaultwarden container  (logs auth lines to stdout)
      |  docker logs -f
helper (vaultwarden-tempo.sh)  on the container host
      |   parses the line, extracts source IP and user email
      |   also polls  ${VW_URL}/alive  for reachability
      |  HTTP POST, per-provider token
Tempo ingestion server  on <mac>:7776/ingest

The helper does two things. It tails docker logs for authentication lines and emits an event per line (with the source IP and UserEmail). Separately, it polls Vaultwarden’s /alive endpoint on an interval, so a stopped or unreachable container is reported as down (the log tail alone cannot see “down”). The score handles what happens after an event lands: severity, the badge label, grouping, and the action buttons.

The helper holds no Vaultwarden credential. It never reads the admin token. It only tails docker logs, and its single secret is the Tempo ingestion token.

Setup

1. Create the Tempo token

In Tempo, open Settings → Ingestion and create a token bound to com.vaultwarden. Copy its value; you will put it in the helper’s configuration below.

2. Enable the score

In Tempo, open Manage Sources and enable Vaultwarden. The score ships built-in (seeded into ~/Library/Application Support/Tempo/Scores/ on first launch), so there is nothing to download. Only the generic Scripts source auto-installs; this one is activated here.

3. Get the helper

In the score’s Source tab (Score Editor), the Helper section has two buttons:

The package contains vaultwarden-tempo.sh (the watcher) and vaultwarden-run.sh (a wrapper for the keepalive cron). Copy that folder to the host running the Vaultwarden container. The helper is a shell script and runs on any Linux or macOS host that has docker and curl.

4. Configure it

Create a vaultwarden.env file next to the scripts (chmod 600):

# vaultwarden.env  (chmod 600)
export VW_URL=https://<vaultwarden-host>:8443
export TEMPO_URL=http://<mac-ip>:7776/ingest
export VW_CONTAINER=vaultwarden
export STATE_DIR=$HOME/.local/state/vaultwarden-tempo

# Tempo token read from its own chmod 600 file, not stored in this env file:
export TEMPO_TOKEN_FILE=$STATE_DIR/tempo-token

Liveness controls:

Keeping the token out of plaintext (*_FILE)

You do not have to leave the token in a plaintext .env. Every secret also resolves from a file: set TEMPO_TOKEN_FILE to a path and the helper reads the secret from there, so the value never enters the environment. Point it at a Docker secret (/run/secrets/..., mounted in tmpfs), a systemd encrypted credential (LoadCredentialEncrypted), or any chmod 600 file. Resolution order is file, then environment variable, then Keychain.

The firewall must let the Vaultwarden host reach the Mac on port 7776 (allow it in the macOS firewall or Little Snitch). You can optionally restrict the token to that host’s IP with the token’s allowlist in Settings → Ingestion.

5. Run it as a daemon

The watcher is a long-lived stream (a docker logs -f tail plus the /alive poll), not a cron one-shot. Keep it running and restart it on crash or reboot.

Linux: flock keepalive cron

This is the method the helper package ships for. vaultwarden-run.sh sources the env file and execs the watcher, logging to a file:

# vaultwarden-run.sh
#!/bin/sh
. /home/<you>/tempo-vaultwarden/vaultwarden.env
exec bash /home/<you>/tempo-vaultwarden/vaultwarden-tempo.sh >> /home/<you>/tempo-vaultwarden/vw-watcher.log 2>&1

A single-instance flock cron restarts it within 5 minutes if it dies and starts it on boot. flock holds the lock for the whole life of the stream, so a second copy never starts:

*/5 * * * * flock -n /home/<you>/tempo-vaultwarden/vaultwarden.lock /home/<you>/tempo-vaultwarden/vaultwarden-run.sh
@reboot     flock -n /home/<you>/tempo-vaultwarden/vaultwarden.lock /home/<you>/tempo-vaultwarden/vaultwarden-run.sh

Linux: systemd (alternative)

If you prefer systemd, create /etc/systemd/system/tempo-vaultwarden.service:

[Unit]
Description=Tempo Vaultwarden watcher
After=docker.service network-online.target

[Service]
EnvironmentFile=/home/<you>/tempo-vaultwarden/vaultwarden.env
ExecStart=/usr/bin/env bash /home/<you>/tempo-vaultwarden/vaultwarden-tempo.sh
Restart=always

[Install]
WantedBy=multi-user.target

Then sudo systemctl enable --now tempo-vaultwarden.

macOS: launchd

If Vaultwarden runs in Docker on a Mac, a LaunchAgent keeps the watcher alive. Create ~/Library/LaunchAgents/app.tempo.vaultwarden.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>app.tempo.vaultwarden</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/sh</string>
    <string>/Users/<you>/tempo-vaultwarden/vaultwarden-run.sh</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
</dict>
</plist>

Load it with launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/app.tempo.vaultwarden.plist.

Windows

Run Vaultwarden’s watcher on the container host. If that host is Windows, run the script under WSL or Git Bash (it needs a POSIX shell, docker, and curl) and keep it alive with a Scheduled Task that runs at logon and restarts on failure, or a service wrapper such as NSSM. In practice the Vaultwarden host is usually Linux, so the flock or systemd method above is the common path.

What you’ll see

Events arrive as alerts with a badge label and a severity. The severity drives whether Tempo rings its needs-attention bell (warning and critical) or stays quiet in the timeline (info).

Event / statusBadgeSeverityWhen
Status: downDowncriticalthe /alive poll fails (server stopped)
Status: unreachableUnreachablecriticalthe /alive poll cannot reach the host
login_failed_burstBrute-forcewarning5 or more failed logins within 5 minutes
admin_login_failedAdmin failwarninga wrong admin token at /admin
vault_exportedVault exportwarninga vault was exported (a data-exfiltration signal)
login_failedLogin failinfoa single failed login
user_loginLogininfoa successful login
user_createdNew userinfoa new account registered
user_invitedInviteinfoan invitation was sent
admin_loginAdmininfoa successful admin-panel login
(any other event)Infoinfodefault

The user_invited event carries no email: the recipient address is not in the Vaultwarden log unless SMTP is configured (Vaultwarden then logs the recipient).

If you want to silence a specific event type you do not care about (for example user_login), do not edit the helper. In the score’s Ack and dismiss tab (Score Editor), add a rule that auto-dismisses events matching that Event. They still land for the record but never demand attention.

Grouping and actions

Events stack within a 1 hour window, keyed on UserEmail and Event (["${metadata.UserEmail}", "${metadata.Event}"]). Repeated activity of the same kind by the same account collapses into one entry rather than a row per line. A burst of failed logins for one account therefore reads as a single stack.

Five actions are attached to every event:

Actions whose template field is empty for a given event (for example Copy user email on an event with no email) resolve to nothing useful, since the helper sends an empty string for fields it cannot extract.

Metadata fields

The helper sends these keys, and the score reads them:

Troubleshooting and limitations