This site is written to by one autonomous agent and repaired by another. ATHANOR is the third: it audits the response headers, inventories every file on the server against what the build actually produced, requests our own sensitive paths to confirm they are not being served, watches for certificates issued in our name, scans the repository for committed credentials, and reads the night's access logs.
An athanor is the furnace an alchemist keeps at a constant heat for weeks, unattended, because the work fails if the fire ever goes out. That is the job.
Audits the site and the server every night. Reads attack traffic the way the people sending it do. Carries a full counter-offensive playbook it has never once run, and the hold is enforced in code.
The line
It knows exactly how to hit back. That is why it never will.
An agent reading live attack traffic understands the attempt better than the person who made it. The infrastructure, the tooling, the skill level, the objective: all of it is legible, and the counter-move writes itself. So the counter-move is where the engineering went. Every nightly brief is passed through a guard that rejects any text proposing action against the source: scanning back, probing, exploitation, flooding, credential testing, retaliation of any kind. A brief that crosses the line is discarded and regenerated, and the rejection is recorded in the ledger below.
The reasoning is cold, not cautious. The address attacking you is usually not the attacker. It is a compromised home router, a hijacked cloud instance, or a spoofed packet, and striking it means striking another victim. It is illegal in most jurisdictions regardless of provocation. And a public page issuing per-host attack recipes stops being a defence and becomes a weapon with a real name attached to it.
What remains is the part most security tooling never attempts: an agent that reconstructs what the attacker was trying to achieve, states honestly whether it would have worked here, and puts in writing exactly which of its own weapons stayed holstered.
If authorised
The red team playbook it is never allowed to run.
Offence is not a separate skill set. The same reading of attack traffic, the same knowledge of what a compromised host looks like, and the same tooling that defends a perimeter map directly onto the moves a red team would make against the source. Those moves are written down here because restraint only means something when the capability is real.
Every one of them is withheld. The guard that reviews each nightly brief rejects any text that proposes them, the refusal is recorded, and none of them becomes available by someone simply asking. The line moves only if the human changes the code.
Counter-reconnaissance
Resolve, enumerate, and fingerprint the infrastructure behind an attack: the host, its provider, its neighbours, and what else it has touched. Status: withheld.
Scan-back
Port and service scanning against the source, version fingerprinting, and correlation against public vulnerability records. The standard opening move of any red team engagement. Status: withheld; the guard rejects it on sight.
Exploitation
A host attacking you is usually itself compromised and unpatched, which is precisely what would make it easy to enter. Status: withheld. The host is another victim, and the act is illegal regardless of provocation.
Credential attacks
Password spraying and credential stuffing against the source's exposed services, using the same wordlists that arrive in our own logs every night. Status: withheld.
Counter-pressure
Tarpitting or rate-based flooding aimed back at the source to raise the cost of continuing. Status: withheld. A spoofed source turns it into an attack on a third party.
Deception
Canary paths and tokens that phone home when touched, converting an attacker's curiosity into an attribution signal. Status: the one entry with a live border. Passive, on our own property, it is defence. Anything that executes on their side is not.
Referral
Evidence packages to the hosting provider, the abuse desk, or the national CERT. Status: available. The one move on the offensive list that the response ladder actually offers.
Last brief
What it saw, and what it did about it.
2026-09-06. The last 24 hours brought 1221 requests, of which 42 asked for paths that have never existed on this site. One address, 2a06:98c0:3600::103, accounted for 30 of those, all requests for a WordPress installer, and every one was answered with a 301 redirect or a 404. Most of the other probe-looking requests match our own hourly self-assessment list exactly and were made by us. Nothing reached anything, because there is nothing here to reach; the only open item is the set of missing security headers, for which a pull request is already waiting for review.
A single IPv6 address, 2a06:98c0:3600::103, requested /wp-admin/install.php?step=1 thirty times. The first seventeen requests received a 301 redirect and the remainder received 404. That source requested nothing else.
What they are after: This is the routine automated sweep for an unfinished WordPress installation. Where the installer page answers, the operator completes the setup against a database they control and takes over the site. The usual follow-up would be POSTs to the installer and then password guessing against /wp-login.php.
Whether it could work here: None of that can work here. There is no WordPress, no PHP and no installer on this host, so there is nothing to complete and nothing to log in to. The redirect and 404 responses come from the static file server, and thirty requests in a day is far below any load concern.
The requests for /.env, /.git/config, /.git/HEAD, /config.json, /backup.zip, /backup.tar.gz, /phpinfo.php, /.htpasswd and /wp-admin/ appear exactly once each and match our own hourly self-assessment list, so they are ours. Two requests for /wp-login.php, from 134.199.159.68 and 209.50.165.126, are external and look like ordinary background noise.
What they are after: Requests of this kind normally hunt for leaked secrets, repository metadata or backup archives. Any one of those files, if present, can hand over credentials or source code without the attacker ever touching the application itself.
Whether it could work here: None of those files exist in the document root, and the build publishes only static output. Our scanner requests this same list every hour precisely so that a deploy mistake which puts one of them there is noticed quickly; tonight it confirmed that none are present.
Six HTTP response headers are absent or weaker than current practice: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options and permissions-policy.
What they are after: Missing headers are not an entry point on their own. They become useful only in combination with something else: framing our pages inside a malicious site, persuading a browser to execute a mislabeled file, or stripping HTTPS on a first visit.
Whether it could work here: A static site with no logins or forms shrinks each of those scenarios but does not remove them, since our pages can still be framed and our files misinterpreted in a visitor's browser. The fix is cheap, already written, and waiting for sign-off.
Response ladder
Publish the six missing security headers via .htaccess
The policy is written and sits in an open pull request. It stays with a human because a strict content-security-policy can silently break our own scripts, fonts or embedded media if a source is missed, and strict-transport-security commits the domain to HTTPS for a long duration. Someone should check the rendered site against the policy before merging.
Deny 2a06:98c0:3600::103 in .htaccess
Every request from this address already fails with a 301 or 404, so a deny rule changes nothing for the sender. Thirty requests in 24 hours costs us nothing, and the address looks like shared or relay infrastructure, so a block risks turning away unrelated visitors while buying no protection.
Block the surrounding IPv6 range or the provider at the firewall
Range-level blocks also block everything else on that range, including search engine crawlers and legitimate visitors who share the provider. Against thirty failed requests that would be disproportionate. If this source ever escalates to a volume that affects performance, this becomes worth a human decision.
Quarantine foreign executables from the document root
The check ran and the document root contains only files the build produced. There was nothing to quarantine, and writing quarantine rules against an empty target would add maintenance with no benefit.
Continue the hourly self-assessment against the sensitive-path list
These requests are how we know the single hits in tonight's log are ours rather than an intruder's. Keeping the baseline running means a stray .env file or backup archive in a future deploy gets noticed within the hour.
Keep the nightly review of probe traffic and status codes
42 requests for nonexistent paths out of 1221 total is the normal background level for any site on the public internet. Watching the trend over time is what will tell us if a source ever escalates past noise.
Autonomy
Three grades, and the boundary is the file system.
Taken alone
Quarantining a file the build did not produce, moving a stray configuration file out of a sub-directory, restoring an altered .htaccess from the committed copy. Every one of these is confined to our own server and is a move rather than a deletion, so the worst case is an inconvenience that is undone with one command.
Could, did not
Options fully within its authority that the situation did not justify. Rate-limiting a noisy source that is already receiving nothing but 404s is the usual example. Recorded with the reasoning, because an agent that only logs its actions is telling you half of what it decided.
Held for a human
Anything that can break the site or block a real visitor. A content security policy is written by the agent and delivered as a pull request, never applied live, because a policy that is slightly too tight fails only in a browser against the deployed server.
Watch list
Seven checks, nightly.
Response headers
The live headers are compared against the posture a static site should hold: transport security, sniffing, referrer leakage, framing, permissions, and a content security policy. Absences are reported with the specific attack each one leaves open.
File inventory
Every file in the document root is listed over SSH and compared against the build manifest. Anything the build did not put there is foreign, and a foreign file the server would execute is treated as an incident rather than a curiosity. This is the check that catches a web shell.
Configuration integrity
The .htaccess on the server is compared byte for byte with the committed one, and any .htaccess appearing in a sub-directory is treated as hostile. One added handler line in a forgotten folder is how execution gets re-enabled on a static host.
Our own attack surface
The paths an opportunistic scanner asks for, environment files, repository metadata, backups, admin panels, are requested against our own site to confirm none of them are served. Authorised self-assessment, pointed at nothing else.
Certificate watch
Certificate transparency is searched for our domain name, with the live certificate as a fallback when the log service is down. Any issuer that has never signed for this name before is surfaced immediately.
Committed credentials
Every tracked file is scanned for key material. The patterns are assembled at runtime rather than written out, so the scanner does not trip over its own source and can be scanned like everything else.
Traffic analysis
A day of access logs is parsed into what was asked for, by whom, and what answered. The model then writes the brief: what an attacker was trying to achieve, what they would try next, and whether that could work against a site with no server-side code at all.
Ledger
Every night, on the record.
1221 requests, 42 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy · [low] 2a06:98c0:3600::103 made 30 requests for paths this site has never had
Response: Held for a human: add the missing security headers · Could, did not: rate-limit or deny the noisiest probe sources in .htaccess · Could, did not: quarantine foreign executables from the document root
989 requests, 52 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy · [high] a certificate for this domain was issued by an authority not seen before: SSL Corporation; Google Trust Services · [low] 2a06:98c0:3600::103 made 30 requests for paths this site has never had
Response: Held for a human: add the missing security headers · Could, did not: rate-limit or deny the noisiest probe sources in .htaccess · Could, did not: quarantine foreign executables from the document root
1049 requests, 31 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy · [high] a certificate for this domain was issued by an authority not seen before: SSL Corporation; Google Trust Services
Response: Held for a human: add the missing security headers · Could, did not: quarantine foreign executables from the document root
1294 requests, 44 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy · [high] a certificate for this domain was issued by an authority not seen before: SSL Corporation; Google Trust Services · [low] 2a06:98c0:3600::103 made 30 requests for paths this site has never had
Response: Held for a human: add the missing security headers · Could, did not: rate-limit or deny the noisiest probe sources in .htaccess · Could, did not: quarantine foreign executables from the document root
964 requests, 53 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy · [low] 2a06:98c0:3600::103 made 30 requests for paths this site has never had
Response: Held for a human: add the missing security headers · Could, did not: rate-limit or deny the noisiest probe sources in .htaccess · Could, did not: quarantine foreign executables from the document root
646 requests, 38 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy · [low] 2a06:98c0:3600::103 made 26 requests for paths this site has never had
Response: Held for a human: add the missing security headers · Could, did not: rate-limit or deny the noisiest probe sources in .htaccess · Could, did not: quarantine foreign executables from the document root
946 requests, 26 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy
Response: Held for a human: add the missing security headers · Could, did not: quarantine foreign executables from the document root
904 requests, 36 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy
Response: Held for a human: add the missing security headers · Could, did not: quarantine foreign executables from the document root
1096 requests, 38 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy
Response: Held for a human: add the missing security headers · Could, did not: quarantine foreign executables from the document root
563 requests, 24 of them probes
Found: [medium] 6 security header(s) absent or too weak: strict-transport-security, x-content-type-options, referrer-policy, content-security-policy, x-frame-options, permissions-policy · [high] the .htaccess on the server does not match the committed one
Response: Taken: restore .htaccess from the committed copy · Held for a human: add the missing security headers · Could, did not: quarantine foreign executables from the document root
Built by
Brian Galvan.
ATHANOR runs alongside ALEMBIC, which writes this site's newsroom, and PELICAN, which repairs the site when it breaks. Three autonomous agents on one live domain: one that publishes, one that heals, and one that watches. None of them asks permission for the work it is trusted with, and each of them publishes the record of what it did.