Canary Secrets
Fake secrets that trip a silent alarm when read. Detect compromised machines and freeze the project before the attacker gets any further.
A canary secret is a fake secret you plant in a project. It has a real encrypted value and real machine grants -- it looks indistinguishable from any other secret in the API. Legitimate machines never read it. When anyone reads it, SikkerKey treats the read as a compromise signal: the project is frozen before the response returns, and the attacker still receives the canary value so they have no indication the trap triggered.
Canaries are the opposite of a traditional secret. A normal secret is designed to be read often. A canary is designed to be read never. A single read is the signal.
How It Works
- You plant a canary inside a project and grant it to the same machines you grant your real secrets to. Attackers dump everything, so a canary that isn't granted is invisible to them.
- A compromised machine runs
sikkerkey exportorsikkerkey get <canaryId>as part of an attack. - The canary is read. Before the response is built, SikkerKey atomically marks the project as frozen, records a
canary_triggeredaudit entry at critical severity, and dispatches alerts (email + any subscribed webhooks). - The machine receives a normal-looking
200 OKwith the canary's value. The attacker has no indication the trap fired. - Every subsequent request from every machine in the frozen project returns
423 Locked. The vault owner reviews the audit trail, rotates the offending machine, and explicitly unfreezes the project.
Why Canaries Exist
A compromised machine does not politely enumerate one secret at a time. Attackers dump everything they can reach, as fast as they can, before the blue team notices. The fastest path to exfiltration is sikkerkey export, which returns every granted secret in one response.
Canaries exploit that behavior. They turn the attacker's speed advantage into the detection signal.
Creating a Canary
From the secrets page, click + New Secret and select Canary Secret. Only vault owners see this option -- canary management is owner-scoped because it's a defensive control, not a secret-sharing primitive.
The form has:
- Name -- a label you'll recognize later. Pick a name that blends in with your real secrets (e.g.
Stripe Backup Key,Legacy SMTP Password). Don't name itCANARY_DO_NOT_TOUCH. - Arm immediately -- whether the canary fires on read from the moment it's created. Default on. Turn it off if you want to plant a disarmed canary and arm it later.
- Freeze this project -- if a read trips the canary, freeze every secret in this project. Default on.
- Freeze related projects -- also freeze every other project in your vault that the offending machine has been added to. Default off. Use this when you want lateral-movement containment -- one compromised machine loses access to every project it touches.
There is no value field. The server generates a 64-character symbol-charset value at creation and never reveals it to you. This is deliberate: if you never know the value, you can't accidentally commit it, grep for it, or leak it through another path. The canary's purpose is to be read -- the server just needs a ciphertext to return.
Arming and Disarming
A canary can be disarmed without deleting it. When disarmed:
- The secret still exists and machines can still read it
- Reads are still audit-logged
- The freeze triggers do not fire
Disarming is useful while investigating a suspected false positive, or while deliberately rearranging machine grants. The canary is visually flagged in red when disarmed -- the red ambient tells you a trap is off and needs your attention. Green means armed and protecting the project.
To arm or disarm, expand the canary's row on the secrets page and click Arm canary or Disarm canary.
Triggers
Both triggers are independent. You can enable either, both, or neither.
Freeze this project
When on, a read of the canary freezes every secret in the canary's own project. Every subsequent GET /v1/secret/{id}, POST /v1/secrets/export, POST /v1/secrets/list, POST /v1/verify-access, and POST /v1/secrets/poll from any machine in that project returns 423 Locked until the owner unfreezes.
This is the minimum useful containment. The project is the blast radius of any compromised machine inside it.
Freeze related projects
When on, the freeze extends to every other project in your vault that the offending machine has been added to. This is scoped to your own vault -- we never touch a project owned by someone else, even if the attacker's machine has cross-vault team grants.
Use this when a single machine has grants across multiple projects (production, staging, analytics). One canary trip freezes every project the machine could reach, not just the one containing the canary.
Neither trigger enabled
If both toggles are off, the canary still fires. The canary_triggered audit entry is still recorded at critical severity, and alerts are still dispatched. Nothing gets frozen. Use this mode when you want detection without containment -- for example, as a first-touch signal during a measured incident response drill.
What Happens During a Fire
The fire path is synchronous and atomic. The machine's request does not return until:
- The project is marked frozen in the database
canary_triggered(critical) andproject_freeze(critical, one per frozen project) audit entries are written- Alerts are queued for dispatch
Only then does the response go out. The attacker sees a normal successful read with the canary's value. The owner sees a dashboard banner and an email seconds later.
The freeze is deliberately in the request path, not a background job. A background job can be outrun by a second request that completes the attack. An in-transaction freeze cannot.
The Frozen State
While a project is frozen:
| Request | Response |
|---|---|
GET /v1/secret/{id} | 423 Locked |
POST /v1/secrets/list | 423 Locked |
POST /v1/verify-access | 423 Locked |
POST /v1/secrets/export | Project silently skipped, other projects still returned |
POST /v1/secrets/poll | Each watched secret returns status locked |
GET /v1/secrets | Secrets in frozen projects excluded |
The dashboard shows a red pulsing banner on the frozen project's secrets page with the trigger time, the canary ID that tripped, and an Unfreeze project button. The project selector and the overview table both show a Frozen pill next to the project name.
Machines in the frozen project cannot authenticate to fetch any secret, read any field, or verify access. Team members with dashboard access can still read metadata and audit entries, which is how they help investigate.
Unfreezing
Only the vault owner can unfreeze a project. Unfreezing requires confirming that you've reviewed the source of the trigger -- the audit log contains the offending machine ID and source IP, and the expected response is to rotate that machine's identity before letting it back in.
Unfreezing does not disarm the canary. If the same machine reads the canary again after unfreeze, the trap fires again and re-freezes the project. The correct recovery order is:
- Review
canary_triggeredin the audit log for the offending machine ID and source IP - Revoke that machine from the dashboard (or re-bootstrap it with a new identity)
- Unfreeze the project
If you unfreeze without addressing the compromised machine, the freeze immediately re-trips on the next read.
Immutability
Canaries are intentionally inert. Once planted, they cannot be:
- Replaced -- the value is server-generated and cannot be edited. Replacing the value would change the trap out from under machines already granted.
- Rotated -- same reason. The trap must be stable to work.
- Rolled back -- there is no version history to roll to.
- Synced -- canaries cannot be mapped to GitHub Actions, GitLab CI, Bitbucket Pipelines, or any external target. Pushing a canary value to an external system would let an attacker fingerprint which of your secrets are canaries.
- Restored from trash -- deleting a canary permanently destroys its trigger config. Restoring would bring back a canary-shaped secret with no alert chain, which is worse than no canary at all.
If you need a different canary value, delete the existing one and plant a new one.
Machine Access
Canaries use the same machine-grant model as every other secret. A machine reads a canary only if all the standard access conditions are met:
- The machine is registered, approved, and enabled
- The vault owner's account is active
- The machine is added to the canary's project
- The machine has an explicit grant to the specific canary
- The request includes a valid Ed25519 signature
Grant the canary to every machine in the project. A canary granted only to a small subset of machines is a canary that only fires when a subset of machines are compromised.
Audit Events
Canary activity generates these actions in the audit log:
| Action | Severity | When it fires |
|---|---|---|
canary_triggered | critical | A canary was read by a granted machine. The entry includes the machine ID, source IP, and the list of projects that were frozen as a result. |
project_freeze | critical | A project was frozen in response to a canary trigger. One entry per frozen project, so a multi-project freeze produces multiple entries. |
project_unfreeze | high | The vault owner manually cleared a freeze on a project. |
canary_enable | medium | A canary was armed (set to fire on read). |
canary_disable | medium | A canary was disarmed (reads still audited, but no triggers). |
canary_config_update | medium | The trigger configuration (freeze-project, freeze-related) was changed on an existing canary. |
secret_create | info | A canary was planted. Emitted alongside canary_enable when armed-on-create. |
canary_triggered and project_freeze are both at critical severity because they signal an active compromise in progress. Any email or webhook subscription that includes critical events will fire on these.
See the audit reference for the full action catalog.
When to Use Canaries
Use canaries when:
- You want an early-warning signal that a machine has been compromised, before any real damage is done
- You run CI/CD pipelines, agents, or multi-project machines where a single compromise has a wide blast radius
- You want a tripwire that survives your own monitoring being disabled -- a canary fires from inside SikkerKey's request pipeline, not from an external scanner
Canaries are not a substitute for:
- Rotation -- a stale secret is still a leaked secret, canary or not
- Access review -- canaries don't replace auditing machine grants periodically
- 2FA or strong passwords -- canaries protect machine-facing paths, not dashboard access
Don't plant canaries into projects where:
- Machines legitimately call
sikkerkey exportas part of normal operation (the export will trip the canary on every invocation) - You haven't granted the canary to the same machines as the real secrets (the attacker simply won't see it during a dump)
Canaries are low-cost to plant and high-value when they fire. We recommend one canary per project, granted to the same machine set as the project's real secrets.