Skip to main content

Overview

Vault canaries are fake credential entries injected into your vault that look indistinguishable from real ones. Each canary contains a tracking URL embedded in the password field. If an attacker exfiltrates your vault and tries to use a canary credential, their browser or HTTP client hits the tracking URL, triggering an immediate alert with the attacker’s IP address, user agent, and referer. This is a defense-in-depth measure. Even if an attacker somehow decrypts your vault (compromised master password, key extraction, etc.), canaries act as a tripwire that catches them in the act of using stolen credentials.

Why canaries work

Credential theft typically follows a pattern: exfiltrate, then use. Attackers who steal password databases rarely inspect every entry manually — they feed credentials into automated tools or try them directly. Canary passwords exploit this behavior by embedding a beacon URL that fires on use. The key insight is that the attacker does not know which entries are real and which are canaries. The canary entries are:
  • Visually identical to real vault entries in the UI
  • Named after high-value targets (Chase Bank, AWS Console, Coinbase) to tempt usage
  • Hidden from the normal vault UI via a __CANARY__ marker in the encrypted metadata
  • Realistic in structure — they use plausible usernames derived from the owner’s email

Canary templates

PassAgent ships with six built-in canary templates designed to look like high-value targets that attackers would prioritize:
TemplateURLCategory
Chase Bankhttps://chase.comBanking
Corporate VPNhttps://vpn.internal.corpWork
AWS Consolehttps://console.aws.amazon.comCloud
Coinbasehttps://coinbase.comCrypto
Company Slackhttps://slack.comWork
Personal Gmail Backuphttps://mail.google.comEmail
When canaries are provisioned, pickCanaryTemplates(count) selects a random subset using Fisher-Yates shuffle with crypto.randomInt() for cryptographic randomness.

How canary passwords work

Each canary password is a URL pointing to PassAgent’s alert endpoint:
https://passagent.app/api/canary/alert?t={honeypot_id}
The honeypot_id is a 32-character hex string generated by crypto.randomBytes(16). When an attacker pastes this “password” into a login form, their browser may follow the URL (depending on the service’s behavior). More commonly, automated credential-stuffing tools that process password lists will hit the URL directly.

Alert mechanism

The GET /api/canary/alert endpoint in app/api/canary/alert/route.ts processes canary triggers:
1

Honeypot lookup

The endpoint extracts the t parameter from the query string and looks up the corresponding vault_canaries record by honeypot_identifier. If no match is found, a generic 404 is returned to avoid tipping off the attacker.
2

Record the trigger

The canary’s last_triggered_at timestamp and trigger_count are updated in the database. Multiple triggers from the same or different IPs are all recorded.
3

Log forensic data

The following data is captured and logged at error severity (to ensure alerting pipelines catch it):
  • source_ip — the attacker’s IP address (via getClientIp())
  • user_agent — the HTTP client identifier
  • referer — the referring page (if present)
  • triggered_at — precise timestamp
4

Dispatch webhook (if configured)

If the canary has an alert_config with a webhook_url, a POST request is sent with the full trigger payload. The webhook URL must be HTTPS and must not point to a private/internal IP range (SSRF protection validates against localhost, 127.x, 10.x, 172.16-31.x, 192.168.x, 169.254.x, ::1, and fc/fd IPv6 prefixes).
5

Return decoy response

The endpoint returns a 503 Service Unavailable HTML page with a generic “Please try again later” message. This mimics a typical service outage, giving the attacker no indication that they triggered a canary.
The canary alert endpoint intentionally does NOT require authentication. The whole point is that an attacker with stolen credentials — who does not have a PassAgent session — triggers the alert by using the canary password.

Username generation

generateCanaryUsername() creates realistic-looking email addresses derived from the vault owner’s actual email to make canary entries blend in:
VariantExample (owner: alice@gmail.com)
Same emailalice@gmail.com
Plus-addressedalice+chase@gmail.com
Base emailalice@gmail.com
The variant is selected randomly using crypto.randomInt(). The plus-addressing pattern is common for service-specific registrations and increases the canary’s credibility.

Hidden from UI

Canary entries are marked with the __CANARY__ constant in their encrypted metadata. The vault UI filters out entries containing this marker, so canaries never appear in the user’s password list, search results, or autofill suggestions. They exist only in the encrypted vault data — visible only to someone who decrypts the raw database.
Because canaries are hidden from the UI, they do not clutter the user’s vault or interfere with normal password management. The user can manage their canary configuration from a dedicated settings panel, but individual canary entries are never shown alongside real credentials.

SSRF protection

The webhook dispatch includes strict URL validation to prevent Server-Side Request Forgery:
Allowed: https://hooks.slack.com/services/...  (public HTTPS)
Blocked: http://anything                       (non-HTTPS)
Blocked: https://localhost/...                  (loopback)
Blocked: https://10.0.0.1/...                  (private RFC 1918)
Blocked: https://192.168.1.1/...               (private RFC 1918)
Blocked: https://[::1]/...                     (IPv6 loopback)
Blocked: https://[fd00::1]/...                 (IPv6 ULA)

Detection vs. prevention

Vault canaries are a detection mechanism. They do not prevent an attacker from accessing real credentials — they alert you that your vault has been compromised and credentials are being actively used. This enables rapid response: change passwords, revoke sessions, enable additional security measures.

Best practices

  • Do not delete canaries — they cost nothing and provide passive monitoring
  • Configure a webhook to Slack, PagerDuty, or a similar alerting service for real-time notification
  • Use the default high-value templates — attackers prioritize banking and crypto credentials
  • Treat any canary trigger as a confirmed breach — immediately rotate all passwords and review device sessions

API reference

MethodEndpointDescription
GET/api/canary/alert?t={honeypot_id}Canary trigger endpoint (unauthenticated, public)
Canary management (create, list, configure webhooks) is handled through the vault settings UI. The only public-facing endpoint is the alert trigger, which must remain unauthenticated to function as intended.