The projects page on this site lists fourteen projects, and seven of them carry a clickable address. So I publish seven open doors: one is this blog, six are applications a stranger can walk into.
Say somebody landed on one of those addresses and found something — a form accepting more than it should. That person wants to tell me. Where do they write?
I asked my own infrastructure that question today. Instead of guessing the answer, I went and checked: the same request sent to each of the seven addresses, the files that came back read line by line, and the contact addresses inside them tested at the DNS level for whether they can actually receive mail.
Three addresses had a door. One of those three has no delivery path behind it. And the standard I had followed says, in its own words, that this case may be worse than having nothing at all.
The bad part is that the audit did not stop there. I found the same pattern — the sign is up, the thing it points at does not work — in four more layers.
Seven addresses, three doors
There is an agreed-upon place on the internet for vulnerability reports: /.well-known/security.txt at the root of a domain. RFC 9116 defines it. I requested that path from all seven addresses, and here is what came back around midday on 11 November 2026:
| Address I publish | /.well-known/security.txt |
|---|---|
| burncpu.com | 200 |
| spamkalkani.com | 200 |
| hrmarge.com | 200 |
| hesapciyiz.com | 404 |
| islistesi.com | 404 |
| gercekveri.com | 404 |
| mustafaerbay.com.tr | 404 |
Three out of seven. The part of this table that bothers me is not the number of 404s but the bottom row. mustafaerbay.com.tr is the site that lists the other six. If somebody gets curious about any of these projects and tries to reach the person behind them, this is the first place they land, and there is no reporting door here.
The gap between the three and the four is not ignorance either. I got it right three times. I know how it is done, I have done it on three separate domains, and on four I have not. When those two sentences sit next to each other, the question stops being "how do you do this" and becomes "why did it not spread."
What is behind the door
I read all three files that came back. At first glance they were better than I expected:
# burncpu.com Contact: mailto:mustafa@mustafaerbay.com.tr Expires: 2027-07-14T00:00:00Z Canonical: https://burncpu.com/.well-known/security.txt Policy: https://github.com/merbay-erp/burncpu/security/policy Preferred-Languages: tr, en RFC 9116 makes two fields mandatory. For Contact the text is unambiguous: this field must always be present in a security.txt file. For Expires the requirement is just as firm — it must always be present and must not appear more than once. The standard also recommends that the value sit less than a year in the future so the file does not go stale.
All three files have both mandatory fields in place. The expiry dates are 2027-07-14 for burncpu, 2027-05-05 for spamkalkani and 2027-07-11 for hrmarge — roughly six to eight months out from today, so all valid and all inside the one-year window the standard recommends. All three are served over HTTPS as text/plain, both of which the RFC requires.
I could have stopped here and written "three projects have a reporting channel." It would have been wrong, because what I had checked was only the two mandatory fields. I had not looked at the fields the standard merely recommends. When I did, all three turned out to have no Encryption field — although the RFC recommends encryption when Contact is an email address. None of the three files is signed either; the standard separately recommends an OpenPGP signature.
A valid file, an undeliverable address
The three Contact values belong to three different domains. For an email address to receive anything, the domain needs an MX record — or, failing that, its address record has to speak SMTP on port 25. That fallback is called implicit MX and it comes from RFC 5321.
I checked all three in turn:
Contact address | MX record | Delivery path |
|---|---|---|
| mustafa@mustafaerbay.com.tr | mail.sysroot.app | yes |
| destek@spamkalkani.com | mail.sysroot.app | yes |
| destek@hrmarge.com | none | not found |
hrmarge.com publishes no MX record at all. I asked three separate resolvers (1.1.1.1, 8.8.8.8, 9.9.9.9) and all three came back empty; the domain is not NXDOMAIN, it resolves cleanly on Cloudflare nameservers. So I fell through to implicit MX: the A records point at two Cloudflare addresses (104.21.95.108 and 172.67.144.162), and port 25 is closed on both from here.
That measurement needs a control, because plenty of ISPs block outbound port 25 — if mine did, every address would look "closed" and the test would say nothing. So I connected from the same machine to my own mail server, 137.74.47.57 behind mail.sysroot.app: port 25 open. The closure is not on my side.
The domain does carry an SPF record, v=spf1 a:mail.sysroot.app -all. That misled me for a moment, because my mail server's name appears in it. But SPF governs the sending side: it says who may send mail on behalf of that domain. It has nothing to do with where incoming mail is delivered.
Let me state the limit of the measurement too. I did not send a test message to that address. What this check covers is the delivery path at the DNS level; I derived the existence of the mailbox from that. But a stranger's report would travel the same path, and if the path is closed, what is at the end of it does not matter.
The verdict the RFC gives this case
Before interpreting the finding I went back to the standard, because RFC 9116's security considerations have a section on exactly this: incorrect or stale information. The text says that if the information in the file is not kept up to date, security reports either never arrive or go to the wrong contacts. Then comes this: not having a security.txt file may be preferable to having stale information in it.
It follows that with four instructions to organizations: the information in this file and the resources it references — web pages, email addresses, telephone numbers — should be kept current, be accessible, be controlled by the organization, and be kept secure.
After reading that I re-read my own table and the number changed. I have three addresses that publish a security.txt, and two that can receive a report. The third is a picture of a door: standards-compliant, unexpired, correct in form, empty behind. For a researcher that is worse than a 404. A 404 says "there is no channel here, look for another way." destek@hrmarge.com says write the message, send it, consider the job done. And I, having received nothing, assume there is nothing wrong.
The worst box in that diagram is not the one at the bottom right. The worst part is that the path leading to it returns a 200.
What is at the end of the Policy line
I was about to stop here, pleased with the lesson, when I noticed I had not asked the same question of the file's other lines. Policy points at an address too, and I had only checked whether it opened. All three opened.
Reading what opened broke the table again. The Policy line on spamkalkani.com goes to /privacy.html and from there to the /privacy page. That page is titled "Gizlilik Politikası — Spam Kalkanı" — a privacy policy — and contains no section on vulnerability reporting at all; searches for "vulnerability" and "disclosure" come back empty. So a researcher who follows the Policy line finds a privacy notice, not a reporting policy. HTTP 200, irrelevant content.
The Policy line on burncpu.com goes to the security policy on GitHub, and that file is genuinely good: a scope list, response-time targets, a safe-harbour clause, a clear email address. It also contains this instruction — if possible, encrypt the report with the maintainer's key, available at keys.openpgp.org.
I went looking for that key. keys.openpgp.org returns 404 for the address in question, and for all three of my address variants. To make sure I was not testing it wrong, I asked for a key I knew existed: security@torproject.org returns 200 from the same endpoint. The query method works. The key is not there.
That makes three instances of the pattern. File present, address dead. File present, policy irrelevant. Policy present, key missing.
The name on the address is a contract too
The Contact values in my three files begin with mustafa@, destek@ and destek@. None of them is security@.
When RFC 9116 defines the Contact field it says security email addresses should follow the conventions in section 4 of RFC 2142. That section is a short table: ABUSE for customer relations, NOC for network infrastructure, and SECURITY for network security — security bulletins or queries. A convention that has been sitting there since 1997.
This is a recommendation, not a requirement. But here is why it earns its keep: a security@ box is bound to a role rather than a person. All three of my addresses go either to me or to a general support box. When the first goes on holiday, or the second becomes a queue full of password-reset requests, a security report gets lost in the same pile. A role address does not fix that by itself, but it separates it — and you can write a filter against a queue that has been separated.
The same pattern on the repository side
If a stranger cannot get through the site, they look at where the code lives. My merbay-erp account has eight public repositories. I asked each one whether it has a SECURITY.md — in the root, under .github/ and under docs/, since GitHub reads all three. Three have one: burncpu, CORTEX-Private-Intelligence-Framework and zee. Five do not.
This morning I would have written "three out of eight" and moved on. But since the whole point of this piece is not settling for the presence of a file, I opened all three and got caught by my own thesis.
burncpu/SECURITY.md gives an address: mustafa@mustafaerbay.com.tr. An address with a delivery path. That file does its job.
CORTEX-Private-Intelligence-Framework/SECURITY.md directs the researcher to GitHub's "Report a vulnerability" button, and if that is unavailable, to contact the maintainer through their GitHub profile. Both routes are shut: that button depends on private vulnerability reporting, which is disabled on all eight of my repositories, and my GitHub profile publishes no email address.
zee/SECURITY.md opens with "Depo özeldir" — the repository is private — except the repository is public; the API reports visibility: public. It offers the security advisory form as the reporting route, which somebody without repository access cannot use while private vulnerability reporting is off. And the file's own sentence closes the matter: no new contact address is allocated by this document.
So on the repository side there are three files, and one of them gives a stranger a working route.
As for the front door
On this site itself there is no SECURITY.md in the repository, no public/.well-known/ directory, and no contact page — neither iletisim nor contact under src/pages/.
There is a gap in the structured data too. The file I audited in the piece where I wrote my identity into four registries, src/lib/profile.ts, is the source of everything this site says about me. Line twenty-one reads:
email: undefined as string | undefined, The field is declared and has no value. I pulled the schema.org person record the homepage emits and checked it live: there really is no email key. I publish no machine-readable contact information at all.
On the human side there are addresses, and rather too many. The privacy page carries hello@mustafaerbay.com.tr as a clickable mailto: link. The subscription result pages carry mustafa@mustafaerbay.com. burncpu.com's security.txt carries mustafa@mustafaerbay.com.tr. Three variants across two domains; both domains have MX records pointing at the same server of mine, so all three have an open delivery path.
There is also an easter egg I print to the console, and in Turkish it reads: "If you are poking around here, write to me, let's have a coffee." I wrote a line that says exactly the right thing to somebody poking around, back in May. The trouble is that none of these addresses sits where a researcher would look: nobody hunts for a reporting channel inside a privacy notice or in the browser console. They look at /.well-known/security.txt.
Six signs, three of them working
At the end of the audit I counted. I publish six documents that tell a stranger where to write: three security.txt files and three SECURITY.md files. The test is simple — can a stranger who reads that document, with no access to the repository, reach me?
| Document | Gives a stranger a working route |
|---|---|
burncpu.com security.txt | yes |
spamkalkani.com security.txt | yes |
hrmarge.com security.txt | no — the address has no delivery path |
burncpu SECURITY.md | yes |
CORTEX SECURITY.md | no — a disabled button and an empty profile |
zee SECURITY.md | no — allocates no address |
Three out of six. Exactly half.
What bothers me about that number is that all six would pass a formal check. They all exist, they are all in the right place, they all return 200. If I wrote a scanner, it would mark all six green. The wall a stranger hits starts one layer past the existence of the file — and until today I had never looked at that layer.
Why it did not spread
There is no position here from which I can defend myself, but there is one from which I can understand it. I wrote a security.txt for three projects because the subject was on my mind while setting them up, and not for four because it was not. There was nothing in between: no checklist, no template I run when opening a new project, no audit asking whether this domain's reporting channel works. If you apply a practice project by project, it is not a habit; it is a coincidence.
The irritating part is learning afterwards that the propagation mechanism was within reach the whole time. If an account has a public repository named .github, GitHub uses the SECURITY.md inside it as the default for every repository that lacks its own — whatever the target repository's own visibility. The file can sit in the root, in the .github folder or in the docs folder; a repository's own file wins. So I do not need five files for five repositories, I need one. That repository does not exist yet either: merbay-erp/.github returns 404.
That convenience does not cover this blog, though, because the blog's repository lives under a different account — itwise-bilisim/mustafaerbay. The default file works per account, so that is a separate repository and a separate commit. On the site side there is no shortcut at all: every domain has to serve its own security.txt.
It is instructive to look at how this gets institutionalised in the public sector. The binding operational directive CISA issued in 2020 requires federal agencies to develop and publish a vulnerability disclosure policy — and in a specific place: at the /vulnerability-disclosure-policy path of the agency's primary .gov website, in plain text or HTML. It mentions security.txt too, noting that its use is not required under the directive but that it can help some people work out who to share findings with. Even there the mechanism is not "everyone remember": fixed path, fixed format, deadline.
From here
What I take from this audit is not a checklist but a change of habit: after setting up a channel, walk it once from the other side. Requesting the file is not enough — you have to ask whether the address inside it has a delivery path. A 200 on the Policy line is not enough — the page that opens has to actually be a reporting policy. Writing "encrypt it with my key" is not enough — the key has to be published. All three were steps I skipped, and one question catches all three: what does the stranger reading this line do next, and will what they do work?
This site has a history of leaving open items for its readers; in the piece where I audited my own promises, eleven of the thirteen commitments I could check were still open. So I will keep the list short. Three items, all measurable:
- For
hrmarge.comI will either build the delivery path or change the address in itssecurity.txtto one that has a delivery path; if neither happens, the RFC's own verdict says the right move is to remove the file. Whichever it is, I will not call it done before a real test message reaches that address. - I will add a public
.githubrepository to themerbay-erpaccount with a singleSECURITY.mdinside it; that closes the gap for five repositories.CORTEXandzeeneed separate fixes, since their current files point at a disabled button: either I enable private vulnerability reporting or I put a working address in the files. - I will put a
security.txtat the root of this site withsecurity@mustafaerbay.com.tras theContact. Before writing the file I will create the mailbox and test it with a real message, and I will setExpiresunder a year with a renewal reminder — otherwise today's file becomes next year's dead door.
The piece where I found that none of my seventeen workflows run when a PR opens arrived somewhere similar: there the missing piece was a trigger, here it is a mailbox. What they share is me looking at a configuration and calling it done. Configuration shows what you intended; whether it works is something you only learn by walking it from the other side.
Official Sources
- RFC 9116 — A File Format to Aid in Security Vulnerability Disclosure — the format of
security.txt; the mandatoryContactandExpiresfields (2.5.3, 2.5.5), the signature andEncryptionrecommendations (2.3, 2.5.4), and the ruling that stale information may be worse than no file (5.3). - RFC 2142 — Mailbox Names for Common Services, Roles and Functions — section 4, which defines the
SECURITYrole mailbox. - RFC 5321 — Simple Mail Transfer Protocol — section 5.1: with no MX record, the address record is treated as an implicit MX.
- GitHub Docs — Configuring private vulnerability reporting for a repository — enabling the structured reporting form on public repositories.
- GitHub Docs — Creating a default community health file — how a
SECURITY.mdin the.githubrepository becomes the default for other repositories. - CISA — BOD 20-01: Develop and Publish a Vulnerability Disclosure Policy — the fixed path and format required of federal agencies, and where
security.txtsits in the directive.
