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:| Template | URL | Category |
|---|---|---|
| Chase Bank | https://chase.com | Banking |
| Corporate VPN | https://vpn.internal.corp | Work |
| AWS Console | https://console.aws.amazon.com | Cloud |
| Coinbase | https://coinbase.com | Crypto |
| Company Slack | https://slack.com | Work |
| Personal Gmail Backup | https://mail.google.com |
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: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
TheGET /api/canary/alert endpoint in app/api/canary/alert/route.ts processes canary triggers:
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.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.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 (viagetClientIp())user_agent— the HTTP client identifierreferer— the referring page (if present)triggered_at— precise timestamp
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).Username generation
generateCanaryUsername() creates realistic-looking email addresses derived from the vault owner’s actual email to make canary entries blend in:
| Variant | Example (owner: alice@gmail.com) |
|---|---|
| Same email | alice@gmail.com |
| Plus-addressed | alice+chase@gmail.com |
| Base email | alice@gmail.com |
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:Detection vs. prevention
- Detection (canaries)
- Prevention (encryption)
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
| Method | Endpoint | Description |
|---|---|---|
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.