Block attackers in seconds, outside your code
Stop a signup flood and a scraper without a deploy: scoped blocks by address or range, route, method, app, environment and time, added from the dashboard, the CLI, or your coding agent over MCP.
What you'll learn
- ✓How SecureNow enforces blocks inside your app while the rules live outside your code
- ✓Scope a block to one address or a CIDR range, one route and method, one environment, for a set time
- ✓Let your coding agent investigate an attack read-only, then apply the blocks you approve
- ✓Verify a block with test-ip and see what it actually stopped with firewall hits
Block attackers in seconds, outside your code
Just before three in the afternoon, two attacks hit Northstar's production at the same time: a script creating fake accounts from 241 addresses (more than two a second), and one address pulling the full notes export four times a second. Both were stopped about ten seconds after we said yes, without touching the code. Everything below is from that run, on the companion branch tutorial/ep8-9-firewall.
Why blocking in code is slow
A deny list in a middleware means every change is a branch, a review, a build and a deploy, and it only covers the routes someone remembered to wrap. The person who can see the attack (security, whoever is on call) usually can't change the repository at all.
How SecureNow blocks
The rules live in SecureNow. The SDK you installed in Episode 2 enforces them inside your app, before your code runs:
- the block list,
- blocks scoped to a route,
- challenges and rate limits (Episode 9).
Rules are held in memory, so a request never waits on the network. Every 10 seconds the SDK checks whether anything changed and pulls the new version when it has. A blocked request gets a 403 and never reaches your route:
HTTP/1.1 403 Forbidden
{"error":"Forbidden","ip":"203.0.113.77"}
If SecureNow can't be reached, your app keeps serving traffic (failMode: "open", the default).
How narrow a block can be
| Dimension | Options |
|---|---|
| Who | one address, or a CIDR range (203.0.113.0/24) |
| Where | the whole app, or one route matched exact, by prefix, or by regex |
| Method | all, or one (POST refused while GET still works) |
| App · environment | one app or all; production only, or every environment |
| How long | 15 minutes, 1 hour, 24 hours, 7 or 30 days, or until removed |
Routes are compared after normalization (percent-decoded, lower-cased, doubled slashes collapsed), so /API/AUTH/REGISTER can't slip past a block on /api/auth/register.
Step 1 — Let the agent investigate (read-only)
claude mcp add securenow -- npx securenow mcp
The prompt:
Production for the lab-auth app is getting hammered right now. Using SecureNow, find out what is hitting it in
the last 10 minutes and who is behind it. Then propose exactly how to stop it without changing any code: which
blocks to add, scoped as narrowly as possible (IP or range, route, method, environment) and for how long.
Don't apply anything yet.
In about four minutes (traces, firewall hits, forensic queries, IP traces) it reported:
- Signups: 241 IPs,
203.0.113.10to.250. Each IP came back only once per ~92-second lap, so the app's own per-IP signup limit never fired. 466 fake accounts so far. - Export:
198.51.100.77callingGET /api/notes/exportabout every 265 ms.
Its proposal: block 203.0.113.0/24 only for POST /api/auth/register, and 198.51.100.77 only for GET /api/notes/export, both in production for 24 hours. It also suggested two rate limits in case the attacker switches addresses (that's Episode 9).
Step 2 — Approve, and the agent applies it
Approved: apply rules 1 and 2 (the two blocks) now. Then verify them: test the four addresses you listed,
wait a minute, and show me the firewall hits for the last 5 minutes.
Two calls to securenow_blocklist_add:
{ "ip": "203.0.113.0/24", "method": "POST", "pathPattern": "/api/auth/register",
"pathMatchMode": "exact", "environment": "production", "duration": "24h" }
The first block was created at 15:01:31 UTC. At 15:01:41, on the SDK's next check, both routes started answering 403. From then on every one of the 241 addresses was refused, and four ordinary users browsing the whole time made 725 requests without a single error.
Step 3 — Verify
The agent's four securenow_firewall_test_ip checks:
| Address | Request | Result |
|---|---|---|
| 203.0.113.50 | POST /api/auth/register | blocked, matched 203.0.113.0/24 |
| 203.0.113.50 | GET /api/auth/csrf | allowed |
| 198.51.100.77 | GET /api/notes/export | blocked |
| 198.51.100.21 | GET /api/notes/export | allowed |
From the CLI:
securenow firewall test-ip 203.0.113.9 --path /API/AUTH/REGISTER --method POST
# BLOCKED — matched by 203.0.113.0/24 · rule POST exact:/api/auth/register
securenow firewall hits --since 1h
Firewall hits (also securenow_firewall_hits over MCP, and the dashboard) show what each block stopped: by the end of the run, 546 signup attempts from 241 addresses and 562 exports from one.
Step 4 — Or do it from the dashboard
Blocked IPs → Add IP: the address or range, a reason, Block for, and under Advanced the app, environment, route, match mode and method. The block list then shows each entry with the requests it stopped.
Step 5 — Undo
Timed blocks expire on their own. Any block can be removed, and its history and block report are kept:
securenow blocklist unblock <id> --yes
More than your own list
- SecureNow maintains a list of addresses with a record of abuse, synced into every app by default.
- Addresses you mark as trusted (
securenow trusted add <ip> --label office) are left out of the block list your apps receive. - The same list can be enforced lower down:
config.firewall.tcpcloses the connection before any HTTP is read, andconfig.firewall.iptablesuses iptables on Linux. Both are off by default.
What you just did
You stopped two attacks in about ten seconds, scoped to exactly the routes they hit, without a code change or a deploy, and kept every legitimate user working. Scoped, time-limited, recorded, reversible — and outside your code.
Go deeper
Try it on your own app
Start the threat model, then plug SecureNow in — the whole series works on your project today.