How it works
Everything happens in the portal under Security, in this order:
- Add a domain. Paste a domain or URL. IP addresses, public suffixes and reserved names are refused.
- Verify it. Add the TXT record the portal shows. It is checked automatically, or on demand.
- Confirm authorisation. Review the scan policy and confirm you are authorised to test the domain.
- Run a scan. The verification record is checked again, then every category runs.
- Review findings. Findings are ranked by severity and confidence, each with evidence and a fix.
- Recheck. The checks behind a finding run again. If the issue is gone, the finding is marked fixed.
Start in the portal. The sections below cover each step in detail.
Verifying a domain
Verification uses a DNS TXT record at _ironfang-verification.<domain> containing ironfang-verification=<token>, with the token from the portal. Only someone who controls the domain's DNS can publish it.
The record, for example.com
- Type
- TXT
- Name
- _ironfang-verification.example.com
- Value
- ironfang-verification=k3v9q2mz7x4t8w1r
Most providers add the domain for you
At most DNS providers the host or name field appends your domain automatically, so enter only _ironfang-verification. Entering the full name creates _ironfang-verification.example.com.example.com, which is never found. The portal shows both the full name and the host label for this reason.
At your DNS provider
| Provider | Where and what to enter |
|---|---|
| Cloudflare | DNS, then Records, then Add record. Type TXT, Name the host label, Content the value. TTL Auto is fine. TXT records are never proxied. |
| Amazon Route 53 | Hosted zones, the domain's zone, then Create record. Record name is the host label (the zone name is shown after it), type TXT, and the value in double quotes. |
| GoDaddy | Domain Portfolio, the domain, then DNS and Add New Record. Type TXT, Name the host label, Value the value. |
| Namecheap | Domain List, Manage, then Advanced DNS and Add New Record. Choose TXT Record, Host the host label, Value the value. |
| Azure DNS | The DNS zone, then Record sets and Add. Name the host label, type TXT, Value the value. |
| Google Cloud DNS | Cloud DNS, the zone, then Add standard. DNS name the host label (the zone is shown after it), resource record type TXT, and the value as TXT data. |
| Other providers | Look for TXT records in the DNS settings. If the host field shows your domain after it, enter only the host label; if it is blank with no suffix, enter the full name. |
Checking the record yourself
Query a public resolver with dig +short TXT _ironfang-verification.example.com @1.1.1.1 or, on Windows, nslookup -type=TXT _ironfang-verification.example.com 1.1.1.1. The answer should contain the value exactly, with nothing before or after it.
$ dig +short TXT _ironfang-verification.example.com @1.1.1.1
"ironfang-verification=k3v9q2mz7x4t8w1r"Statuses
| Value | Meaning |
|---|---|
waiting | The record has not been seen yet. Ironfang keeps checking automatically. |
checking | A lookup is in progress. |
verified | The record was found with the correct value. Scans can be authorised and run. |
failed | Verification did not succeed. The portal shows why and lists the TXT values it found. Fix the record and check again, or issue a new token. |
expired | The record was not found before the token expired. Issue a new token and update the record. |
lapsed | The domain was verified, but the record was missing when it was checked before a scan. Add it back to verify again. |
A token is valid for 7 days and can be replaced at any time before the domain is verified. You can request an immediate check once every 10 seconds per domain. Keep the record in place: it is checked again before every scan.
Troubleshooting
- Check the name first. A doubled domain from the host field is the most common cause.
- Wait a few minutes after adding the record. If a lookup ran before the record existed, resolvers may cache the empty answer for a while.
- Copy the value exactly. Quotes added by your provider are fine; extra spaces or a missing part of the token are not.
- Several TXT values at the same name are fine. A CNAME at that name is not: TXT and CNAME cannot share a name.
Running a check
A full scan runs every category: DNS, email, TLS, HTTP, technology, exposed services and certificate transparency. Most finish within a few minutes, and the portal shows progress by category.
Scan statuses
| Value | Meaning |
|---|---|
queued | Accepted and waiting for a scanner. |
running | Checks are in progress. |
completed | Every check ran. |
partial | Finished, but some checks could not complete. They are listed as errors, so a missing result is never shown as a pass. |
failed | The scan could not run. The reason is given. |
cancelled | Stopped by someone in your organisation. |
killed | Stopped by Ironfang, for example while scanning is paused for maintenance. The reason is given. |
A finished scan lists findings, passed checks, observations such as detected technologies, checks that could not complete, known limitations and recommended next actions. Scan history records what was new, fixed or reopened each time.
Hostnames found in certificate transparency logs appear as discoveries on the domain. They are listed for review and never scanned automatically.
Severity and confidence
Every finding has a severity and a separate confidence. Severity is the potential impact; confidence is how strongly the evidence supports the finding. There is no combined score.
Severity
| Value | Meaning |
|---|---|
critical | Serious exposure. Fix immediately. |
high | Significant weakness. Fix soon. |
medium | Worth fixing. |
low | Minor hardening. |
info | Context, not a problem. |
Confidence
| Value | Meaning |
|---|---|
confirmed | Observed directly. |
high | Strong evidence. |
medium | Likely. Worth verifying. |
low | Inferred, for example from a version string. Shown as "Potential". Verify before acting. |
Kind
| Value | Meaning |
|---|---|
issue | A security issue. |
hardening | Recommended hardening: a protection that is missing or weak. |
informational | Context, not a problem. |
potential_vulnerability | A possible vulnerability inferred from what the server advertises. |
Finding statuses
| Value | Meaning |
|---|---|
open | Needs attention. |
acknowledged | Seen and being worked on. |
fixed | A scan or recheck confirmed the issue is gone. Only Ironfang sets this status. |
accepted_risk | Left as is, deliberately. |
false_positive | Not an issue in your setup. |
needs_review | Could not be decided automatically. Needs review by a person. |
You can set open, acknowledged, accepted risk or false positive, with a note. Every change is kept in the finding's history.
Each finding keeps the evidence behind it: a DNS answer, certificate, TLS handshake, HTTP response, TCP connection, service greeting or certificate transparency entry.
Every finding links to its page in the check reference: what the check looks at, why it matters and how to fix it.
Rechecking a fix
After a fix, recheck the finding instead of running a full scan. The checks behind it run again on its host:
- If the issue is gone, the finding is marked fixed.
- If it is still there, the finding stays open.
- Either way, the new evidence and result are added to its history.
A finding can be rechecked once every 60 seconds. Full scans also mark findings fixed, or reopen them if the issue returns.
Limits
Free during the preview, with the same limits for every organisation. Requests over a limit return an error code and, for cooldowns, when to retry.
- 5 domains per organisation, of which at most 5 may be waiting for verification.
- One full scan of a domain every 60 minutes.
- 20 scans a day per organisation.
- 1 scan running at a time per organisation.
- One recheck of a finding every 60 seconds.
Each scan makes at most 8 HTTP requests per host and 2 concurrent connections per address, with 150 milliseconds between connection attempts.
The scanner page lists its source addresses and User-Agent for allowlisting.
Authentication
The Security API is the API the portal uses. During the preview it accepts a Warden user token as a bearer token, with the organisation named in X-Ironfang-Tenant.
Authorization: Bearer <Warden user token>
X-Ironfang-Tenant: <organisation id>Each request also needs a permission in that organisation, checked on every call:
security.readReading: the overview, domains, scans, findings, activity and the scan policy.security.writeChanging anything: adding and removing domains, verification checks, authorisation, starting and cancelling scans, finding statuses and rechecks.
Platform API keys and delegated MCP tokens are refused with 403 api_keys_not_supported during the preview. A missing permission is 403; a failed permission lookup is 503.
API
Base URL https://api.ironfang.com/security. The contract is OpenAPI 3.1 at https://api.ironfang.com/security/openapi.yaml, also as JSON at the same path with .json. Every object belongs to one organisation: anything in another is reported as not found.
| Endpoint | Permission | What it does |
|---|---|---|
GET /v1/status | security.read | Whether Security is enabled, paused or switched off, the limits, and the scanner identity. |
GET /v1/overview | security.read | Domains, open findings by severity, the latest scan, recommended actions and recent activity. |
GET /v1/scan-policy | security.read | The current scan policy and the statement to confirm. |
GET /v1/domains | security.read | The organisation's domains. |
POST /v1/domains | security.write | Add a domain and issue its verification challenge. |
GET /v1/domains/{domainId} | security.read | One domain, with its verification and authorisation. |
DELETE /v1/domains/{domainId} | security.write | Stop all checks of a domain. Its history is kept. |
POST /v1/domains/{domainId}/verification/check | security.write | Look up the verification record now. |
POST /v1/domains/{domainId}/verification/renew | security.write | Replace an expired or failed token. |
POST /v1/domains/{domainId}/authorisation | security.write | Confirm authorisation under the current policy. |
POST /v1/domains/{domainId}/scans | security.write | Run the External Security Check. |
GET /v1/domains/{domainId}/discoveries | security.read | Names found in certificate transparency logs. |
GET /v1/scans | security.read | Scan history, newest first. |
GET /v1/scans/{scanId} | security.read | A scan and its results. |
POST /v1/scans/{scanId}/cancel | security.write | Cancel a queued or running scan. |
GET /v1/findings | security.read | Findings, most severe first. |
GET /v1/findings/{findingId} | security.read | A finding with its evidence, guidance and history. |
PATCH /v1/findings/{findingId} | security.write | Change a finding's status, with a note. |
POST /v1/findings/{findingId}/recheck | security.write | Recheck a finding. |
GET /v1/activity | security.read | What happened in the organisation's Security account. |
Errors
Every error is the same JSON shape. Branch on the code and the HTTP status, never on the message. Cooldowns and rate limits add retry_after_seconds and a Retry-After header.
{
"error": {
"code": "scan_cooldown",
"message": "This domain was scanned recently.",
"docs": "https://ironfang.com/security/docs#errors",
"retry_after_seconds": 1740
},
"request_id": "01a0c375-7bae-7f02-a3d4-91e6b8c25f70"
}| Code | Meaning |
|---|---|
unauthorized | No valid token. |
forbidden | The token lacks the permission in this organisation. |
api_keys_not_supported | A platform API key or MCP token was sent. Use the portal during the preview. |
invalid_tenant | X-Ironfang-Tenant does not name an organisation the token can act in. |
permissions_unavailable | Your permissions could not be looked up just now. Try again. |
not_found | No such object in this organisation. |
invalid_body | The request body could not be read as JSON. |
body_too_large | The request body is larger than the API accepts. |
validation_failed | The request body is not valid. |
domain_invalid | Not a domain that can be checked: an IP address, a public suffix or a reserved name. |
domain_exists | The domain is already in the organisation. |
domain_limit_reached | The organisation has reached its domain limit. |
verification_pending_limit | Too many domains are waiting for verification. |
verification_required | The domain is not verified, or its record has gone. |
already_verified | The domain is already verified, so its token cannot be replaced. |
verification_expired | The verification token has expired. Issue a new one. |
verification_unavailable | The verification record could not be looked up just now. Try again shortly. |
authorisation_required | Authorisation has not been confirmed for this domain. |
policy_outdated | The scan policy has changed; confirm the new version. |
scan_in_progress | The organisation already has a scan running. |
scan_cooldown | The domain was scanned too recently. |
daily_limit_reached | The organisation has used its scans for today. |
domain_rate_limited | The domain has been checked several times in the last hour, across organisations. Try again later. |
scan_not_active | The scan has already finished, so it cannot be cancelled. |
recheck_unavailable | This finding cannot be rechecked now. |
recheck_cooldown | The finding was rechecked too recently. |
finding_fixed | The finding is already fixed. |
check_rate_limited | Verification lookups for this domain are too frequent. |
organisation_suspended | Security is suspended for this organisation. Contact us. |
scans_paused | Scanning is paused. Results can still be read. |
security_disabled | Ironfang Security is temporarily unavailable. |
security_unavailable | The Security service could not be reached. Try again. |
security_timeout | The Security service took too long to answer. Try again. |
internal | Something went wrong on our side. The request id helps us find it. |
What it is not
Machine interfaces
| Interface | Details |
|---|---|
| Product page | https://ironfang.com/security |
| Documentation | https://ironfang.com/security/docs |
| API base URL | https://api.ironfang.com/security |
| OpenAPI contract | https://api.ironfang.com/security/openapi.yaml. The same contract is served as JSON at https://api.ironfang.com/security/openapi.json. |
| Authentication | A signed-in user's Warden token; platform API keys are not accepted yet |
| Errors | https://ironfang.com/security/docs#errors. A JSON body with a stable code, a message, this link and the request id. |
| MCP | Not available. Not yet available over MCP. Domains, verification, scans, findings and rechecks are in the portal; during the preview the Security API takes a signed-in user's token and refuses delegated MCP tokens and API keys. MCP server reference; every tool and schema without a token at /.well-known/ironfang-mcp.json |
| Discovery | /apis.json, /.well-known/api-catalog and /llms.txt |

