Most services that go to production go to production missing the basic security controls. Not because the team disagrees with the controls, but because the work of adding them is annoying, and there is always a feature that feels more urgent. The result is a tier of common vulnerabilities that nobody designed in but that exists in the codebase by default: secrets committed to git history, security headers missing from responses, CORS configured permissively because the team got tired of debugging the strict version, rate limits absent because the service has not been hit hard enough yet to need them, input validation present in some routes and absent in others. None of these are subtle bugs. They are the items on every security checklist, and they are the items that produce the kind of incident report that ends with someone explaining to a customer why their data was exposed.
The work of hardening is not glamorous. It is wiring an auth middleware in front of every protected route, attaching a validator to every request body, configuring rate limits per endpoint and per identity, setting up the right security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options), tightening CORS to the actual allowed origins, and migrating secrets from environment variables and code into a real secrets manager. Each item on that list is a few hours of focused work. The list adds up to a week, and the week never gets scheduled because every individual item feels small enough to defer. The /warden-harden skill exists to do that week of work in one pass: it produces the hardening spec for the service in front of it and implements the controls so the service is not relying on goodwill to stay secure.
Why generalist AI does not harden services
When you ask Cursor or ChatGPT to add a feature, you get the feature. The feature might or might not include input validation; it almost certainly does not include rate limiting; it definitely does not include the security headers on the response; it has nothing to do with CORS or secrets management. The reason is the prompt. The prompt asked for the feature, the model produced the feature, and the security controls that should wrap the feature are not in scope. This is not the model's fault. It is the wrong tool for the job. Hardening is a service-level concern, not a feature-level concern, and asking for it one feature at a time produces the spotty coverage every audit eventually catches.
The other failure mode is the controls themselves. When generalist tools do produce security controls, the controls are usually the most generic version possible. Rate limiting is "100 requests per minute" with no thought to which endpoints actually need stricter limits (login, password reset, signup) and which can run at higher limits (read-only public endpoints). CORS gets * because that is the version that does not produce confusing errors in development. Security headers get the boilerplate values from a tutorial, often with a CSP that is default-src * because the tighter version broke some inline script. The controls technically exist; they do not actually protect the application. /warden-harden is built for the calibrated version of every control, not the generic one.
What hardening actually requires
A hardened service has six layers of control, each calibrated to the service. Authentication: a middleware that validates the credential on every protected route, with explicit decisions about which routes are public and why. Input validation: a schema validator on every route that accepts user input, rejecting malformed payloads before they reach business logic. Rate limiting: per-endpoint and per-identity limits, with stricter limits on sensitive routes (login, password reset, signup) and looser limits on read-only ones. Security headers: CSP that allows what the application actually uses and blocks everything else, HSTS for HTTPS enforcement, X-Frame-Options for clickjacking, X-Content-Type-Options for MIME sniffing. CORS: a tight allowlist of origins the application actually serves, with a separate development origin if needed. Secrets management: every secret in a manager (AWS Secrets Manager, GCP Secret Manager, Vault, 1Password) with rotation cadence documented, never in code, never in environment variables in plain text.
Each layer needs the calibration step. Generic rate limits do not protect login. Generic CSP does not protect against the actual XSS vectors in the app. Generic CORS leaks data to any origin. The discipline of hardening is the discipline of going through each layer, looking at the specific service, and configuring the layer for what this service actually does. A senior security engineer can do this in a focused day per service. The reason it usually does not happen is the senior security engineer is a scarce resource. /warden-harden is built to do that day's work as a routine pass on every service that needs it.
How /warden-harden works
Step one: read the service
Before adding any controls, /warden-harden reads the service to understand what it is. Which routes are public, which are authenticated, which require additional scopes. Which endpoints accept user input and what shapes. Which external origins the service serves. Which secrets the service uses and where they currently live. The read is the input to the calibration: the rate limits will be different for a public API than for an internal admin tool, the CSP will be different for a marketing page than for an SPA dashboard, and the auth middleware needs to know which routes are intentionally public so it does not accidentally lock them down.
Step two: produce the hardening spec
Before any code is written, /warden-harden produces the hardening spec: a structured document that lists every control to be added, the configuration for each, and the reasoning. Auth middleware: which routes it covers, which it skips, which credential format. Validators: which routes get them, which schema library, what the rejection response looks like. Rate limits: per-route limits with the stricter ones called out (login at 5/min per IP, signup at 3/min per IP, read endpoints at 100/min). Security headers: the CSP with explicit sources, HSTS with the right max-age, the rest of the header set. CORS: the allowed origin list, the allowed methods, the allowed headers, whether credentials are allowed. Secrets: the full list, with the recommended manager and the migration plan from current location.
Step three: implement the controls
Once the spec is approved, /warden-harden implements each control. The auth middleware lands as a single file with explicit configuration of the public routes. The validators land as schema files paired with the route handlers. The rate limits land in the existing middleware stack with per-route configuration. The security headers land in a single configuration file or middleware. The CORS configuration replaces the current permissive setup. The secrets migration is staged: the secrets are loaded from the manager, the code stops referencing the environment variables, the environment variables are removed in a separate deploy. Each implementation is small; the cumulative effect is the service that passes its next audit.
Step four: verification
After implementation, /warden-harden runs verification. The auth middleware is tested against a sample of unauthenticated and authenticated requests. The validators are tested against malformed payloads to confirm rejection. The rate limits are tested with burst traffic to confirm the lock-out kicks in. The security headers are checked against the production response. The CORS configuration is tested with disallowed origins. The secrets are checked to confirm none remain in code or in environment variables. Verification is what separates a hardening spec from hardening that actually works in the deployed service.
Permissive CORS (Access-Control-Allow-Origin: *) combined with Access-Control-Allow-Credentials: true is invalid per spec but accepted by some servers, and produces a leaked-cookie vulnerability when it is. /warden-harden refuses to produce that combination and surfaces the override decision if a service genuinely needs the wildcard origin.
Tonone's /warden-harden skill produces a hardening spec and implements security controls (auth, validation, rate limits, headers, CORS, secrets manager) calibrated to the service.
When to use /warden-harden, and when not to
/warden-harden is the right call when a service is being exposed to the public internet for the first time, when a security audit has produced a list of missing controls, or when an existing service is handling sensitive data without security as a first-class concern. The signal is when the team can name controls that should exist and cannot point to where they are configured. The skill is also the right call after a /warden-threat model has identified the mitigations; it implements the mitigations the model called for.
Skip the skill for services that are genuinely internal-only and unreachable from the internet (a sidecar that talks only to the local API, a CLI tool with no network surface). For those, a lighter approach with /warden-audit confirms the assumption and surfaces any actual exposure. For services that need IAM design from scratch (roles, policies, service accounts), /warden-iam is the right call.
| Capability | Tonone | Generalist chatbot | Cursor / Copilot |
|---|---|---|---|
| Reads service before configuring controls | Yes, route inventory and exposure analysis | Generic config snippets | Suggests within current line |
| Calibrated rate limits per endpoint | Yes, stricter on login/signup/reset | One global limit | Not in scope |
| CSP scoped to actual application sources | Yes, explicit allowed sources | Often default-src * | Not in scope |
| CORS tightened to actual origins | Yes, explicit allowlist | Often * for convenience | Not in scope |
| Secrets migration to a manager | Yes, staged migration with verification | Not in scope | Not in scope |
A worked example: hardening a public API
Suppose the brief is: harden the public REST API before it goes to a paying customer. Run /warden-harden against the service and the spec output looks like the following abbreviated form.
auth:
middleware: bearer-token validation
public_routes:
- GET /health
- GET /version
reasoning: only health checks and version are public; everything else
requires a valid customer token.
validation:
library: zod
applied_to: every POST/PUT/PATCH route
reject_with: 400 with { error: { code: 'invalid_request', details: [...] } }
rate_limits:
default: 100 requests/minute per token
POST /auth/login: 5/minute per IP, 10/minute per email
POST /auth/password/reset: 3/minute per IP, 5/hour per email
POST /auth/signup: 3/minute per IP
POST /webhooks/*: 1000/minute per IP (for webhook providers)
reasoning: stricter on auth endpoints to slow credential stuffing
and reset abuse.
security_headers:
Content-Security-Policy: |
default-src 'self';
script-src 'self' 'sha256-...';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.stripe.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
cors:
allowed_origins:
- https://app.example.com
- https://staging.app.example.com
allowed_methods: [GET, POST, PUT, PATCH, DELETE]
allowed_headers: [Authorization, Content-Type, X-Idempotency-Key]
expose_headers: [X-Request-Id]
allow_credentials: true
max_age: 600
secrets:
current_locations:
- .env (DATABASE_URL, STRIPE_SECRET, JWT_SIGNING_KEY)
target: AWS Secrets Manager (existing)
migration:
1. Add boot-time loader from Secrets Manager.
2. Deploy with both env vars and Secrets Manager loader (dual-read).
3. Remove env vars from deployment configuration.
4. Verify service still boots.The spec is reviewable in one sitting. Once approved, /warden-harden implements each section and runs verification against the deployed service. The whole pass takes a fraction of the time it would take a human security engineer working through the same checklist by hand.
Related skills
/warden-harden implements the controls. Before that, /warden-threat produces the threat model that justifies which controls need which calibration. After hardening lands, /warden-audit runs the recurring scan that confirms nothing has regressed. For IAM design, /warden-iam handles roles, policies, and service accounts.
Install
/warden-harden ships with the Warden agent in the Tonone for Claude Code package. Install Tonone, invoke /warden-harden from any Claude Code session inside the repository, and the skill produces a hardening spec calibrated to the service.
1. Add to marketplace
2. Install Warden
Hardening is the work that does not show up in feature demos and shows up in postmortems when it is missing. The skill is built so the work is cheap enough to apply on every service before it goes to production, not as a quarterly cleanup.
Frequently asked questions
- What does /warden-harden do?
- It produces a hardening spec for a service and implements the controls: authentication middleware, input validation, rate limits, security headers, CORS configuration, and secrets manager migration. Each control is calibrated to the specific service rather than generic.
- How is /warden-harden different from a generalist AI adding security?
- A generalist produces generic config that often includes unsafe defaults. /warden-harden reads the service, surfaces the calibration decisions in a reviewable spec, and implements the controls scoped to the actual routes, origins, and secrets in use.
- When should I use /warden-harden?
- When a service is going to the public internet for the first time, when a security audit has produced a list of missing controls, or when an existing service is handling sensitive data and was built without security as a first-class concern.
- What controls does /warden-harden implement?
- Auth middleware, input validation, per-endpoint rate limits, security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy), CORS, and secrets migration to a manager. The list adapts to what the service needs.
- Does /warden-harden support different secrets managers?
- Yes. AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, 1Password Secrets Automation, and Doppler are supported. The migration plan adapts to whichever the project already uses or plans to use.
- How do I install /warden-harden?
- Install Tonone for Claude Code via the get-started guide at tonone.ai/get-started. /warden-harden ships with the Warden agent and is invoked as a slash command in any Claude Code session. Tonone is free and MIT-licensed.
- Is /warden-harden free?
- Yes. The skill is part of Tonone, which is MIT-licensed. The only cost is Claude Code token usage during the work.
- Does /warden-harden break existing functionality?
- The spec is reviewable before any code lands, and verification runs after implementation to confirm the controls do not regress existing behavior. Public routes that should remain public are surfaced explicitly so they are not accidentally locked down.