Agents › Arm at scale
bulk tripwires

Monitor decoys across your records. Register them in batches.

For teams managing many customer records, register decoys (fake credentials) for alerts when our monitored decryption service sees them. A tripwire is that registered monitor. The deny-sh tripwires arm-bulk command registers batches and exports a CSV, a table you can import into your database. Configured alerts can be delivered when registered fingerprints are observed at /api/decrypt.

How registration works

Use the online API, a code-library helper (SDK), or the command-line tool (CLI). Generate a decoy that looks like a real credential of the same type, hash it, register the hash, hand back the decoy. Link the decoy to the corresponding record. Keep real credentials inaccessible to agents that only need the decoy.

Worked example: 250k Stripe rows

You run a B2B SaaS. Every customer row in your DB has a real Stripe secret in stripe_key_real. You add a stripe_key_decoy column. Expose only the decoy column to an agent that should not read the real key. If both columns leak, the real key is compromised too. The decoy follows a live key’s format (sk_live_ prefix, 107 chars, same charset). The first time it ever shows up at /api/decrypt at a monitored deny.sh endpoint, configured alerts can notify you. Testing it against Stripe does not trigger this monitor.

1. Get a tenant API key

The Team plan includes 10,000 armed tripwires. This 250,000-row example requires an Enterprise contract with a scoped limit.

2. Arm the tripwires

One command. Writes a CSV with every armed decoy + its tripwire id:

# Install the CLI (one-time)
npm install -g deny-sh

# Arm 250k stripe-live-key decoys, save the mapping
DENY_API_KEY=cs_your_tenant_key \
  deny-sh tripwires arm-bulk \
    --type stripe-live-key \
    --count 250000 \
    --label-prefix prod-stripe \
    --out armed.csv

# Throughput depends on your environment and service limits.
# Each row in armed.csv:
#   label,decoy_value,tripwire_id,hash_suffix
#   prod-stripe-0000001,sk_live_decoyAbc123...,42891,7b9c4e5f3a2d

3. Write the decoys back to your DB

Whatever import tool you already use: psql \copy, mysql LOAD DATA, dynamodb batch-write-item, your own import script. Test a small batch and keep real columns access-controlled. The tripwire_id column stays on your side as a reference; you can pass it to deny-sh tripwires test <id> if you ever want to fire one synthetically to test your alert pipeline.

4. Connect your alert channels

Tripwire events route through /dashboard/webhooks, same as every other audit event. Configure Datadog API key, PagerDuty integration key, or Slack webhook URL once. The event shape is decoy.tripwire.triggered with the matched label, the tenant id, the timestamp, the request IP, and the request user-agent. When the decoy is observed at /api/decrypt, the configured channel receives the alert.

What lands in Slack

The webhook adapter ships a structured payload; the Slack adapter renders it as a block-kit message. Stripped of formatting, the body is:

🚨 Decoy tripwire triggered
label: prod-stripe-0148732
tenant: acme-prod
fired_at: 2026-07-04T14:22:13Z
remote_ip: 198.51.100.42
ua: python-requests/2.31.0
audit_chain: ✓ signed, receipt RFC-3161
investigate: https://deny.sh/dashboard/decoy-alerts/42891

A tripwire records an observation, not an attacker’s intent. The decryption path does not return a real-versus-decoy flag; external evidence may still distinguish a fake.

From real DB rows (the arm-from-csv path)

If you'd rather drive arming from an existing export instead of generating fresh decoys with sequential labels, point the CLI at a CSV. It reads one row at a time, arms a decoy for each, writes the result back next to the source id:

# real-rows.csv (one column: customer_id)
# 1042
# 1043
# ...

DENY_API_KEY=cs_your_tenant_key \
  deny-sh tripwires arm-from-csv \
    --type aws-access-key \
    --in real-rows.csv \
    --id-col 0 \
    --skip-header true \
    --label-template "cust-{id}-aws" \
    --out armed.csv

# Output rows match input rows by id, so a Python or psql join
# writes the decoys straight back into the customer table.

Labels link observations to records. An alert does not by itself prove which system leaked a value. Use the event and activity log alongside your own investigation evidence.

Tiers and caps

Each tenant, a separate customer workspace, has its own registration cap:

TierTripwire capPrice
Free10$0
Team10,000$99/mo or $990/yr
EnterpriseBy contractScoped quote

Counts are per-tenant. If you run a multi-tenant platform with one deny.sh tenant per end-customer, each end-customer gets their own cap.

Why this is the alert layer for AI agents

A general-purpose AI agent with database read access, a fetch tool, and a prompt-injection-friendly input surface can be told to read a customer row and POST it somewhere. Conventional database access logs see the row read; they don't see anything wrong because the agent is supposed to read rows. The decoy creates a possible detection signal, not a monitor of every leak. The first time the decoy bytes hit /api/decrypt at a monitored deny.sh endpoint, configured alerts can notify you. Use at another service is outside this monitoring path.

Decoys match supported format checks, such as prefix, length or checksum. They are not working credentials. Labels, surrounding data and checks against the real service can expose a fake; an attacker may never send it to a monitored deny.sh endpoint.

Privacy boundary

For local generation and hash registration, send only the SHA-256 fingerprint (a one-way identifier) and a label. Hosted generation receives a type label. Monitored decryption receives the inputs submitted to it. Keep real secrets out of labels and alert payloads.

Where to go next