Shield turns away the visitors you choose: by country, from your static sites, server apps, functions and worker.js programs; by address or range, and AI crawlers that say who they are, from your static sites and functions. It keeps the files beside your pages that should stay private from ever being served, can put a static site behind a password (paid plans), counts what its rules catch on static sites and functions, and checks how the workspace itself is set up. Network-level attack protection runs in front of every site, app and function and needs no setting.
The rules are one setting for the whole workspace. Saving them puts them on every live static site at once, and Apply now puts them on any site a save could not reach. A server app reads the country rules live: a change reaches it within about a minute, with no redeploy. Functions and worker.js programs get them on their next deploy. Turning Shield off reaches each of them the same way.
Changed on 2026-10-11: country rules used to reach static sites only. Since then a workspace that blocks countries refuses them on its server apps too (they took effect within about a minute, with no redeploy), and on each function and worker.js program from its next deploy. The rules are one setting for the whole workspace, so an app cannot be left open to a country its sites block: take the country off the list, or keep that app in another workspace.
In plain words
Protection for your websites and apps. Keep out visitors from countries you choose, or put a site behind a password. Private files next to your pages are never shown.
For example: A shop that only delivers in its own country turns away visitors from the countries it never sells to.
What each part covers
| Part | What it does | Where |
|---|---|---|
| Country rules | A visitor from a blocked country gets 403 "Not available in your region". On a static site the check runs before the cache, so a cached page never gets past it; on a server app it runs before the app is woken, so a blocked visitor never reaches it; in front of a function or a worker.js program it runs before the code, which does not run for them. | Static sites (at their harakumo.app address and on their own domains), server apps, functions and worker.js programs |
| Address lists | An address or range on the deny list gets 403, whatever else is true. One on the allow list is let past the country rules and the AI-crawler rule. | Static sites and functions |
| AI-crawler blocking | AI crawlers and AI assistants' fetchers that say who they are in their user agent get 403. One that pretends to be a browser is not caught. | Static sites and functions |
| Log-only mode | Counts what the rules above would have refused and lets it through, so a rule can be checked before it blocks anyone. A site password still applies. | Static sites and functions |
| What the rules caught | Each site and function counts what it refused, or what log-only mode let through, and sends the counts a moment later; Dashboard → Shield shows them per site, function and rule for the last 7 or 30 days. They are close counts, not exact ones. | Static sites and functions |
| Site passwords | A static site can be put behind one password (paid plans). A visitor sees the password page before anything else, and the right password lets them in on that site for seven days. Only a salted hash is kept, sealed; the password itself is never stored or answered. | Static sites only |
| File guard | Dotfiles, .env files and private keys (.pem, .key, id_rsa and the like) are left out when the site deploys, with a line in the deploy log, and a request for one answers 404. /.well-known/ stays public. | Static sites |
| HTTPS redirect and security headers | Plain HTTP is redirected to HTTPS, and answers are sent with security headers. | Static sites, server apps, functions and worker.js programs |
| Network-level attack protection | Always on, because every Harakumo address is served through the edge network. There is no setting for it, and no numbers are shown for it. | Every site, app and function |
| Security checks | A check of the workspace's own records: two-factor, API keys, domains, roles and more (below). | The workspace |
Country rules reach static sites (at once on a save), server apps (within about a minute, no redeploy), functions and worker.js programs (on their next deploy): a server app's deploy log and a worker.js program's name the countries they refuse. The address lists, the AI-crawler rule, log-only mode and the counts work on static sites and functions; the file guard and site passwords on static sites only.
Settings
| Setting | Meaning |
|---|---|
enabled | Whether the rules are applied (default on). Turned off, a site blocks nobody from that save or the next Apply now, a server app within about a minute, and a function or a worker.js program from its next deploy. A site password stays in force. |
blockedCountries | Two-letter country codes whose visitors are refused, such as ["KP"] (default none). The dashboard takes them comma separated. |
denyIps | Addresses and ranges that are always refused, such as ["203.0.113.7", "2001:db8::/32"] (default none). |
allowIps | Addresses and ranges let past the country rules and the AI-crawler rule (default none). An address on both lists is refused: the deny list wins. |
blockAiCrawlers | Whether AI crawlers that say who they are in their user agent are refused (default off). |
logOnly | Count what the rules would refuse and let it through (default off). A site password still applies. |
passwords | Set or remove a static site's password: { "passwords": { "<site>": "<password>" } } sets one, and null in place of the password removes it (a paid plan to set one, any plan to remove it). It is never read back: the answers name the protected sites (protectedSites) and say whether this plan may set one (passwordsAllowed). |
A country code that is not a country, an address that is not an address or a range, and a setting Shield does not have are refused with 400, and nothing is stored. Check each code before you save it: the United Kingdom is GB, not UK. XX stands for visitors whose country is unknown and T1 for the Tor network.
Where to change them
- Dashboard → Shield. Every member can see the settings and what the rules caught; an owner or admin changes them.
GET /api/services/shieldandPUT /api/services/shield(the body is merged over the saved settings);POST /api/services/shieldis Apply now.- An owner's or admin's AI assistant, with
get_service_configandupdate_service_configfor the serviceshield.
A save puts the rules on your live static sites at once and says which it reached (appliedNote); Apply now puts them on any site a save could not reach. A server app reads the country rules within about a minute of a save, with no redeploy. Functions, and worker.js programs (the country rules), get them on their next deploy, and a save names the functions still running the rules from before. A static site's deploy log names the countries it blocks (Shield: blocking KP); a server app's and a worker.js program's say which they refuse as they go live.
Security checks
Dashboard → Shield also checks the workspace itself, measured from its own records each time you look: a score out of 100, what each check found, and the changes made in the workspace per hour over the last 24 hours (UTC). Every member can see them. The same checks come from GET /api/security/overview, the SDK's hk.security.overview() and the AI connector's security_overview tool.
| Check | What it looks at | In the score |
|---|---|---|
| Owners and admins with two-factor | Whether every owner and admin signs in with two-factor. | Yes |
| Full-access API keys | Active keys that are neither read-only nor limited to one project. | Yes |
| Oldest active key | How old the oldest key that has not been revoked is. | Yes |
| Never-used keys | Active keys that have never made a call. Revoke the ones nobody needs. | Yes |
| Domain verification | Custom domains that are not verified yet. | Yes |
| Owners and admins | How many members hold a role that can change everything. | Yes |
| Environment variables | How many are stored. Anyone with developer access to a project can read its variables. Shown, not scored: storing them is normal. | No |
| End-user sign-in lockouts | Users of your apps locked out after repeated failed sign-ins. | Yes |
| Password hashing | How Harakumo stores members' passwords. Shown, not scored: it is not something you set. | No |
| Sign-in for your apps' users | Whether your apps' users can add a second factor. It is not available yet, so it is shown, not scored. | No |
| Audit activity | Changes recorded in the audit log over the last 7 days. | No |
| API key activity | Which keys were used in the last 24 hours, and the data calls counted today and yesterday (UTC). | No |
Not yet
- No rate limits per site and no bot challenges yet.
- No managed rule sets of the kind a web application firewall has.
- Rules are one setting for the whole workspace, not per project.
- Server apps and worker.js programs get the country rules only: the address lists, the AI-crawler rule, log-only mode and the counts do not reach them yet. The file guard and site passwords are for static sites only.
- Functions get a change on their next deploy, not at once; so do worker.js programs.
Reference
const { score, checks } = await hk.security.overview(); // the workspace's security checks
// Shield's settings have no SDK method yet: use REST or the AI connector.GET /api/services/shield · PUT /api/services/shield { "enabled": true, "blockedCountries": ["KP"], "denyIps": ["203.0.113.0/24"], "blockAiCrawlers": true, "logOnly": false }
PUT /api/services/shield { "passwords": { "<site>": "<password>" } } # null in place of the password removes it
POST /api/services/shield # Apply now: the saved rules on every live static site
GET /api/services/shield/blocks?days=7 # what the rules caught (7 or 30 days)
GET /api/security/overview # the workspace's security checks