Skip to content
ATON
ChangelogThe same work, as one number.

Written for you.
Not for us.

Nobody should have to read commit messages to follow a project. Every change here carries an entry in plain sentences, and the check that runs on every merge refuses to let one through without it.

Three questions each time. What changed, why it was worth doing, and what it means for the project. Published exactly as written, including the entries that are about something going wrong.

Sunday, August 30, 2026

52 changes
fixedstatusplan 19

The form sent people nowhere

What changed
Leaving your address on the status page now takes you to a page confirming it. It was sending people to an address that does not exist outside our own servers.
Why
The redirect was built from the address the app sees itself running on, which inside a container is not the address anybody types. It looked correct in the code and produced a link to a machine nobody can reach.
What it means
Worth saying plainly because of where it was. This is the form on the page people are sent to when something else is already broken, so the failure would have arrived at exactly the worst moment: something goes wrong, we send you somewhere to be told when it is fixed, and that page throws you into a dead end. It was found by filling the form in on the live site rather than by reading the code, which is the only way this kind of thing turns up. The code was fine. The assumption underneath it was not.
changedinfraplan 12, 19

The failover is built and waiting on one permission

What changed
The piece that sends visitors to the maintenance page when a site is down is written, uploaded and ready. It is not switched on yet, because the key we use for our DNS provider is allowed to read the setting it needs and not to change it.
What it means
The toggle is a script rather than a note, because switching this on changes how encryption terminates for a live address, and that is not a thing to do by hand at two in the morning. It turns on with one command and off with the same one, and reads the result back afterwards rather than trusting that the change took.
fixedstatusplan 19

A health check that answers

What changed
The status page reports its own health immediately instead of waiting on its database first.
Why
The first deploy was rejected because the container "did not respond to health checks". It was responding. It was politely waiting on a database connection it was not yet allowed to make, and the platform gives that check one second before concluding the container is dead.
What it means
Beyond fixing the deploy, this is the right shape for this page in particular. Its most important job is showing the maintenance notice when other things are broken, and that needs no database at all. A health check that failed whenever the database was unreachable would pull the page out of service at exactly the moment somebody needed it to tell them something was wrong. The database is still checked and still reported. It just gets a short deadline and its answer is information rather than a vote.
addedinfraplan 12, 19

Work that reports it happened

What changed
The deposit watcher and the scanning machine now tell the status page each time they finish a pass.
Why
Neither of them answers requests. They wake up, do a piece of work, and go back to sleep. There is nothing to knock on, and asking the machine whether it is switched on answers the wrong question entirely. The failure that matters is not the machine being off. It is the work quietly stopping while everything around it looks fine. A payment watcher that has not run since Tuesday and a payment watcher that has run every two minutes and found nothing look exactly alike from the outside, and one of them means somebody paid and got nothing.
What it means
They report only after the work actually succeeded, not merely after the process started. Something failing every two minutes would otherwise send a perfectly healthy signal on every attempt, which is worse than no signal at all: it would be an alarm system reporting all clear because it was ringing. The status page watches for the silence rather than for a message, so nothing has to notice the failure and decide to raise it. Not hearing anything is the alarm.
fixedstatusplan 19

The build needed something to copy

What changed
The status page now ships a logo and a robots file, which also fixes a build that worked here and failed everywhere else.
Why
The container copies the public folder into the image. That folder was empty, and git does not track empty folders, so it existed on the machine the build was first tried on and nowhere else. The build passed locally and failed the moment it ran anywhere it had not been hand-prepared.
What it means
Worth writing down because the failure was informative rather than annoying. A build that only passes on the machine it was written on is not a build, and the only reason this surfaced within minutes is that it goes through the same pipeline as everything else rather than being deployed by hand. The robots file lets search engines find the page on purpose. Somebody searching for whether we are down should land here rather than on somebody else's forum thread.
addedstatusplan 12

The page and the checker have to agree

What changed
A test that fails if a service is listed on the status page but nothing is actually checking it.
Why
The list of what the page shows and the list of what gets probed live in two files, because the checker runs without a build step and the page does not. That is a sensible split and exactly the kind that rots quietly: add a service to one, forget the other, and the page lists something nobody is watching. It would show as "not checked yet" forever, which looks like a deliberate state rather than a mistake. Nobody would go looking.
What it means
The test was checked by breaking it on purpose: adding a service the checker does not watch, confirming it failed and named the service, then putting it back. A test nobody has watched fail is a test nobody should trust. It also refuses to let an internal address be written into either file. Those come from the environment, and a hostname typed in by hand would end up in the repository, which is the thing the whole arrangement avoids.
addedinfraplan 12, 19

A way back when a site is down

What changed
When one of our sites stops answering, visitors are now sent to a maintenance page that explains what is happening and offers to email them once it is fixed, instead of a browser error.
Why
A browser error tells somebody nothing except that something went wrong, and leaves them unsure whether it was them, their connection, or us. Worse, it gives them nothing to do except keep trying.
What it means
Only page loads are redirected. A form submission or an API call that received a redirect to another address would lose what it was carrying, and the caller would see something far more confusing than the error it would otherwise have got. Those are told plainly instead. A page that is merely missing is left alone. A 404 is a working site saying a page does not exist, and dressing that up as maintenance would be a lie.
addedsiteplan 12, 19

A status page, and it is on the tracker

What changed
The build tracker has a new item under Foundation: a public status page, in progress.
Why
Until now the only way to find out whether something was broken was to ask us. The tracker item exists because the page is real work with its own repository and its own deploy, and anything that size belongs on the tracker rather than appearing one day with no history.
What it means
The status page reads this site's published build state, so anything not built yet is listed there with the state the tracker gives it. There is one place that decides whether something exists, and it is the repository that owns it. A status page making its own judgement would eventually disagree with the tracker, and then one of the two would be wrong with nothing to say which.
addedstatusplan 12, 19

A page that says whether we are up

What changed
There is now a status page. It says whether each part of Atheron is working, checks everything once a minute, and remembers what it found so you can see whether a problem is new or has been going on all afternoon.
Why
Until now, the answer to "is it me or is it them" was to ask us and wait. That is a bad answer at the best of times and a worse one at two in the morning.
What it means
Two things are watched differently. Anything that answers a request gets asked once a minute, and is only called down after three failures in a row, because one failed request is usually a dropped packet and a page that goes red over that is a page nobody believes. Work that runs on a schedule rather than answering requests reports in instead, and the page watches for the silence. A probe cannot tell the difference between a payment watcher that ran and found nothing and one that has not run since Tuesday, and the second is the failure that costs somebody their order.
fixedinfraplan 12

A pass should survive a blip

What changed
The scanning machine now reconnects to its database instead of holding one connection open for the whole of a twenty minute pass.
Why
It lost a pass tonight. Seventeen minutes of real work finished, and then the write at the end failed because the connection had been broken while it ran, so every result from that pass went with it. The thing that broke it was a firewall being applied to that machine, which is a one-off. The fragility was not. A managed database closes connections that have been idle, and a pass is mostly idle from the database's point of view: it writes at the start and the end and spends the middle running code. Sooner or later that ends a pass on its own.
What it means
The connection is now made when it is needed and made again if it has gone. The one place that writes inside a transaction gets its own connection, because a reconnect halfway through a transaction would commit nothing while the earlier half was rolled back, and the stored inputs would quietly empty themselves. Worth noting that this failed loudly rather than quietly. The pass was marked failed, and the server check would have reported it within hours.
changedspecplan 2

The starting consensus parameters are locked

What changed
The baseline is marked done on the build tracker: 8 blocks per second, an anticone constraint of 40, sized against 1.5 seconds of propagation delay and breaking at 1.94.
addedinfraplan 12

Continuous fuzzing is running

What changed
The continuous fuzzing item on the build tracker is done. It has moved from a design note to a machine that has been running all evening and has already found something.
addedinfraplan 12, 18

Serious findings now come and find you

What changed
When the hourly scanning finds something high or critical, it says so in Discord straight away instead of waiting on the dashboard for somebody to look.
Why
A dashboard is a place you have to decide to visit. The whole point of a machine that runs every hour is catching the thing nobody was looking for, and something serious found at two in the morning should not sit until the next time a tab gets opened. Only high and critical go out. Everything quieter stays on the dashboard, where it belongs, for the same reason the alerts are sent once rather than hourly: a channel that reports everything is a channel people stop reading.
What it means
The machine posts through a channel webhook rather than the bot itself. That machine spends all day running invented input against code it has never seen, so what it holds should be worth as little as possible to whoever might take it. A webhook can post to one channel and read nothing. The bot could read the server and hand out roles, and would buy us nothing extra here. The channel it posts into should be one only the team can read: a finding quotes our code and describes how to reproduce a problem that is not fixed yet.
addedportalplan 12

A finding gets said once

What changed
The scanning now records when it has announced a finding, so a serious one gets raised the moment it is found and then never repeated.
Why
Findings collapse onto a single entry and are seen again on every pass. Announcing whatever is open would have meant the same handful arriving every hour until somebody dealt with them. That is how a channel gets muted, and a muted channel is worse than no channel at all: nobody decides to stop paying attention, they just quietly stop, and there is nothing afterwards to show it happened.
What it means
Marked only when the message genuinely went out. If the announcement fails the entry keeps its place in the queue and goes out on the next pass, because an alert that silently marks itself delivered is the exact failure it exists to prevent.
changedinfraplan 12

What automatic delivery may and may not replace

What changed
Written down: pushing a change to the scanning machine's own scripts does not deploy it. Somebody copies it across on purpose.
Why
Automatic delivery gives that machine a fresh copy of our source after every push. That is data, and the machine only reads it. Letting the same path replace the scripts the machine runs would be handing it the ability to run anything as root, which is the opposite of what the arrangement is for: this is the machine that spends all day running input nobody trusts, so it should have as little reach as possible.
What it means
Adding a new check still needs nothing but a new file, because those travel with the source. Only the machinery itself is copied by hand, and it is a short command.
changedinfraplan 12

Findings that close themselves

What changed
When a server check stops reporting a problem, that problem now closes itself on the dashboard instead of waiting for somebody to tick it off.
Why
The first server check found sixty-nine security patches waiting on one machine. Applying them took a minute. Clearing the entry would have taken a person going and clicking it, and until they did, the dashboard would have carried on saying the machine was behind. That is how a list stops being read. Once most of what is on it was fixed days ago, the entries that still matter are buried among the ones that do not, and the whole thing becomes something people scroll past.
What it means
Only entries the server check raised, and only for the machine it just looked at, and never over the top of a decision somebody made by hand. It closes what it can no longer see, and nothing else.
fixedinfraplan 12

A check that was not checking

What changed
The property tests now actually run on the scanning machine. They had never run there once.
Why
The machine was asked to run them in a directory with none of the project's dependencies installed, so the command failed instantly every hour. It recorded that it could not run them, which was honest, but a note buried in a run record is not the same as a result and nobody was reading it. That is the exact failure this whole machine exists to catch, so finding it in our own tooling is not a comfortable discovery. A check that cannot run looks identical from a distance to a check that runs and finds nothing.
What it means
Dependencies are installed once and kept between runs, reinstalled only when the lockfile actually changes, so the hourly pass does not pay for it. Twenty-six properties across nine files now run every hour with new inputs each time.
changedportalplan 12

Seeds, and a run list that shows more than an hour

What changed
Fuzzing targets can now ship example inputs alongside them, and the list of recent scanning runs on the internal dashboard shows thirty rather than twelve.
Why
A fuzzer given random bytes spends most of its time discovering that its input has a shape at all. A handful of realistic examples skips that entirely: the wallet target only reaches signature recovery because one of its examples is a signature that genuinely verifies, and without it that whole check was decoration. The run list was written when one pass was one row. A fuzzing pass is five, one per target, so twelve rows had come to mean about an hour, and the server checks and dependency scans dropped off the bottom before anybody saw them.
What it means
Examples are stored as raw bytes, with line ending conversion switched off for that directory. A carriage return in the middle of a signed message makes it a different message, and the signature stops verifying, which would have quietly turned that check back into decoration without anything appearing to break.
fixedinfraplan 12

A push should not interrupt a scan

What changed
The scanning machine now takes its own copy of the source at the start of each pass and works from that, rather than reading the copy that new pushes replace.
Why
The two happened at once within an hour of the automatic delivery being switched on. A push landed four minutes into a twenty minute fuzzing pass, replaced the directory underneath it, and the four checks that had not started yet all failed to build. That failure at least announced itself. The static analysis had the same problem and a worse symptom: the tools would have found their files disappearing mid-read and returned partial results, which look exactly like clean ones. A scanner that quietly examines half of something and reports nothing wrong is worse than one that is switched off, because the switched off one does not tell you it is fine.
What it means
Copying costs about a second per pass. Both passes are now indifferent to what lands while they run.
changedsiteplan 12

The site goes to the fuzzer too

What changed
Every push to the site now hands a copy of the source to the machine that scans it, the same as the portal does.
Why
The scanning machine was reading a copy somebody put there by hand. A copy that is only refreshed when somebody remembers is a copy that drifts, and a scanner reading week old code reports on a week old problem while saying nothing about today's.
What it means
The push happens after the checks pass, so what gets scanned is at least a version that builds. The machine still holds no key to any repository: the source is pushed to it rather than pulled by it, over a key that can do nothing else on arrival.
addedinfraplan 12

The machine that checks the machines

What changed
The fuzzing machine now does two more things. It runs coverage guided fuzzing every hour against the parts of the system that handle money and identity, and it asks every one of our servers what state it is in.
Why
Fuzzing covers the code we wrote. Servers fail differently: nothing crashes, nothing throws, the site stays up, and the box is quietly weeks behind on security patches with a service in a failed state that nobody has looked at. The first time that check ran it found one. A service on the payments machine had been failing every two minutes for thirteen hours and had told nobody, because the thing it does when it works is find nothing, and the thing it does when it is broken also looks like finding nothing.
What it means
The machine holds no key to our source. It runs invented input against code it has never seen, which makes it the last machine that should be able to read anything on its own account, so it cannot: each repository pushes a copy to it after every push, over a key that is pinned to one command and cannot open a shell. That was checked by trying to use it for a shell, for another command, for a path outside the allowed list, and for nothing at all. All four were refused. The same shape works in reverse for the server checks. It asks each machine a fixed question over a key that can only ask that question, so the machine running untrusted input all day cannot touch the one holding payment credentials. Adding to any of this is adding a file. The runner finds new targets on its own.
fixedportalplan 12, 17

The fuzzer found one in nine tries

What changed
A machine now spends every hour throwing invented input at the code that handles money and identity, and it has started finding things. The first was a payment amount that could stop a whole run of the deposit watcher. It is fixed, along with the larger problem underneath it.
Why
Reading an amount is the first thing that touches a number arriving from the exchange, and it assumed the number would be a number. Anything else, a stray character, a comma between thousands, threw an error rather than refusing politely. The crash was the small half. By the time the amount was read, the deposit had already been written down as handled, so the next pass skipped it as old news. One odd character would have ended the run, left that payment matched to nothing, and raised nothing to anybody. Somebody would have paid and received nothing, and no alarm anywhere would have gone off, because a payment watcher that finds no payments looks exactly like a quiet afternoon. Now an amount that cannot be read exactly is never treated as equal to anything, so the deposit finds no order and goes in front of a person. A run also survives one bad deposit instead of dying on it.
What it means
The fuzzer found this nine attempts into its first run. It is now at roughly a million and a half attempts an hour against five parts of the system, and it keeps whatever input reached somewhere new so that tomorrow starts from everything it learned today. Those inputs are kept in the database, because a fuzzer that loses them still runs, still reports clean, and has quietly stopped looking anywhere interesting. Every fix lands with a test that pins the fixed behaviour, so the next regression is caught in seconds rather than in an hour.
addedportalplan 17

Point your phone at it, or type it in

What changed
Setting up an authenticator now offers a square to scan or a key to copy, with a toggle between them, on both portals. Entering a six digit code also submits by itself once the sixth digit lands, on the sign in step and on the setup step.
Why
Which way is right depends on the device in your hand. On a laptop the phone points at the screen; on the phone itself there is nothing to point at and the key gets copied instead. Offering only one of those makes half the people do the fiddly version. The automatic submit is for the thirty second clock: typing six digits and then looking for a button is how a code expires between being read and being sent. The button stays, because a recovery code is not six digits and some browsers fill fields in ways this cannot see.
What it means
The square is drawn on the server and put straight into the page. Nothing is fetched and nothing is drawn in the browser, which matters because the thing inside it is the authenticator secret: had it been a separate request, that secret would have travelled in a URL and been written into every proxy log between here and the phone. A refused code clears the field and is remembered, so it cannot resubmit itself in a loop. Without that the automatic submit would spend an entire rate limit allowance in a couple of seconds and lock somebody out of their own account.
fixedportalplan 17

A lockfile that only worked on one kind of machine

What changed
The dependency lockfile carries the platform specific packages for every machine again, rather than only the one it was last regenerated on.
Why
Regenerating it from scratch on Windows recorded only the Windows build of the password hashing library, because that is the only one that machine needed. Everything kept working locally and every build on Linux then failed to start, which is the one place it matters. Thirteen platform entries had become one.
What it means
The change that caused it was an attempt to lift a dependency past four security advisories. That attempt did not work either: the framework restores its own pinned copy on every install regardless of the override, so the override was doing nothing while quietly breaking the build. Dead configuration that also causes damage is worse than the problem it was aimed at, so it is gone and the advisories are recorded as findings to be judged on their merits.
changedsiteplan 0.1

The changelog catches up in a minute rather than five

What changed
The page now refetches at most a minute behind, rather than up to five.
Why
It was accurate the whole time and looked unreliable, which is nearly as bad. Somebody pushing an entry and then opening the page saw the previous answer for several minutes and reasonably concluded the entry had been missed. Checked rather than assumed: of twenty seven entries written today, twenty five were on the page and the two absent ones appeared about a hundred seconds later, which is exactly the length of the window.
What it means
The thing that would make it immediate is a webhook on the organisation, which the receiver has been waiting for since it was built. Creating one needs a permission nobody has granted, so a shorter window is the honest fix rather than the clever one.
addedinfraplan 18

A machine whose whole job is trying to break what we shipped

What changed
A dedicated machine runs static analysis, dependency advisory checks and the property suite against every repository, on a schedule, and writes what it finds where the people responsible can read it.
Why
It runs on its own hardware rather than next to the payment watcher because a machine deliberately doing strange things does not belong beside exchange credentials. It writes into the team database rather than the customer one, because a fact about our own code is team data.
What it means
The first pass found four dependency advisories, two of them rated high, in a package pulled in three levels down. Nobody had looked, and nothing else would have looked. That is the argument for this existing, and it arrived on day one rather than in theory. Every pass is recorded, including the ones that find nothing. An empty list from a machine that stopped a week ago reads exactly like an empty list from a machine that is working, and the page says which it is looking at before it says anything else.
changedportalplan 17, 18

The two portals now share nothing at all

What changed
The team portal has its own database. Identities, second factors, sessions, sign in links, roles and rate limiting all live there, and the application has no connection to the customer database at all.
Why
They were the same rows. Somebody who set up an authenticator for the team portal found it being asked for on the customer one, because there was one account underneath both and one place recording that a second factor existed. That is not a settings problem, it is the two portals being the same thing wearing two hostnames.
What it means
One person can hold an identity on each side and they are two different identities: turning something on for one does nothing to the other. The team portal now holds no customer data and cannot query any, which is a real constraint rather than a policy. What it needs from that side, it will ask the customer application for over an authenticated call. The team account also has no password, because there is nothing to have one for: it signs in by emailed link and a compulsory authenticator. The automated deploy carries the new connection too. Without that the next one would apply a configuration with the value missing, and the guard that refuses an incomplete spec would stop it, which is the right failure but a confusing one to meet at three in the morning.
addedportalplan 17, 18

Tests that invent their own inputs

What changed
Twenty six tests that generate thousands of inputs each and check that a rule holds for all of them, rather than checking the handful of cases somebody thought to write down. Pointed at the three places the plan names first: the arithmetic behind payments, the tokens that let people in, and the checkout gate.
Why
An ordinary test proves the cases its author imagined. These state the rule instead and let the machine hunt for something that breaks it. Among the things now asserted for every generated input: two different orders never produce the same payment amount, a signed link read for the wrong purpose is always refused, changing any single character of a token always invalidates it, a sanctioned country is never allowed and never offered a challenge, and a deposit from before the cutoff is never adopted.
What it means
This is the first and cheapest layer of the continuous fuzzing described in the plan. It runs inside the test suite that already exists, needs no machine of its own, and finds the same class of bug for a fraction of the setup. A dedicated fuzzer on its own hardware is still worth building; it is worth building second. Nothing failed, which is the answer worth having and not one to relax about: it says the rules hold across roughly forty thousand generated cases, not that no rule was left unwritten.
addedportalplan 17

A way back in if the wallet goes

What changed
An account that signs in with a wallet can now attach an email address as a second way in, from a page that also shows plainly which methods the account currently has.
Why
A wallet is a good credential right up until the day it is lost, and an account with only that one way in is an account nobody can recover afterwards, including us. Saying so at the moment somebody is looking at their account is more use than saying it in terms and conditions.
What it means
The address is not attached when it is typed. A link goes to it and the address is attached when that link is opened, because an address nobody has proved they can read would otherwise be a way in: anybody could claim one they do not own and then ask us to send a sign in link to it. The confirmation link names both the account and the address inside its signature, so it cannot be pointed at a different account or a different address. Both are read back out of the signature rather than taken from the request that arrives.
fixedinfraplan 18

The team portal could not send mail, and said nothing about it

What changed
The team portal now has the settings it reads. It had a database connection and nothing else, so it could not send a sign in link, could not sign the short lived token used for the second factor step, and could not have finished setting up an authenticator even if somebody had reached that screen.
Why
The request endpoint answers identically whether or not a link was sent, which is deliberate and stops it being used to ask who works here. The cost of that design is exactly this: a portal that cannot email anybody looks, from outside, precisely like one that just did. The only visible symptom was somebody saying the link they received went to the wrong portal, when in truth the right portal had sent nothing and the only link in the inbox was the other one.
What it means
Worth naming the shape rather than the instance. An endpoint that deliberately reveals nothing needs its own way of being seen to work, and the answer here was to check what the running application actually holds against what its code actually reads, rather than to trust that a deployment that reported healthy was complete.
addedportalplan 17

A sidebar on both portals, and one that works on a phone

What changed
The customer account is now four places rather than one long page: an overview of counts, orders and what you own, your licence and the devices using it, and gifts in both directions. A fifth, security, is where the second factor is turned on. The team portal has the same frame.
Why
One page that grows a section every time something is built is a page nobody can find anything on. Splitting it also means a link can point at the part somebody needs.
What it means
On a narrow screen the column becomes a drawer behind a button, closing itself whenever a link is followed or the browser goes back, because a menu left covering the page you just asked for is the sort of thing only noticed on a phone. On the team side the entries somebody's roles do not grant are absent, and the ones granted but not yet built are shown greyed with the word soon rather than hidden. A role that grants nothing visible looks like a bug to the person holding it.
fixedportalplan 17

A link does not get past a second factor

What changed
Opening a sign in link on an account that has an authenticator now asks for the code before anything is signed in. Until this, the link created the session on its own.
Why
Somebody who turns on a second factor is saying that reading their email should not be enough to reach their account. Signing them in from an emailed link alone quietly disagrees with them, and it does so in the one case where they had explicitly asked for the opposite.
What it means
Between the two steps there is a short lived token in a cookie that proves a link was opened minutes ago. It is not a session and grants nothing by itself, so intercepting it earns somebody the right to be asked for a code and nothing else. It is cleared on success and on giving up, rather than sitting in a browser waiting for whoever is there next. A second factor is also now offered on the customer portal, where it is optional. Declining is remembered on that device for six months rather than against the account: it is a preference about being interrupted, and answering on a phone should not answer on a laptop. Six months rather than forever, because the answer somebody gives while looking at an empty account is not the one they would give once it holds something.
changedportalplan 17

Staff addresses stay on the team side

What changed
An address on a company domain can no longer hold or sign in to a customer account, and cannot claim a gift onto one either. Signing in to each portal is unaffected otherwise: the same person can hold a team account and be told, on the customer portal, that this is not where they sign in.
Why
The permission model rests on the difference between somebody who works here and somebody who bought something. An account that is both blurs exactly the line every later decision depends on, and it does so quietly, because nothing about such an account looks unusual.
What it means
There is one exception, and it is a column on the account rather than an address written into the code. One address has to be able to see both portals to check that both work. An exception living in a condition is invisible to anybody reading the database, and it tends to acquire company. The refusal says the same thing every other refusal on that endpoint says. Answering "that domain cannot be a customer" would tell a stranger which domain we are.
changedportalplan 17

No more passwords, on either portal

What changed
Signing in is now an emailed link, or a wallet signature on the customer side. Passwords are gone from both portals, along with the reset flow that existed to recover them. A link works once and lasts fifteen minutes, so the copy left behind in a mailbox opens nothing.
Why
A password is a secret a person has to keep, reuse somewhere else, and be phished for. Removing it removes all three. The wallet option exists because somebody who paid in crypto already holds a key that proves who they are, and asking them to invent a second credential on top of it is asking for a worse one.
What it means
The team portal is stricter in three ways that all look identical from outside: the address must be on a domain we allow, it must have an account, and that account must be a team member. Telling somebody which of the three stopped them would answer questions about who works here. A second factor is now compulsory for the team and enforced as a column on the member rather than a rule in one file, so it survives that file being rewritten and is re-checked on every request. Turning the authenticator off does not grant a grace period; it returns that person to the setup screen until they turn it back on. Sign out now says goodbye and returns you to the door, rather than leaving you looking at a response body.
fixedportalplan 18

Seeding an account without letting a shell touch the password

What changed
The command that creates the accounts used to look at the portals now reads its values from a file directly rather than from environment variables set by a shell.
Why
The first run reported success at every step: the account was created, the roles were granted, and reading them back confirmed six of six. Signing in then failed, with the portal correctly saying the password did not match. The password had been carried to the command through a shell, and what arrived was not what was in the file, so the hash that got stored was for a password nobody had.
What it means
This is the third time on this project that a credential has been altered on its way through a shell and the symptom has been the same each time: everything reports success and the credential simply does not work. Passing values as data rather than as shell variables removes the step where that can happen. Worth saying plainly that the read-back check did not catch it. It confirmed the roles, which were correct, and said nothing about the password, which was not. A check that verifies the easy half is how something like this survives a green run.
addedportalplan 17, 18

A way into the team portal, and roles that mean something

What changed
The team portal has a sign in. Members authenticate as ordinary accounts, and being on the team is checked separately afterwards, so a correct password for somebody who is not a member is refused in exactly the same words as a wrong one. Six roles now exist in the database rather than as an idea: finance, refunds, support, development, development operations, and owner.
Why
The members table has existed since the first version of the database carrying a second factor and no password, which is not so much an oversight as an unfinished thought. Reusing the customer accounts system finishes it without writing a second implementation of password hashing, rate limiting, second factors and sessions, which would be younger and less examined than the one already in production.
What it means
Owner is not a superuser. It grants and revokes roles, which is a different job from doing the work the other roles describe, and treating them as the same thing is how one compromised account becomes every capability at once. Signing in leads to a page listing what you are permitted and saying plainly that the screens behind those permissions are the next phase. An honest empty room beats a menu of things that do not work.
changedportalplan 17

Less on the sign in screens, and a larger mark

What changed
The panel beside the sign in form is now the mark and one line, centred, with the logo about half as large again. The list of features that sat under it has gone from both portals.
Why
Three bullet points about refund windows and second factors, read by somebody who has arrived to type a password, are three things to skim past. They were true and they were in the wrong place: the refund window belongs next to the refund, and the second factor belongs in account settings. On a sign in screen they were noise dressed as reassurance.
What it means
The team portal was saying the same thing twice, once in a panel and again underneath it, so that is now one card. Both screens are the mark, a sentence, and the thing you came to do.
addedbrandplan 0.1

A web sized mark and wordmark

What changed
The lockup used on the portal sign in screens is now a file of its own, cropped to the artwork and reduced from 354 KB to 9 KB.
Why
The master export carries far more coordinate precision than a screen can render, which is fine in a design tool and wasteful as the first thing somebody downloads before they have signed in. Reducing the precision to what a display can actually show produced a measured pixel difference of two hundredths of a percent.
What it means
Worth knowing about the source: the file named as having no words does contain the wordmark. The name describes what it lacks compared to the fullest version, which also carries the tagline, rather than describing what is in it. Checked by rendering it rather than trusting the name.
changedportalplan 17

Both portals now look like something you would put a password into

What changed
The sign in screen is two columns on a wide display: what the portal is for on the left, under the mark and wordmark, and the form itself on the right in a panel of its own. On a phone the left column is dropped rather than stacked, so the form is the first thing rather than the third. The team portal carries the same shell.
Why
The previous version was a single narrow column on a flat background, which read as unfinished next to what it is asking for. A sign in page is usually the first thing anybody sees of a product, and often the only thing they see before deciding whether to trust it with a password.
What it means
The team portal still has no form, deliberately. There is no team authentication yet, and a form that looks real and refuses everybody is worse than an honest empty page: it wastes the time of somebody who has been told they have access, and it teaches whoever built it to stop believing what a screen says. The logo used is the mark with the wordmark, reduced from 354 KB to 9 KB with no visible difference, because it was exported with far more coordinate precision than a screen can render.
addedinfraplan 18

A commit cannot be made now without the entry that explains it

What changed
A hook in every repository refuses a commit that changes something real and does not include a changelog entry. It prints the format it wants and the escape hatch for the rare commit that genuinely carries no news.
Why
The rule that a commit ships with its entry has been in place since the first week and has been broken repeatedly, by intending to remember it. An audit of the last few commits in each repository found the misses ran into double figures, and the public changelog sat a day behind a day of work while the raw commit stream on the same page kept moving, which made it look like the changelog was updating when it was not.
What it means
Enforced rather than remembered. Writing the entry with the work is also the point rather than an overhead: an entry written a day later is a reconstruction, and it reads like one.
addedbrandplan 0.1

Logo files sized for where they are actually used

What changed
The logo set now includes versions cut for specific places rather than one master file used everywhere: a square mark and a two-to-one lockup for identity verification, a tight wordmark, and a web copy of the full logo.
Why
The master exports carry far more coordinate precision than any screen can render. The full logo was 377 KB and the mark 354 KB, which is a lot to send somebody before they have signed in. Reducing the precision to what a screen can actually show took them to 14 KB and 9 KB, with a measured pixel difference of two hundredths of a percent between the two renders.
What it means
A separate lesson is written down alongside them. A logo dropped into a fixed shape is only as large as the artwork inside that shape allows: a ten-to-one wordmark placed in a two-to-one box can never fill more than a fifth of its height, however many pixels the file has, which is why the identity verification screen looked blurry until the mark replaced the wordmark there.
fixedportalplan 17

Static files were missing from the built image

What changed
The container images for both halves of the portal now include the public directory. They did not before, so anything served straight from disk was absent from the running application.
Why
Nothing pointed at that directory until today, so nothing had ever missed it. The moment the sign in page asked for a logo, the page rendered perfectly, the build succeeded, the deployment reported healthy, and the image came back as a page not found. Every check that exists would have passed.
What it means
It was caught by running the finished image and asking it for the file, rather than by reading the build output. That is the only check that would have found it, and it is worth remembering that a green build says the code compiles, not that the thing you shipped contains what you think it does.
changedportalplan 17

The portal opens on the way in, and looks like something you would trust

What changed
Arriving at the customer portal used to land on a page saying there was nothing there yet. It now takes you to your account if you are signed in, and to the sign in screen if you are not. That screen has been rebuilt around the full logo, on a card, with the second factor step reading as a continuation rather than a rejection.
Why
A portal is a door. A landing page in front of the door is a click somebody has to make for no reason, and the one that was there told people nothing was working, which stopped being true.
What it means
The team portal has the same treatment, and deliberately still has no sign in form. There is no team authentication yet, and a form that looks real and refuses everybody is worse than an honest empty page: it wastes the time of people who have been told they have access, and it teaches whoever built it to stop believing what the screen says. The logo went from 377 KB to 14 KB on the way in, without any visible difference, because it was exported with far more coordinate precision than a screen can show.
addedinfraplan 18

A check that our VPN detection actually detects anything

What changed
A command that asks whichever services we have configured about addresses that are known to be exactly what they are: real Tor exit nodes from the Tor Project's published list, real VPN exits from a VPN provider's published list, and two ordinary home connections that must not be flagged. It reports how many of each were caught, and exits unhappy if nothing was caught by anything.
Why
Our own tests prove we read each service's answer correctly. They cannot prove the service still knows what a VPN is, because that is a fact about the outside world and it changes without telling us. One of the two we tried returns a perfectly valid answer, on time, with no error, that says no to every VPN we could find. A service failing that way is indistinguishable from a quiet week unless something goes looking on purpose.
What it means
Worth running after changing services, after a plan changes, and occasionally for no reason at all. The two ordinary addresses are as important as the bad ones: a check that flags honest people would send every customer through an identity check, and that failure is much easier to ship than to notice.
addedinfraplan 18

A rollout that refuses to ship a gap

What changed
There is a single command that rolls either half of the customer portal onto a version already built, and it is the same command whether a person runs it or the automated pipeline does. It fills the deployment settings in from the secrets file, then checks two things before it sends anything: that no placeholder survived, and that no value it filled in was empty.
Why
A deployment configuration replaces the previous one wholesale rather than editing it, so a placeholder left unfilled would be written into a live setting as literal text. The application would start, answer its health check, look entirely healthy, and then fail at the one call that needed that value. The empty check exists for a sharper reason: two storage keys once sat in the secrets file with nothing after the equals sign, and every check downstream read an empty string and gave an answer that resembled success. It cost a day, and the shape of that mistake is worth guarding against permanently rather than remembering.
What it means
The customer portal was running twenty three commits behind its own branch while everything reported healthy. The reason was that deploying was a manual step somebody had to remember. It is not one any more.
changedportalplan 17

The portal deploys itself, and will not deploy something that does not compile

What changed
Merging to the main branch now builds both halves of the portal, checks the types, runs the tests, and rolls them out only if all of that passes. Until today every deployment was a person running a build by hand.
Why
The running version had drifted twenty three commits behind the branch while every dashboard reported it healthy, so everything built since then existed only on one machine. Separately, this repository had no type checking in its checks at all, which is how a commit that did not compile was merged with a green tick beside it.
What it means
The platform brings the new version up, waits for it to answer its health check, moves traffic across, and only then retires the old one, so the running version serves throughout. The rollout refuses to apply a configuration with a value missing, because an empty setting deploys exactly as quietly as a real one and then fails much later somewhere that looks unrelated.
addedportalplan 17

The customer portal has a way in, and a way back when you forget

What changed
There is a sign in page now, a page for asking to reset a password, the page those reset emails have always pointed at, and an account page showing orders, what you own, your licence and devices, gifts you have sent, and how long is left to change your mind on anything you have paid for.
Why
The reset page is the pointed one. Password reset mail has linked to it since the day mail was switched on, and it did not exist, so every one of those links led to a page not found. To the person holding it, that looks like an expired link or a company that does not work.
What it means
Accounts are made when you buy something, and later when you join the referral programme. There is no open sign up, and the sign in page says so plainly rather than leaving somebody hunting for a button that was never there. The account page is written for an account with nothing in it, because that is what all of them look like today and will for months yet. Everything on it is fetched on the server against the account that is signed in, so there is no request carrying an identifier that could be edited into somebody else's.
fixedportalplan 10, 17

Paying for something now actually gives you something

What changed
A confirmed payment now creates what was bought: a record of each item, a licence key for the account, and a claim link for anything bought as a gift. Until now it changed an order's status and did nothing else.
Why
The tables for all of this have existed since the first version of the database with nothing ever writing to them. Somebody who paid would have received a receipt and nothing they could use, and no part of the system would have noticed, because nothing was looking for the absence.
What it means
Granting happens inside the same transaction that records the payment, so an order cannot end up paid and empty. In case that transaction is not the thing that fails, a sweep runs every couple of minutes looking for paid orders with nothing in them and puts them right, loudly. Nothing is given a start date. The product does not exist yet, and a date claiming a subscription had begun would be a lie told by a column.
addedportalplan 17

Two services now judge a connection, because one of them cannot see VPNs

What changed
Checkout asks two independent services where a connection comes from and whether it is hiding, and a flag from either one counts. It used to ask nobody, which meant everybody looked unverifiable and would have been sent to an identity check.
Why
We measured both against addresses known to be exactly what they are: real Tor exit nodes from the Tor Project's own published list, and real VPN exits from a VPN provider's published list. One service caught six of eight Tor exits and none at all of eight VPN exits. The other caught eight of eight of both. Neither alone closes the gap, and the one that misses VPNs does not error while missing them. It returns a perfectly valid answer that says no, which is the sort of wrong that never gets noticed.
What it means
Both are asked every time, rather than one waiting as a spare for the other, because a service that is confidently wrong has not failed and never will. Ordinary home connections were flagged by neither, which matters as much as the catches do: a check that suspects honest people sends all of them through identity verification for nothing.
changedportalplan 10

Nothing is for sale yet, and now the server says so as well as the buttons

What changed
The buy buttons on the pricing page have always been switched off. The endpoint behind them is switched off too now, and refuses before it even reads the request.
Why
A disabled button stops people who use buttons. The address behind it is reachable by anyone who can type one, and until today such a request would have been priced, turned into an order, and handed a payment address. Nothing was going to be delivered, because the product does not exist yet, so money arriving that way would have been sitting against a promise nobody had made.
What it means
It stays closed unless a setting says the exact word open, so a missing value, a typo, or a brand new deployment all mean closed. When the Launchpad opens, one value changes.
fixedportalplan 17

Where an identity check sends you back to

What changed
Two different addresses are involved in an identity check and they had the same name, which hid the difference between them. One is where the provider's servers post the signed result. The other is where a person's browser lands when they have finished. Ours only answered the first kind of request, so anybody redirected back after verifying would have met an error with nowhere to go. There is a real page for people now, and the machine endpoint quietly redirects to it if the two ever get swapped in a settings screen.
Why
The names invite the mistake, and the failure only appears at the very end of the flow, after somebody has already photographed their passport.
What it means
That page deliberately does not tell you whether you passed. It is reached by following a link, and a link is something anyone can type, so only the signed result is allowed to move an order. It says what it can honestly say: the documents are with the provider, we will email, and we keep the country rather than the document.
fixedportalplan 17

Verification callbacks are now checked the way they are actually signed

What changed
Identity verification results arrive signed three different ways, and we were checking two of them against the wrong bytes. One is computed over a timestamp, session, status and event type joined by colons, and we were joining a different set of fields with pipes. Another is computed over the body with its keys sorted, and we were using the body exactly as it arrived. Both are now built the way the provider builds them, and the timestamp that comes with every callback is checked as well.
Why
Only one of the three signatures was ever going to match, so verification was working by luck rather than by design. On the day that one is retired every callback would have been refused, and the effect would have been that nobody stopped at checkout could prove who they were and carry on. Nothing would have looked broken from our side. The timestamp matters for a different reason: a signature does not expire on its own, so without a freshness window one captured callback could be replayed for as long as the secret lived.
What it means
The canonical form we sign is checked byte for byte against the form the provider produces, including non-ASCII characters, rather than against our own code agreeing with itself. Anything more than five minutes old is refused.

Saturday, August 29, 2026

22 changes
fixedsiteplan 0.1

An internal address should not have been on a public page

What changed
The build plan named the subdomain the internal finance portal runs on. It no longer does. The item still says what that portal is for, because that is worth knowing, and no longer says where to find it.
Why
Publishing the address of an admin surface hands a stranger the first thing they would otherwise have to guess. It does not make the portal insecure on its own, since it is behind sign-in and a role nobody holds by default, but it removes a step from anybody deciding to try, and it does so for no benefit to a reader who has no business there.
What it means
The customer-facing address stays published, because that is a place customers are meant to go. Only the internal one is withheld, and the plan says plainly that it exists and is deliberately unnamed rather than pretending there is nothing there. Hiding the fact would be a different kind of dishonest.
addedsiteplan 17, 12, 0.1

The two portals and continuous fuzzing join the plan

What changed
Three items appear on the build plan that were not there before: the customer portal, the team portal, and continuous fuzzing. All three are marked in progress, which for two of them means real work exists and for the third means the design is being written.
Why
The plan is only worth reading if the things being worked on are on it. Two of these have been under construction for a while without appearing anywhere a reader could see them, which makes the tracker quietly less honest than it looks: work happening off the plan is work the plan does not account for.
What it means
The customer portal is where accounts, checkout, licences and gifting will live. Signing in works today with either an email and password or a wallet, and both were tested against the real database rather than a mock. The team portal is deployed and serving, with nothing behind the page yet: sign-in, roles and the finance dashboards come next. Continuous fuzzing is the one with nothing running. The idea is a machine that spends every hour trying to break what has already shipped and reports what it finds, pointed first at the code that handles money and identity, because that is where a quiet bug costs the most and is hardest to notice. It is at the stage of choosing tools and targets, and the note on the plan says so rather than implying more.
fixedsiteplan 17, 0.1

The caps limit places, not people

What changed
Deep Freeze and Absolute Zero no longer say one per person, because they are not. The limit is on how many places exist, and one person may hold as many as they can pay for. The cart lets you add them the same way as the other two.
Why
The rule changed once gifting entered the picture, and the page had to change with it. A tier that says one per person and can be bought five times as gifts is a page telling the reader something that is not true, and this one had been live saying it.
What it means
Three and a half thousand and five hundred are still the numbers, and they still exist for the reason they always did: each place commits us to serving somebody for a long time, and a promise that size has to be countable. What they never were was a fairness mechanism, and the page said otherwise for a few hours. Somebody can now take several and give them away, which is the point of allowing gifts at all.
addedsiteplan 17, 0.1

The Launchpad has a cart

What changed
The Launchpad page can now be used to assemble a pledge. Frostbyte and Permafrost have quantity controls because they can be bought repeatedly, the other two have a single add because they cannot, and the total updates as things are chosen. Both payment buttons are visible and both are switched off.
Why
The tiers were readable but not selectable, and the two that stack were indistinguishable from the two that do not until you read the rules underneath them. A quantity control next to one and a single toggle next to the other says it without a sentence. The payment buttons are shown disabled rather than hidden. Somebody deciding whether to back this wants to know how they would pay before they start picking tiers, and an empty space where the buttons will be answers nothing.
What it means
Nothing takes money yet. What the cart does is arithmetic, and it does all of it from the same selection: the total is the sum of the lines, and the months are the sum of the grants, so the sentence about what you get and the number you would pay cannot come to disagree with each other. What is selected stays in the browser and is not sent anywhere, because somebody who has not decided to buy has not agreed to be recorded.
changedsiteplan 17, 0.1

The Genesis Program is now the Product Launchpad Program

What changed
The pre-order campaign is renamed throughout the site, and its page moved from /genesis to /launchpad. Anybody following the old address is sent to the new one permanently rather than finding nothing there.
Why
The old name collided with itself. This is a chain, and a chain has a genesis block, a genesis block rate and a genesis state, all of which mean something specific and none of which have anything to do with buying a software subscription early. Two unrelated things sharing a word on the same site is how a reader ends up believing the campaign sells something to do with the chain, which is the exact impression the campaign page spends its length trying to dispel.
What it means
The rename was deliberately partial. Twenty-three references to the programme changed, and the genuine blockchain uses were left exactly as they were: the genesis block, the genesis block-rate target, and genesis zero for the compute mesh all still say genesis, because that is what they are. The tier names, the caps, the prices and the rewards are untouched. Only the name of the thing they belong to moved, along with the badge that used to read Genesis Cohort and now reads Launchpad Cohort.
changedsiteplan 17, 0.1

The two capped tiers open up, and buying rules go on the page

What changed
Deep Freeze goes from two thousand places to three and a half thousand, Absolute Zero from two hundred and fifty to five hundred. Both are now one per person. The two uncapped tiers can be bought as many times as anybody likes and the months add up. All of that is written on the page rather than left in the terms.
Why
The caps were sized to raise a specific amount between them, and the amount went up. Five hundred at $1,499 and three and a half thousand at $399 comes to a little over $2.1M if both fill, which leaves room for the ones that get refunded rather than requiring every single place to sell. The single purchase limit on those two is not a scarcity trick. Each one commits us to serving somebody for a year or for good, and a promise of that size has to be a number that can be counted rather than one that depends on how many times somebody presses the button. The other two have no such problem, so they have no such limit. Their months stack: three Frostbyte is six months of Pro, two Permafrost is twelve.
What it means
Only time stacks, and the page says so beside the offer instead of somewhere nobody looks. A second purchase does not produce a second registry entry, a second badge or a second name on the wall, because there is only one of the person buying. The twenty percent discount does not stack either, and that one is worth being direct about: five of them would be a permanently free account, which is not something that can be honoured, so it is not something to offer and then walk back.
changedsiteplan 17, 0.1

The Genesis tiers now cost less to honour than they raise

What changed
Three of the four Genesis tiers grant less than they did an hour ago. Frostbyte drops from six months of Pro to two, Permafrost from eighteen months to six while keeping its twenty percent discount for life, and Deep Freeze changes from Elite for life to twelve months of Elite. Absolute Zero is untouched.
Why
The prices had been published for a while without anybody working out what they cost to deliver. With a real figure for what a month of service costs, three of the four tiers turned out to be sold below cost, and not marginally: if both capped tiers had filled, the campaign would have raised roughly $1.17M and owed about $2.34M of service against it. Deep Freeze was the worst of them. At what it costs to run an Elite account, $399 buys about sixteen months, and it was promising a lifetime. None of the prices moved. What changed is what each one buys, which is the honest correction when the price is right and the promise was too large. Every tier now carries a positive margin, and Absolute Zero turned out to have been priced almost exactly correctly all along.
What it means
The part worth saying plainly, because it is easy to get wrong in the good direction: most of what a campaign like this raises is not money the company has earned. It is service sold and not yet delivered, and it has to still be affordable in eighteen to twenty-three months when people begin using what they paid for. On the strongest scenario the campaign brings in about $1.9M and owes about $1.46M of that back as service. Roughly $450,000 is genuinely free to spend. Treating the rest as budget would mean funding the build with money owed to the people waiting for it. The offers are smaller now and will convert worse than the ones published this morning. That is the cost of them being deliverable.
fixedsiteplan 18, 0.1

Everybody clicks the same link, which is exactly what broke the counting

What changed
A page arriving with a newsletter marker on it is no longer allowed to be stored and handed out again. Without that, the counting would have been close to useless in the one situation it exists for.
Why
Everyone who clicks a particular link in a particular issue asks for exactly the same address. Something in front of the site keeps a copy of the answer to an address that has been asked for before, so the first person through reached the server and was counted, and everyone after them was handed the stored copy while the server heard nothing. In testing this looked like an occasional lost visit. In an actual send, where thousands of people follow one link, it would have counted one of them. It was found by accident and then confirmed on purpose. The pages that kept their counts were the ones that are rebuilt for every request anyway, and the pages that lost them were the ones that are not, which was the clue. Four requests to a single address were counted once; four requests to four different addresses were counted four times. That is not a subtle result.
What it means
Only pages arriving from a newsletter link give up being cached, which is people coming from an email and not ordinary traffic. Everything else on the site is unaffected. This is the fourth thing in two days that behaved correctly on a development machine and wrongly in production, and all four were about the environment rather than the code: a value not present when the image is built, a request not permitted to leave and come back, work not surviving after a response is sent, and now an answer being remembered by something in between. None of them can be found without running the thing where it actually runs.
fixedsiteplan 18, 0.1

A quarter of the visits were being lost, because of a belief nobody checked

What changed
Arrivals from newsletter links are now written down at the moment they happen, by the same piece of code that notices them. Twelve visits produce twelve records. The arrangement before this produced nine, consistently, and had no idea it was doing so.
Why
The whole design rested on one belief: that the part of the site which sees every request cannot read configuration that arrives when the site starts, only configuration baked in when it was built. Everything else followed from that. It had to pass each visit to something else that could read a key, which meant an internal request, which meant a hop that could fail, which meant work left running after the reader had already been sent their page. The belief was never tested. It is wrong here. The key is perfectly visible to the part that was assumed to be blind, and checking took a couple of minutes against a copy of the real thing. Two rounds of increasingly careful work went into making that hop reliable, including one fix that genuinely helped and moved the number from zero to nine. None of it was necessary. So the hop is gone. A visit is written where it is seen, and the page waits for that write instead of trusting it to finish on its own. Waiting is the point: work left to finish after the response has gone out is exactly what was being dropped. The cost is a few milliseconds, only on visits that arrive from an email, and only ever to storage in the same city as the server.
What it means
Holding visits in memory to write in batches is gone with it, which removes the other way they were being lost: anything held in one server's memory disappears when that server is replaced, and servers are replaced on every deployment. Writing each visit on its own costs one small request per visit, and a link in a weekly email will never produce enough traffic for that to be worth optimising. Forty-three tests, and the whole thing was run against real storage before it went out.
changedsiteplan 0.1

The site is live

What changed
Launching the project site is marked done on the build plan. It is served at aton-network.org, the .com forwards to it, and both work with or without the www. Every push to the main branch builds it and puts it out without the site going down in between.
Why
The item had been sitting at in progress with a note saying the site and the tracker were built and the domain and the deployment were still to come. Both of those arrived, so the note was no longer true and neither was the state. The plan is only worth reading if the items in it say what is actually the case, and an item that stays in progress after it is finished is the same kind of lie as one that claims to be finished early.
What it means
What is live is the site, the build tracker reading the organisation directly rather than a stored copy, this changelog, and the weekly mailing list. What is not live, and is not claimed to be, is everything the site describes: there is no network running, no token, and nothing to buy. The site being public is the site being public and nothing more than that.
fixedsiteplan 18, 0.1

A storage failure that would have gone unmentioned

What changed
When the click counts cannot be written to storage, that is now said in the log. It used to be handled correctly and mentioned nowhere.
Why
The handling was already right: a batch that fails to write is kept and tried again rather than thrown away. What was missing is that nobody would ever know it was happening. The retry only comes around when the next visit arrives, so during a quiet stretch a batch can sit unwritten for as long as the quiet lasts, and the only visible symptom is numbers that are lower than they should be, which is indistinguishable from a quiet week. This is the third time in two days that the same shape of mistake has cost real time here. Something fails, the failure is caught so that it cannot hurt anybody, and catching it is treated as the end of the matter. The visitor is fine in every one of those cases. The person trying to work out why a feature reports nothing is not, because a healthy log and a broken feature look identical.
What it means
Also worth recording honestly, because it looked like a bug and was not: a test of twelve arrivals recorded nine. The three that went missing were waiting to be written on a container that was replaced while the site was being updated, which is the known cost of holding them for a few seconds rather than writing each one on its own. Repeating the test on a settled site recorded twelve out of twelve.
fixedsiteplan 18, 0.1

A visit that went uncounted, and said nothing about it

What changed
The part that notices somebody arriving from a newsletter link now speaks to the rest of the site directly instead of going out to the public address and back, and it says so in the log when it cannot get through. Before this, arrivals on the live site were being counted on a development machine and not counted at all in production, with nothing anywhere to say so.
Why
The noticing and the storing have to happen in two places, because the half that notices runs somewhere its configuration is fixed when the site is built, and the site is deliberately built without any configuration in it. So one half passes the visit to the other. The address it was passing it to was the site's own public one, which means the request leaves the building, goes out to the wider network, comes back through everything sitting in front of the site, and arrives at the same process it started in. That is a lot of things that are each allowed to say no, and one of them did. It now tries the server it is already inside first, and only falls back to the long way round. It also complains when both fail. That is the more important half of this. The first version treated a failure to count as not worth mentioning, which is true for the visitor and completely wrong for anybody trying to find out why the numbers are empty: the page had been served correctly, the response looked healthy, and the logs were clean.
What it means
Worth recording how this was found, because the first two explanations were both wrong and both comfortable. It was blamed on having tested before the new version had finished going out, and then on a permission somewhere. What settled it was running the same image on a local machine, where it worked immediately, which meant the difference was not in the code but in what the code was allowed to reach.
addedsiteplan 18, 0.1

The storage exists, and the signing is proven rather than assumed

What changed
There is now a private bucket in Toronto holding the click counts, a key with access to it, and the request signing has been put through a real test instead of a flattering one. Counting is live.
Why
Everything blocking this turned out to be a question asked badly rather than a permission that was missing. The key that was supposedly not allowed to create buckets created one on the first attempt once it was a key rather than an empty string. The interface for making keys, which had been written off after a request for the list of them came back saying there were none to see, creates them perfectly well when asked to create rather than to list. Reading is restricted on that account and writing is not, so the check that was run answered a different question from the one being asked. The signing test was the same mistake in a more serious place. A signed request had been sent to a bucket that did not exist, and the reply, that there was no such bucket, was read as proof the signature had been examined and accepted. It proves nothing of the kind. The real test needs a bucket that does exist, and a control: the same request signed with a deliberately wrong key. It now has both. An object was written, read back byte for byte, found in a listing, and the request signed with the wrong key was refused as a bad signature. Without that last part a server that never checks anything looks exactly like a signature that is correct.
What it means
The bucket refuses anonymous readers, which was checked rather than assumed. The one key that got printed to a terminal during this was revoked and replaced before anything was built on it. What is written there stays what it was always going to be: which issue, which page, what time, and nothing about who.
fixedsiteplan 18, 0.1

A check that looked like it passed, and did not

What changed
The claim that the request signing for object storage had been verified against DigitalOcean has been withdrawn from last night's entry, because it had not been. The storage credentials this was all resting on turned out never to have been created.
Why
The signing was tested by sending a real signed request and reading the answer. It came back saying the bucket did not exist, which was taken to mean the signature had been examined and accepted, since a bad signature is normally refused as a bad signature. That reasoning was wrong. A request naming a bucket that is not there is answered as a missing bucket before the signature is looked at, so a valid signature and a worthless one produce exactly the same reply. Sending one with a deliberately invalid key returned the identical answer, which is how this came to light. Underneath that was a plainer mistake. The two storage keys exist in the local configuration as names with nothing after the equals sign. Every check that appeared to confirm they worked was reading an empty string, including one that came back successful and was read as proof, and the error returned when creating the bucket failed was not a permission being refused but an empty key being rejected. The conclusion drawn from it, that the key is not allowed to create buckets, is withdrawn as well.
What it means
The deployment stopped rather than shipping this, because the check that refuses to deploy when a value is empty did its job and named both of them. That check was added after an empty value reached production earlier this month; this is the first time it has caught something. The site deploys without the storage attached. The recording endpoint accepts what it is sent and discards it, no page behaves differently, and the mailing list is unaffected. What is still owed is the honest version of the check that was claimed: a real object written and read back, once a key exists that is not an empty string.
addedsiteplan 18, 0.1

Click counting that does not follow anybody around

What changed
Links in the weekly update can now carry a marker saying which issue they came from, and the site counts them. This replaces the click tracking that had to be switched off last week for sending mail to junk, and it is a better arrangement than the one it replaces rather than a consolation for it.
Why
The provider's version worked by rewriting every link so it passed through a redirect first, and by putting an invisible image in the message to record when it was opened. Both are things this project would rather not do to people, and both were actively hurting whether the mail arrived at all. The replacement does neither. A link points at the page it says it points at, the reader sees the real address in the status bar and lands on it directly, and what gets written down is the issue, the page and the time. Not the address it was sent to, not the network address it was opened from, nothing that says which subscriber it was. That is a real limit and worth stating rather than glossing: this cannot tell you that a particular person clicked, only that a particular link was used. It also cannot be trusted as a count of humans, because the pages are public and anyone can put a marker on a link to one. The number is a shape, not a ledger. Both were considered acceptable, because the question actually worth answering is which parts of an issue people bothered with.
What it means
The counts go into object storage in Toronto, the same region the site runs in, which keeps the data in the country the company is being incorporated in. Reaching that storage means signing requests, and rather than take on a dependency tree to do it, the signature is calculated here. The hashing is tested against two published values, so that half is checked. The other half is not: an attempt to prove the finished signature by sending it to DigitalOcean looked like it succeeded and did not, because a request naming a bucket that does not exist is answered as a missing bucket before the signature is examined at all. A deliberately invalid key gets the same answer, which is how that was caught. The signing stays unproven end to end until there is a bucket and a key to write to it with. Twenty-eight tests cover the counting and the signing between them, fifteen and thirteen.
fixedsiteplan 18, 0.1

Mail was going to junk, and two of the three reasons were ours

What changed
Every test message from the newsletter was landing in junk rather than the inbox. A DMARC policy has been published for the company domain and for the newsletter subdomain, and the open and click tracking that the mail provider applies by default has been turned off. Replies to the newsletter now go to a real shared mailbox that people read, so the mail no longer has to tell readers not to bother replying.
Why
Two of the three causes were things we had done, or failed to do. There was no DMARC record anywhere on the domain. The signing and the sender records were all correct and all passing, but a receiver checking them has nothing to compare the result against without a policy published, and mail from a domain with no policy is treated as the weaker option by default. Both records are set to take no action for now. They ask receivers to report what they see and instruct them to change nothing, so no message that is arriving today can stop arriving because of this. Tightening happens later and only on evidence. The tracking was the worse one. The provider was rewriting every link in the message to pass through a redirect on a subdomain with no history, and inserting an invisible image to record when a message was opened. On a confirmation email that is close to indefensible: the single link the reader is being asked to click had been replaced with a redirect through an unknown domain, which is a reasonable description of what a phishing message does. Both are off. Open and click figures on future issues are gone with them, which is a genuine loss and the right trade while a confirmation link is the whole point of the message.
What it means
The third cause is that the domain is new and has no reputation, and nothing fixes that except sending regularly to people who asked for it. Also worth writing down: testing against our own mailboxes flatters nothing, it does the opposite, because Microsoft treats a message claiming to come from something resembling its own tenant with extra suspicion. An outside address has to be part of the check before anyone concludes this is solved.
changedsiteplan 18, 0.1

The newsletter says outright that nobody reads replies

What changed
Mail from the list now tells the reader plainly that replies to it are not read, and points at the community page for the ways to reach a person. No reply address is set on the mail, and none is going to be.
Why
The address the newsletter sends from cannot receive anything, and that is not a gap to be filled later. Its mail record belongs to the sending provider and is one of the records the domain's permission to send at all is counted from, so pointing it at a mailbox would stop the newsletter going out. The obvious address on the main domain is already in use for something else, and inventing a new one only moves the problem, because a mailbox nobody has agreed to read every week is a mailbox nobody reads. That leaves saying so. Somebody who hits reply on a letter from the founder has spent real time writing something and is then waiting for an answer that is not coming. Silence costs them more than bluntness costs us.
What it means
The confirmation email carries the line and a test fails if the wording goes missing, but the weekly issues are written by a person in the provider's composer, where nothing can enforce it. So the rule is written down in the repository next to the reasoning for it, along with what to check before a send goes out and what would have to change together if a monitored mailbox ever does exist.
addedsiteplan 18, 0.1

Replies can now be pointed at a mailbox that will actually receive them

What changed
The confirmation email can carry a reply address that is different from the address it was sent from. Nothing is set yet, so replies still go nowhere; what changed is that there is now somewhere to put the answer once the mailbox exists.
Why
The newsletter sends from a subdomain, and that subdomain cannot receive anything. Its mail record belongs to the mail provider and is one of the records the provider counts when deciding whether the domain is allowed to send at all. Aiming it at the company's mailbox would take a record the provider is relying on, and the likely result is the newsletter quietly stopping. Sending from a subdomain and taking replies on the main domain is the arrangement this setup expects, not a workaround for it.
What it means
Until an address is configured no reply header is added, which is a deliberate choice rather than an oversight. Naming a mailbox that has not been created yet is worse than naming none: a reply to a real address that does not exist bounces, so the person is actively told their message failed, whereas a reply with no route just goes quietly nowhere. Neither is good, and the second is the one that does not waste somebody's time. Four tests cover it, including that the reply address is never on the sending subdomain.
fixedsiteplan 18, 0.1

One person, one place on the list, and an unsubscribe that stays

What changed
An address can now only occupy one place on the mailing list, and somebody who has unsubscribed is no longer put back on it. Both came out of poking the mail provider with real requests rather than reading its documentation, and both were things it does that we did not expect.
Why
The provider treats upper and lower case as different people. Adding [email protected] after [email protected] does not update the first contact, it creates a second one, and that person then receives every issue twice with two separate unsubscribe links. Addresses were already being lowercased before the confirmation link was signed, so this was not reaching the list in practice, but it depended on one function upstream doing the right thing and nothing checked that it still did. The lowercasing now also happens at the edge that talks to the provider, and a test fails if it stops. The second one was worse. Adding an address always marks it subscribed, including an address that had opted out, and a contact record carries no note of when the opt-out happened. That means a confirmation link somebody unsubscribed after and a confirmation link somebody deliberately asked for again cannot be told apart. The list now looks an address up first and leaves an opted-out one alone, and the page says so plainly instead of appearing to work. Somebody genuinely wanting back on has to ask, which is a real cost, taken because the alternative is mail arriving for a person who asked it to stop.
What it means
Seven tests hold the rules in place without touching the network, and the whole sequence was then run for real against the live list through the shipping container: a mixed case link, the same address again in lower case, an unsubscribe, and a link clicked after it. One contact throughout, and it stayed off at the end.
changedsiteplan 18, 0.1

The signup box is in the footer too, and the whole thing was tested in the shipping image

What changed
The weekly update now has a second signup box, sitting between the footer navigation and the disclosures, so it is reachable from the bottom of every page rather than only from the top of the home page. It is the same component in a smaller size, not a copy, so there is one place where the wording and the behaviour live. The component now generates its own field identifiers instead of using fixed ones.
Why
Two forms on the same page was the thing that made the identifiers matter. An element identifier is supposed to be unique on a page, and a label points at the first match it finds. With both boxes on the home page and both using the same fixed name, clicking the label in the footer would have moved the cursor to the field in the hero. Nobody using a mouse would ever notice; somebody navigating by keyboard or with a screen reader would land in the wrong place with no explanation.
What it means
Before any of this was committed the site was built into its production container with no credentials present, exactly as the deployment builds it, then started with the real ones, exactly as the deployment starts it. A signup run through that container produced a confirmation email that was delivered from the right address, and the confirmation link added the contact. That ordering matters more than it sounds: several faults earlier this month were invisible on a development machine and only appeared once the thing was built somewhere without secrets and run somewhere with them.
changedsiteplan 18, 0.1

The mailing list is wired up and tested end to end

What changed
The weekly update now sends from [email protected], on a subdomain with its own sender records rather than the address the company's ordinary mail uses. The whole path was run for real before any of it was committed: a signup produced a confirmation email that was delivered, clicking a genuine link added the contact, altering a single character of that link had it refused, and the test contact was removed afterwards.
Why
A signup box that looks right and cannot send is a worse failure than one that is obviously missing, because the visitor is told to check an inbox that will never receive anything. Sending from a subdomain matters for a related reason: bulk mail attracts complaints, and complaints damage the reputation of whatever domain they arrive against. Keeping the newsletter on its own subdomain means a bad week for the mailing list cannot affect whether a security report reaches us.
What it means
The deployment refuses to start if any of the three new values is missing, rather than shipping a form that quietly fails. Everything remains on a branch and nothing is live. Worth recording that the delay in getting here was self-inflicted: the tooling used to test the credentials was being refused by the mail provider for its own reasons, which looked exactly like the credentials being wrong, and a working key was replaced on that basis before the real cause was found.
addedsiteplan 18, 0.1

Added a weekly mailing list, with consent that can be proven

What changed
There is a box in the home page hero for the weekly update. Typing an address into it does not subscribe anybody. It sends one email, and clicking the link in that email is what subscribes them, which means an unconfirmed address is never stored anywhere at all, not by us and not by the mail provider.
Why
The company is being incorporated in British Columbia, which puts this under Canada's anti-spam law. That law wants consent that can be demonstrated afterwards, and "somebody typed it into a form" is not that. Doing it this way also means nobody can be signed up by a person who merely knows their address, and the confirmation email explains who it is from and what happens if it is ignored, because one that does not reads exactly like the thing it is trying not to be.
What it means
The reply to a signup is the same whatever happens, so the box cannot be used to find out whether a particular person is on the list. There is a hidden field that people never see and automated form-fillers always complete; anything arriving with it set is answered as a success and dropped. And the consent mechanism is the first part of this site to arrive with tests: nine of them, covering an edited link, a link signed with the wrong key, an expired one, and addresses in the wrong shape.

Friday, August 28, 2026

46 changes
changedsiteplan 0.1, 7

The home page says what the product is

What changed
The opening line was about mining. It now says what somebody arriving actually wants to know: there is an AI that trades, and there is a chain underneath it whose job is to make the record checkable. The four figures beneath the headline changed with it, from block rate and virtual machine counts to what happens to your funds, where the decisions are written, and whose hardware the AI runs on.
Why
The old opening led with proof of work and asked a visitor to follow an argument about electricity before finding out what was being sold. It described the machinery before saying what the machinery was for, which is a reasonable thing to be interested in and a poor thing to open with. The chain is what makes the claims checkable rather than the thing being offered.
What it means
Nothing about the substance moved. The same facts are on the page, ordered by what a reader needs first, and the honest ones are still the ones doing the work: we never hold your funds, we never see your keys, and every call is written down before the market moves rather than selected afterwards. The site title and the social card were carrying the old line too, and now match.
fixedsiteplan 0.1

Every page that reads the repositories now renders when asked

What changed
The home page, the changelog, the roadmap and the security page join the tracker in being rendered per request rather than built into the image. All five read data from the repositories, and the image is built without a token to read them with.
Why
Fixing this on the tracker alone would have left four pages doing the same thing: showing whatever they look like when they can see nothing, and serving that to everyone until the first revalidation after each deploy. The changelog was the visible one, telling visitors there was nothing to show while eighty-five entries sat in the repositories it was describing.
What it means
The data behind these pages is still cached, so rendering on request costs a render rather than a call to GitHub. The rule worth keeping is the general one: a page that shows live data should not be built from a process that cannot see it, because what gets captured is not an empty page, it is a confident description of an empty page.
fixedsiteplan 0.1

The tracker page no longer announces a disconnection it is not having

What changed
The build tracker is rendered when somebody asks for it rather than baked when the image is built. Everything else about it is unchanged, including the cache in front of the data it reads.
Why
The image is built without a read token, deliberately. Prerendering the page during that build captured the state it has when no token exists, which is a notice saying the site is not connected to the organization, and served that to every visitor for the first minute after each deploy. The page was working correctly and describing itself inaccurately, on a timer, which on a page whose entire subject is not overstating things is the wrong failure to have.
What it means
The data underneath is still cached, so rendering per request costs a render rather than a round trip to GitHub. The disconnected notice remains, and remains correct: it appears when there genuinely is no token, which is what it is for.
fixedsiteplan 0.1

Stopped asking GitHub for a number nothing displayed

What changed
The query the tracker sends no longer asks for each repository's open issue count. That number was fetched, carried through the types, and never shown on any page. Reading it needs a permission the site's read token does not have, and in this query language a field the caller is refused takes its whole parent down with it, so one unused count was emptying every repository in the response.
Why
The site is supposed to hold a token that can read files and nothing else. Asking for issue counts quietly widened what that token would have to be allowed to do, in exchange for a figure nobody could see. The right fix is to stop asking rather than to hand the running website more access.
What it means
A public web server should hold the narrowest credential that does its job, and every extra field in a query is a quiet argument for a wider one. The check that reports on this now names the exact field GitHub refused rather than only saying something was forbidden, so the next one of these takes minutes instead of an evening.
changedsiteplan 0.1

The dependency check runs the site's exact query

What changed
The dependency endpoint now sends the same query the tracker sends, rather than a simplified version of it, and reports anything GitHub objects to.
Why
A smaller query proved only that some of GitHub answers. A credential can be refused a single field inside a larger request and take the entire response down with it, which looks identical to everything working when you are only asking for the easy parts. The simplified probe was passing while the real query returned nothing.
What it means
The check and the thing it checks are now the same request, so the two cannot disagree. Any objection GitHub raises is reported rather than swallowed, which is what should have been happening from the start.
fixedsiteplan 0.1

The tracker is connected

What changed
The deployed site reads the repositories directly again, instead of showing the snapshot committed beside it and telling every visitor it was not connected. The cache that sits in front of those reads is now only consulted when there is a token to read with.
Why
The container image is built without a token, because a credential baked into a build layer is a credential in the image. Every page that reads repository data is rendered during that build, and those renders were going through the cache and storing an empty answer under the key the running server would later ask for. The server then served that empty answer, from a cache filled by a process that was never going to find anything. Nothing about GitHub was wrong, which is why nothing that checked GitHub could show it.
What it means
The build tracker is the thing this project points at to say progress is computed rather than claimed, and for its first day in public it was pointing at a file. It reads the organization now, and the page no longer carries the disconnected notice. That notice, for its part, was correct the entire time and is the reason the fault was worth chasing rather than explaining away.
changedsiteplan 0.1

The dependency check compares raw access with our own code path

What changed
The dependency endpoint now asks the same question twice: once directly against GitHub, and once through the accessor the rest of the site uses. It reports both numbers side by side.
Why
Every external check was passing. GitHub was reachable, the token was accepted, the organization was visible and the progress files inside it were readable. The tracker still showed nothing. When everything outside is healthy the remaining possibility is that the fault is inside, and the fastest way to establish that is to run both paths in one request and compare.
What it means
A gap between those two numbers points at our own caching rather than at anybody else's service, which is a very different thing to go and fix. It is worth keeping afterwards for the same reason.
changedsiteplan 0.1

The dependency check reads a file, not just a name

What changed
The dependency endpoint now asks for the contents of the progress file in each repository, and reports how many it could actually read, rather than only how many repositories it could see.
Why
Listing repositories and reading a file inside one are two different permissions. A token can hold the first without the second, in which case everything looks healthy from outside while the tracker receives nothing it can use and quietly falls back to the committed snapshot. Reporting the count of repositories was therefore reassuring and useless.
What it means
The endpoint now exercises the exact operation the tracker depends on, so a green result means the tracker can work rather than that some part of GitHub answered. That is the difference between a check and a decoration.
addedsiteplan 0.1

Added a dependency check that says what actually failed

What changed
There is a second health endpoint that calls GitHub and reports what came back: whether the machine could reach it at all, what status it returned, how long it took, and how much of the rate limit is left. It is separate from the check the hosting platform polls, which must never fail because somebody else is having a bad morning.
Why
The site was deployed, configured with a token, and still showing old data. From outside there is no way to tell whether the token was being rejected or whether the machine could not reach GitHub in the first place, and those have completely different fixes. Reasoning about it from the code was going in circles, so the app was given the ability to answer for itself.
What it means
Anything that goes wrong with the tracker's data now has one place to look before anybody starts guessing. It never returns the token, only what GitHub thought of it, so it is safe to leave reachable. This is the third time something has only been visible by running the code somewhere other than the machine it was written on, and the pattern is worth noticing.
changedsiteplan 0.1

The dependency check now asks GraphQL as well as REST

What changed
The dependency endpoint runs the same organization query the tracker runs, and reports how many repositories came back along with anything GitHub complained about. It previously only checked the simpler of GitHub's two interfaces.
Why
The two interfaces have separate permissions and separate rate limits, and a token can be accepted by one while being refused by the other. The tracker uses the one that was not being checked, so a green result from this endpoint was consistent with the tracker seeing nothing at all, which is exactly the situation it existed to rule out.
What it means
A diagnostic that can pass while the thing it is diagnosing is broken is worse than not having one, because it sends you looking somewhere else. This one now exercises the same path as the feature it reports on.
fixedsiteplan 0.1

The build tracker had never actually been committed

What changed
The tracker page and the endpoint behind it are now in the repository. They were not before. A line in the ignore file read `build/` without a leading slash, which matches a directory of that name at any depth rather than only at the root, so `app/build` and `app/api/build` were quietly excluded from every commit ever made. Both patterns are now anchored, along with two others that had the same flaw.
Why
It surfaced on the first deployment. The site came up and served every page except two, and those two were the ones whose paths contain the word build. Everything ran correctly on the machine the files were sitting on, which is exactly why nobody noticed: the only way to see it was to run the code somewhere else.
What it means
The tracker is the thing this project points at when it says progress is computed rather than claimed, and it existed on one laptop. Nothing else was affected, and the two files are unchanged, but the checks that guard this repository all passed the whole time. A build does not fail because a route is absent, and typecheck cannot miss a file it was never given. What caught it was deploying somewhere else and looking, which is the only check that would have.
fixedsiteplan 0.1

The tracker went live

What changed
The deployed site reads the repositories again, rather than showing the snapshot committed alongside it and telling every visitor it was not connected to the organization. The read token is now looked up when it is used instead of when the file is first loaded.
Why
The container image is built without a token on purpose, because a credential baked into a build layer is a credential in the image. The framework, though, replaces any environment variable it can identify at build time with whatever that variable held during the build, and the token was being read once at the top of a file rather than inside the function that needed it. So it compiled to nothing and stayed nothing, no matter what the running container was given afterwards. The token was sitting in the environment the whole time, correct and ignored.
What it means
The failure was invisible from every angle except the right one. Every page rendered, nothing errored, the logs were clean, the token was present, and the site could reach GitHub and be accepted by it. What finally separated the possibilities was building an image with no token and running it with one, which is the exact shape of the real thing. The page that reports whether the site is connected was, to its credit, correct throughout: it said not connected, and it was right.
changedsiteplan 0.1

The health endpoint reports whether the app was configured

What changed
The health check now says which of the things the site needs are actually present in the running environment. Whether each one exists and how many characters long it is, never the value itself.
Why
A deployment handed no read token behaves almost identically to one that was: every page renders, nothing errors, and the tracker quietly shows the committed snapshot instead of live data. That is the kind of fault that survives for weeks, because the only symptom is a number that is slightly too old and nobody knows what it should be. Asking the running app what it was given turns a guess into one request.
What it means
Anything can now be checked from outside rather than reasoned about from the inside. It also matches what the webhook endpoint already did: it has always answered whether it holds a secret, without revealing it, and the same idea was worth extending to everything else the site depends on.
changedsiteplan 0.1

The site ships as a container and runs on two instances

What changed
The site is now built into a container image by the same run that checks it, pushed to our own registry tagged with the commit that produced it, and rolled out from there. It runs as two instances rather than one. The image contains the compiled server and the files it serves, and nothing else: no build tooling, no source, no package manager, and it runs as a user that owns nothing.
Why
Two reasons, and the second one is the real one. Pulling code straight from the repository meant granting the hosting platform read access to private source through a permission flow a person has to click through by hand, once per organisation, forever. Building the image ourselves removes that: the platform never sees the repository at all. And tagging each image with its commit means the version running can always be named, and rolling back is picking an earlier tag rather than reconstructing what was there.
What it means
A deploy no longer interrupts the site. New containers start, have to pass a health check that talks to nothing external, take over the traffic, and only then are the old ones retired, so requests already in flight finish on the version that received them. Two instances also mean a crash or a machine being taken away for maintenance is not an outage, which one instance can never promise however carefully it is deployed. That redundancy is the larger part of what this costs to run, and it was chosen deliberately over the cheaper arrangement.
addedsiteplan 0.1, 19

The site now deploys itself when the checks pass

What changed
Merging to the main branch now runs the existing checks and, only if they pass, deploys the site and waits for it to answer before calling the run a success. The application itself is described by a file in the infrastructure repository rather than by settings in a hosting dashboard, so what is running is whatever that file says. There is also a health endpoint that answers without calling anything external.
Why
A deploy that happens because somebody remembered to press a button is a deploy that eventually does not happen, or happens from the wrong branch, or happens at the same time as somebody else's. Tying it to the checks means a change that fails typecheck or arrives without a changelog entry cannot reach production at all, rather than reaching it and being noticed later.
What it means
Changing production is now a pull request. The diff says what changed about the running site and the review is the same review as any other change, which is the same reason the build tracker computes itself from the repositories instead of being edited by hand. The health check deliberately talks to nothing: if it called GitHub, an outage at GitHub would look like this site being dead and would restart a container that was working perfectly.
fixedsiteplan 3.2, 12.1

Corrected a chain ID heading that contradicted its own paragraph

What changed
The network parameters section on the developer page was headed "Separate IDs, registered early" while the paragraph underneath it said the IDs had been checked against the registry and would be checked again "immediately before the registration is filed". Both cannot be true. The heading now reads "Separate IDs, chosen but not yet filed", which is what the paragraph always said.
Why
It was found while working out what has to be true before the site goes public. The body was accurate and the heading was the half that claimed more than we have done, which is the direction these things always drift in and the reason to go looking for them.
What it means
Nothing about the IDs has changed: the same two numbers, checked against the public registry and unclaimed at the time of checking. They are not filed yet, and the page now says so in the part people read first rather than only in the part they read second. Filing them is worth doing sooner than later, because a number that is unclaimed today is not guaranteed to be unclaimed in three months.
changedsiteplan 3.5, 0.1

Rebuilt the governance page

What changed
The page now opens with the emergency capability's two properties drawn rather than described. Along the top, proposals moving through their timelock, each one visible on-chain and filling toward the point where it may run. Underneath, the scope: the two components the key can reach in amber, and the seven things it cannot, crossed out. The copy was rewritten around the four questions anybody sensible asks when a project admits to holding a key.
Why
A page about a centralisation point has one job, which is to let somebody decide whether they are comfortable with it. Prose can say the scope is narrow and the action is visible in advance; a board showing two reachable items against seven crossed-out ones, with a timelock bar filling in front of you, lets a reader judge that for themselves in a couple of seconds.
What it means
The disclosure is now complete in a way it was not. The honest note adds a second centralisation point the page previously never mentioned: consensus-level changes stay with the core team until there are enough independent miners and validators, which is a wider power than the emergency key and is held for longer. It also says plainly that a sunset condition written by the people it constrains is worth only their willingness to honour it, and points at the three things a reader can actually check instead of taking our word: the contract, the on-chain record of every use, and the dated changelog entry behind every revision of this page.
fixedsiteplan 0.1

A production build no longer stops the dev server

What changed
There is now a second build command that writes its output to its own directory instead of the one the dev server is using. Running it leaves whatever is serving localhost untouched, so checking that a change compiles no longer means shutting the site down and waiting for it to come back.
Why
A production build and the dev server both want to own the same output directory, so every verification build meant killing the server first and restarting it after. That is thirty seconds of nothing working, several times an hour, for a check that has no reason to interfere with anything.
What it means
Deployment is unaffected: the ordinary build command still writes where everything downstream expects it, and only the checking command writes elsewhere. The new directory is ignored by version control. Hot reloading was never actually broken, but it was being interrupted constantly by builds, which amounts to the same thing from the outside.
changedsiteplan 11, 19

Incorporation is under way

What changed
Incorporating the company in British Columbia has moved from planned to in progress on the build tracker, which is the first item in the plan to start since the repositories went up.
Why
It is the first thing in phase zero and several other items sit behind it, so it is worth being able to see that it has started rather than inferring it from silence. The tracker gives partial credit for work in progress, so the overall figure moves a little. It has not moved because anything finished.
What it means
Nothing is incorporated yet and nothing downstream of it has moved. The entity has to exist before the campaign, the referral programme or anything else that takes money can be signed off, so this is the beginning of that sequence rather than the end of it. The tracker will say so when it completes, with a date on it.
fixedsiteplan 0.1

Fixed heroes colliding with their own animation between 1024 and 1280

What changed
On a window between roughly 1024 and 1280 pixels wide, the headline on most pages was still being drawn at its largest size while the viewport was not, so it ran across the board beside it and on a few pages covered whole rows of it. Eighteen headlines now hold at the smaller size until the wider breakpoint. Thirteen boards start further across in that range. The fade over each board reaches further in as well, and two headlines that filled the entire measure at that width were shortened so they break where they were going to break anyway.
Why
It was found on one page while checking something else and turned out to affect nearly every hero on the site. A laptop at 1280 or a browser window taking two thirds of a larger screen both land squarely in that range, so this was not an edge case, and on the worst pages the animation was unreadable underneath the words.
What it means
Two headlines changed wording as part of this and both read better for it. Risk controls now says "You set the limits. Not the strategy." on two explicit lines rather than one sentence that wrapped awkwardly, and exchange connections says "Trade where you already do." Nothing else about either page moved. Three pages still have their headline sitting over their animation at that width, and that one is deliberate: those canvases run the full width behind the copy as texture rather than sitting in the right half as a board.
changedsiteplan 3.2, 3.3, 3.4, 3.6

Rebuilt the execution layer page

What changed
The page opens with calls falling through both engines toward one account. Solidity calls appear on the left, Rust calls on the right, and roughly a third of them turn sideways at the router first, change colour while they are being translated, and carry on down the other side. Everything lands in the same state row at the bottom, whose balance is the running sum of the deltas actually drawn. The copy was rewritten around which side to build on and why the router ships at launch rather than after it.
Why
Plenty of chains run two virtual machines. What decides whether that is useful or merely confusing is whether the two share a ledger, and that is a hard thing to feel from a sentence. Drawing both columns converging on one account row makes the point structurally: an execution layer is two engines over one state, and two engines over two states are just two chains sharing a name.
What it means
A developer can see in a few seconds which side their problem belongs on, and the what-lives-where block spells out that the right-hand column is not a wish list but what the network itself is written in. The honest note says the router is the most likely place in this chain for a subtle expensive bug, lists the four things being done about that, and adds something easy to miss: because the Compute Mesh runs as contracts on the native engine rather than as protocol rules, a bug in that engine is a bug in the mesh, which is why its audit gates the mainnet date.
changedsiteplan 4

Rebuilt the mining page and tightened the consensus headline

What changed
The dual-lane mining page now opens with the control loop running. Two lanes compete for blocks, hashrate wanders on each, and every retarget interval each lane adjusts its own difficulty. When one lane takes more than sixty-five per cent of recent blocks, the corrective engages, its difficulty bar turns amber and the share comes back down. Underneath the lanes is a ticker of the last forty-four blocks, coloured by which lane produced them, and the percentages beside each lane are counted from it. The copy was rewritten around why picking a side loses either way. The consensus headline was also shortened, because it was wrapping into its own animation.
Why
The limiter is the part of this design that is hardest to believe from a sentence. Reading that a lane cannot run away is not the same as watching one climb to sixty-eight per cent, seeing the corrective bite, and watching it fall back. The ticker exists so the percentage is checkable by eye rather than asserted: count the squares of one colour and divide.
What it means
The hardware argument is now made rather than stated. The honest note names the two things the GPU lane's audit has to rule out, being a shortcut past the memory-bound work and cards from different vendors disagreeing in the last few bits, and says that the second would split the network along hardware lines. It also says plainly that if a reader finds that risk unacceptable, the chain is still secured by a conventional lane producing half the blocks, which is a coherent position to hold and worth telling them about.
changedsiteplan 2, 3.1

Rebuilt the consensus page

What changed
The page now opens with the same stream of blocks running through two different rules at once. The upper track is what a single-file chain does: one block survives each moment and the rest are crossed out. The lower track is what we actually do: every block kept, linked to the tips it saw, and numbered in the order the rule works out afterwards. The copy was rewritten so the argument arrives before any terminology does.
Why
The reason for using a graph instead of a chain is easy to state and hard to feel. Saying that parallel blocks are not discarded means very little on its own; watching six blocks get crossed out on one track while the same six are numbered and kept on the other takes about two seconds and does not require knowing what an anticone is. The old page explained the mechanism well and never showed it.
What it means
Both figures under the animation are counted from the blocks actually on screen, so the wasted number is literally how many crosses are visible. The honest note says three things plainly: the block rate target is the largest unproven claim on this site, if the testnet does not support it we lower the target or wait rather than launching anyway, and the animation is illustrative because nobody has measured a real collision rate yet. The settled-versus-open table on the page makes the same split in one place.
addedbrandplan 0.1, 6

Archived the whitepaper sources and both PDFs

What changed
The whitepaper sources now live in this repository alongside both finished PDFs and the ten generated charts. The sources are the block lists the papers are written in, the emission model every number comes from, the chart generator and the PDF builder.
Why
The papers are the most consequential documents the project has published, and a PDF with no source is a document nobody can safely revise. Keeping the model beside the papers is the part that matters most: the allocation, the emission curve, the halving table and all five charts are computed from one file, so a figure cannot be quoted anywhere that the model does not produce.
What it means
Anybody revising a paper edits the source and rebuilds, and the website picks up the same JSON the PDF was built from. The README states the one rule plainly: change a number in the model, never in a document. It also records the two things that must not drift, being the tokenomics and the fair-launch disclosure on the site, which describe the same allocation and have to change on the same day.
addedsiteplan 0.1, 6

Published the technical and plain-language whitepapers

What changed
Two whitepapers, readable on the site and downloadable as PDFs, under a new White papers section in the Community menu. The technical one runs to forty-four pages and covers consensus, both mining lanes, the two virtual machines, the Compute Mesh, the Signal Ledger, the trading engine's architecture, the exchange, governance, tokenomics in units rather than percentages, a threat model and every parameter that is still open. The plain-language one runs to eighteen and covers the same project for somebody who owns a bit of crypto and has never read a whitepaper.
Why
The project had a website explaining what it is and no document explaining how it works. Anybody assessing this seriously, an exchange, an auditor, a miner deciding where to point hardware, needs the second thing. Writing it also forced the tokenomics to be stated in actual ATON, which is how the false fair-launch claim in the footer came to be found and corrected earlier today.
What it means
Both papers are authored once as a single list of blocks, and rendered twice: once by the website and once into a PDF. Neither is a transcription of the other, so the page and the download cannot say different things. Every number in both, the allocation, the emission curve, the halving table, the year-by-year appendix and all five charts, is computed from one model file rather than typed into a spreadsheet, so nothing can quote a figure the model does not produce. Section cross-references are resolved at build time from headings, so a reference to a section that does not exist stops the build instead of shipping.
fixedsiteplan 6

Corrected the fair launch disclosure

What changed
The fair launch note in the footer said that every ATON in circulation would come from mining or the open market, and that there was no insider allocation. Neither was accurate. Twenty per cent of the supply is allocated: ten per cent to the team on a four year vest with a twelve month cliff, six per cent to a treasury released against milestones, and four per cent locked for market making at listing. The footer now says all of that, with the figures, and the tooltip explaining what a fair launch is no longer claims that nothing is allocated.
Why
It was found while working out the tokenomics for the whitepapers, which required stating the allocation in actual ATON rather than percentages. The old wording was written before those numbers were in front of anybody and it did not survive contact with them. A claim of no insider allocation, sitting next to a plan that allocates fifteen hundred million ATON to the team, is the kind of thing that ends a project's credibility when somebody else finds it first.
What it means
The fair launch claim is still true and still strong: there is no public sale, no private round, and nobody can buy in ahead of anybody else because until listing nobody can buy at all. Eighty per cent of the supply, twelve billion ATON, is issued entirely by mining. What has changed is that the remaining twenty per cent is now disclosed in the same breath rather than left out of it.
changedsiteplan 7, 8

Rewrote the FAQ so it stops leading with refusal

What changed
The trading engine questions page was rebuilt around answering rather than refusing. The headline was "Often the answer is just no" and is now "Ask us the hard ones". The line above it counted how many answers were a flat no; it now says how many questions there are. The four figures under the hero stated what the product will not do four times over, and now state what it does. In the animation, roughly half the questions had a large red NO stamped across them, which is gone. Two answers were reworded: one accused anybody quoting a return of guessing or lying, and one told the reader they had the wrong tool.
Why
The page read as hostile. Somebody asking whether their money is safe is asking a sensible question, and the old version answered it while making the reader feel like a nuisance for asking. It also spent a section explaining that people who misunderstand things leave and complain, which is a point about protecting ourselves dressed up as candour, and it accused everybody else in the industry of lying in order to make a point about our own honesty.
What it means
Not one fact changed. Every answer still says no where no is the answer, still says we will not quote a return, still says the keys cannot be recovered and the trade cannot be approved in advance. What changed is that the page now puts the answer first and the reasoning after it, so somebody can stop reading as soon as they have what they came for, and nothing on it treats a question as an inconvenience.
changedbrandplan 18

Rewrote the Discord specification as a build manual

What changed
The Discord document went from twenty-five pages to thirty-four and is now written for somebody who has never built a Discord server. It opens with a section explaining how Discord permissions actually work, because the system has three states rather than two and combines them in a specific order that almost nobody guesses correctly. Every role's permissions are now written out switch by switch across five tables rather than described in a sentence. The ambassador programme went from fourteen languages to twenty-eight across six world regions, covering every inhabited continent, and it opens in three waves rather than all at once. The cover was recentred around the logo.
Why
The first version told a reader what to build without telling them how, which is only useful to somebody who has done it before. It also called itself global while covering fourteen languages, most of them European or East Asian, with nothing at all for Africa, South Asia beyond Hindi, or the Persian and Hebrew speaking world. And the section on roles said what each role was for without ever listing a permission, which is the one thing a person sitting in front of the settings screen actually needs.
What it means
Somebody can now build the server from this document alone. The regional programme reaches roughly four and a half billion first-language speakers, and every channel in it is gated on having a vetted ambassador with a language check, so the waves are the honest version of that ambition rather than a promise of twenty-eight rooms that would sit empty. Two new rules came out of writing it down: staff cannot change their own nicknames, because impersonation is easier to spot when a real moderator's name is fixed, and neither bot can join a voice channel, because a bot that can join can record.
changedsiteplan 6, 0.1

Reworked the white paper page layouts

What changed
Layout only on all three white paper pages, with not a word of the documents or the pages around them altered. The index now draws both papers side by side at the same scale, every block in order as a bar as long as the block runs, so the difference between a forty-four page specification and an eighteen page explanation is something you can see rather than a page count you have to compare. Each paper's own page gets the same map in its contents rail, above the section list, as a picture of the document you are standing in.
Why
These two documents are the most considered writing on the site and they did not need rewording. What they did need was the same treatment every other page has had, and the honest version of that here is not an illustration of something the paper says but the shape of the paper itself, drawn from the same blocks the page and the PDF are built from.
What it means
Headings run the full width, figures and tables are picked out, and the awkward-truth callouts are amber. That last colour is the only argument the drawing makes and it is one the document can be checked against: the amber is scattered all the way down both papers rather than gathered into a risks section at the end where it is easy to skip.
changedsiteplan 0.1, 1

Rebuilt the about page

What changed
The page opens on the four pieces arranged as a loop, with marks travelling round it and one arrow coming in from outside. Every few seconds that arrow is cut, the ring drains, and the counter falls on its own. The headline is now "Four pieces. One thing feeding them." Everything below the hero, including the full list of what runs on our servers and what does not, is unchanged.
Why
A flywheel diagram is nearly always drawn as perpetual motion: boxes, arrows in a circle, nothing entering and nothing leaving. That is the part that is never true, and on this page it is the part that matters, because the single input is people paying for a product. Cutting it and letting the ring run down says in four seconds what a caveat at the bottom of the page would not.
What it means
Anyone reading the thesis now sees its dependency at the same time as its shape. The loop is real and it does reinforce itself, and it also stops if nobody subscribes. That was already true and already written down further along the page. It is now the first thing the page does.
changedsiteplan 18

Rebuilt the community page

What changed
The page now opens on the holding window. Referral rewards travel from earned, through a shaded middle section where they can still be taken back, to paid, and a few of them turn round inside it with the reason showing. The counts underneath are of what has finished crossing on screen, so the reversal rate is whatever the drawing did rather than a reassuring figure written beside it. The headline is now "Earned quickly. Kept slowly."
Why
The waiting period was the fourth heading on this page and it is the most interesting thing on it. A referral programme that pays the moment somebody clicks is a referral programme being farmed by lunchtime, and the gap between earning and keeping is what makes the rewards worth anything. Putting it first turns a term buried in the small print into the design decision it actually is.
What it means
The reasons attached to a reversal are the real ones: the same address twice, an account that never activated, several signups from one machine, a chargeback. Showing them rather than a generic rejection means somebody reading the page can tell whether the rules would catch them by accident. The existing note about clawbacks stands, and it is easier to accept when the mechanism has already been drawn.
changedsiteplan 17

Rebuilt the Genesis Program page

What changed
The page now opens with two lines. The top one wanders up and down because nobody knows what a network will do, and the bottom one is perfectly flat because what a backer is promised is the same either way. The percentage underneath is measured between the first and last points of the wandering line as drawn, so it reads whatever the drawing is doing rather than a number chosen to make a point. The headline is now "A pre-order. Not an investment."
Why
This is the page where a misunderstanding costs somebody money, and the misunderstanding is always the same one: that backing early buys exposure to the upside. Two lines make the answer unmissable in a way a paragraph does not. The wandering line is a random walk with no drift in it, deliberately, because a line that trended upwards would be a forecast and this is not one.
What it means
The page still lists every tier and every stretch goal, and the existing split between what a backer is getting and what they are not is unchanged. What is new is that the first thing on the page says it: if the network does extraordinarily well you have the months you paid for, and if it does badly you have the same.
changedsiteplan 13, 12.1

Rebuilt the roadmap page

What changed
The roadmap now opens on the eight phases drawn against a month axis, with the overlaps visible and the shading on each bar being real completion read from the repositories. Every few seconds the same chart fades in again one month later, and the mainnet marker moves with it. The phase windows are now numbers on the plan rather than sentences a chart would have to parse.
Why
A schedule chart is a confident object. It puts bars in a row and invites you to read them as facts, and the one fact it never contains is that the bars are joined end to end. Showing the same plan a month late, with nothing else changed, says what a paragraph about estimates cannot: a month lost at the start is a month lost at the end, and it arrives there unchanged.
What it means
Anyone reading this page now sees the schedule risk in the same picture as the schedule, rather than in a caveat underneath it. The mainnet date is drawn twice, at month nine and at month ten, and neither is presented as the correction of the other. The percentages remain computed from the repositories, so the chart also shows whether the estimates are holding rather than only what was hoped.
changedsiteplan 0.1

Rebuilt the changelog page

What changed
The page now opens with pairs: the terse line that actually landed in a repository, and underneath it the sentence that line became for everybody else. A tick in the gutter where an entry was written, a dash where the rule says one was not needed, and a count of both taken from the pairs on screen. The headline and the copy around it were rewritten.
Why
The argument of this page was in its fourth sentence. A commit message is written by somebody for themselves and it is full of the shorthand of the hour; the entry underneath is the same change said out loud to a stranger. Putting the two on top of each other says that in about a second, and it also shows the exemptions rather than quietly relying on them.
What it means
The exempt rows matter as much as the written ones. Tooling and documentation changes do not need an entry, and a page that only showed the flattering half of that rule would be doing the thing this page exists to avoid. Anyone can check the rule the same way we do, which is that the check refuses the merge, so a change that reached the site without an entry could not have got there.
changedsiteplan 0.1

Rebuilt the build tracker page

What changed
The tracker now opens on a map of the whole plan. Each phase is a bar as long as its share of the total weight, and inside it every unit of work is a segment as wide as its own weight, shaded by however much credit its state earns. Finished fills completely, awaiting audit fills most of the way, in progress fills part of the way, planned not at all. The headline was rewritten and the copy underneath it with the same.
Why
The percentage at the top of this page is the one number on the site people are most entitled to be sceptical about, and until now it was a figure next to a description of how it was calculated. Because the bars are weighted by phase and the fills are weighted by credit, the shaded portion of the drawing is that percentage exactly, and the canvas measures it back off its own segments rather than being handed it. The number and the picture are now the same object, and it verifies: the drawing computed 2.2 percent independently and matched.
What it means
The map also makes the shape of the plan legible in a way a list of phases is not. Phase one is visibly larger than phase seven, work that carries real weight is a wide segment and work that does not is a thin one, and the amber segments waiting on an audit sit in the same picture as everything else rather than in a sidebar. The headline now says the thing the page is actually for: nobody here can move this number, because the only way an item advances is a merged pull request in the repository that owns it.
changedsiteplan 16, 15

Rebuilt the staking page

What changed
The page now draws three fee sources feeding one pool, with the mix between them shifting every few seconds because a governed fee split is a thing that moves. Beside it there is a dashed box where an annual yield figure would normally sit, and it is empty, and it stays empty. The three sources each get a proper description, and there is a section explaining why the percentage is missing rather than leaving a reader to assume we forgot.
Why
Every staking page in this industry opens with a large percentage, and on a network with no fees, nothing staked and a governable split, that number would be three unknowns in a two-operator expression. Drawing the empty box makes the absence deliberate instead of looking like an oversight, and the shifting mix shows why a figure quoted on any given day is a snapshot of something built to change.
What it means
The honest note now says four things the page did not. Nobody quoting a fixed return here controls the parameters that produce it. Three sources reduce variance but do not remove it, and at the start all three depend on the same question of whether anybody shows up. A yield paid in a token whose price can fall is arithmetic people usually do in only one direction. And none of it exists yet: no contract, no hub, no fees and nothing to stake.
changedsiteplan 4, 5

Rebuilt the mining page

What changed
The page now shows three graphics cards and how their day divides between mining, compute work and sitting idle, drawn as one timeline each with the work interleaved. The split underneath is measured off the segments on screen. Both lanes are described properly, with what each is for and what hardware belongs in it, and the three network stages say plainly what each one pays, which is nothing until the third.
Why
The claim that the same card mines and serves the compute network is easy to draw badly. Two bars filling at once would look better and would be false: a card does one thing at a time and switches between them. Drawing it interleaved costs the page a slightly more impressive picture and gains it one that matches what a miner would actually see.
What it means
The honest note now carries four warnings rather than one. Do not buy hardware yet, because the GPU lane algorithm is new and if its audit forces a change then anything bought was bought against a spec that no longer exists. A GPU lane cannot be made ASIC-proof, only made unattractive, and anyone promising permanent resistance is selling something. Nobody can say what it will pay, including us, because that depends on three numbers we do not have. And two jobs on one card means two ways to be disappointed: if demand for mesh work is thin, the second income is thin with it.
changedsiteplan 6

Rebuilt the tokenomics page and put the emission model in code

What changed
The emission schedule now lives in one file that both the page and its animation read, so the curve, the counters, the milestone table and the sentences around them all come out of the same four constants. The hero draws the reward decaying month by month with a mark at every four years, and the label at each mark reads what the reward is actually a multiple of rather than saying "halving" and moving on. Underneath it, a bar showing the whole cap rather than the mining pool, so the mined portion visibly stops short and the three allocations that account for the difference sit at the far end in their own colours.
Why
Two claims on this page were being asserted where they could be shown. The first is that the reward decays smoothly instead of halving in one block, which is a shape and belongs in a picture. The second is that four years of decay is a halving, which is close but not exact, so the drawing measures it: 0.501, which is 0.18 percent off. Rounding that to "a halving" would have been fine and saying the real number costs nothing.
What it means
The allocation is now given in tokens as well as percentages, because a percentage is easy to nod along to and twelve billion written out is not. A bar chart of mining against the mining pool would have filled to the brim and quietly implied the cap gets mined; this one cannot, and the honest note says plainly that three billion ATON is never mined because it is already allocated. The note also adds something the page did not say: every figure here is arithmetic about a chain that does not exist yet, and arithmetic has nothing to say about whether anybody will want it.
changedsiteplan 12, 0.1

Rebuilt the security page

What changed
The page now opens on a board that fills seven boxes per component, quickly and satisfyingly, and then stops in front of an eighth that stays empty and hatched. Seven internal review passes, one independent audit, and the row still reads "still not finished". The counter underneath adds forty-two of ours next to a zero. The rows are the real outstanding audits, handed in from the build tracker rather than written into the page, so a component cannot quietly leave this list without leaving that one. Each of the seven passes now gets a proper description, the reporting section says what to include and what happens after, and the invariants the fuzzers are pointed at are written out.
Why
The temptation on a security page is to lead with how careful you are. Everything we do internally is the argument for trusting our own judgement, and the whole point of the tracker rule is that the argument is persuasive and should not be enough. Drawing seven of ours against one of theirs puts that where a reader sees it in a second rather than in the fourth paragraph.
What it means
The honest note adds three things the page had not said. No audit has been returned, because there is nothing finished to audit, so every figure here is a plan rather than a result and should be judged against what turns up in this changelog later. An audit is a snapshot of one commit against one scope, so an audited component that is then modified goes back to unaudited on the tracker. And the reporting section now states plainly that good-faith research reported to us will not be met with legal threats, which is a commitment the page did not previously make.
changedsiteplan 9, 12.1

Rebuilt the developer page and pulled the devnet chain ID off the site

What changed
The page now opens with a compatibility board that works down a list of the things a project brings with it and marks each one: ports unchanged, still works but reads differently, or native engine only. Five, three and one. The counts underneath are tallied from the rows rather than written in, and the sentence beside the board reads the same list, so neither can drift from the other. The four sections behind the existing anchors were all expanded: which engine to pick and what the commands will look like, why the networks carry separate chain IDs, what is actually usable at each testnet stage with what is missing struck out, and what the protocol specification is being held to.
Why
Every chain with an EVM engine says it is EVM compatible and none of them lead with the part that is not. The three things in the middle column are where a contract that compiled and deployed quietly does the wrong thing, so they are given the same weight on screen as the five reassuring ones. The stage list exists for the same reason: the honest answer to what a developer can use today is nothing, and that stays the answer until month three.
What it means
The devnet chain ID is no longer published anywhere on the site. It was appearing in three places, and devnet is a machine we reset at will and never open to outside nodes, so an address for it only invites connections we would have to refuse. It is now a flag on the network record rather than something to remember, the roadmap timeline prints "internal" in its place, and the network list on this page shows two entries rather than three with a gap. The honest note also adds something the page never said: there is no ecosystem here yet, no indexers, no third-party explorers and no audit firms with these opcodes already in their heads, so building here early means writing some of your own tooling.
changedbrandplan 18

Put the Atheron lockup on the Discord specification cover

What changed
The cover of the Discord architecture document now carries the full stacked Atheron logo instead of a line of type. The title block moved down the page to sit against it properly, and the text mark that used to sit under the logo has gone.
Why
The document is the first thing a new moderator or ambassador will be handed, so it should look like it came from the project rather than from a text editor. The text mark had to go because the stacked lockup already contains the wordmark and its own tagline, which meant the cover was printing the name three times in a row.
What it means
The logo is embedded in the HTML rather than linked, so the PDF renders identically wherever the file is opened from and nobody has to keep an image next to it. A copy of the lockup is kept alongside anyway, so the brand repository is not reaching into the website repository for its own cover art.
addedbrandplan 18

Wrote the full Discord server specification

What changed
A twenty-five page build specification for the Discord server now sits in the brand repository. One server covering both the chain and the trading product, split into twelve categories: general community, fourteen regional communities with their own ambassador programme, separate areas for the chain and for the trading engine, a third-party developer section, tier-gated rooms for Genesis backers, and private core team categories. Thirty-six roles, every one with a colour, and the permission model written out category by category.
Why
A Discord server of this size is either designed before it is built or rebuilt six months later, and rebuilding means renumbering everything people have already learned. Writing it down first also forces the awkward questions into the open: which regions get a channel and who staffs it, exactly which backer tier sees which room, and what happens to a security report that somebody posts in a chat channel.
What it means
Somebody can build the server from the document without asking a question, and the launch checklist at the end will not let it open until a test account has walked every category. Two parts of it are treated as promises rather than settings, because they are stated publicly: the Genesis tier access table, which must match what backers were sold, and the commitment that no member of staff will ever send a member a direct message first, which is the single most effective defence against the impersonation scams this industry runs on.
changedsiteplan 15, 3.4

Rebuilt the exchange page

What changed
The page now sends the same order to both halves of the exchange and prices it twice. On one side an order book being eaten level by level, on the other a constant-product curve with a marker climbing it. The order size changes as you watch: small, and the book is clearly cheaper. Large, and the book runs out and stops. Larger still on a pair that has no book at all, and the pool is the only thing there. Underneath, both average prices and the difference between them. The copy was rewritten throughout, and the launch shape is written out plainly further down.
Why
The question this page has to answer is why there are two market structures instead of one, and the answer is not a preference. It depends on the size of the order and on whether the pair has enough liquidity for a book to work at all, which is a comparison rather than a description. Both prices on screen are computed from what is drawn: the book average is summed from the levels being consumed, and the pool average comes from the curve beside it. Change a level's size and the number under it changes too, which is the only reason to put a number there.
What it means
Cross-engine pairs, where a token on one execution engine trades directly against a token on the other, are the version of this worth being excited about and are not what launches. They wait on the router's allowlist widening, which is judged on how the router behaves on mainnet rather than scheduled. The honest note also says plainly that ordering is extractable at launch: fair ordering is genuinely more achievable here than on a chain we do not control, which is a reason to attempt it and not a reason to promise it. And it draws the line between the contracts, which are open to anyone, and the interface we run ourselves, which will apply geographic blocking and address screening. Those are two different things and it would be dishonest to let the first imply the second.
changedsiteplan 7, 7.1

Rebuilt the trading engine page

What changed
The page now draws the road a decision has to travel rather than describing the engine in the abstract. Both engines score the same moment, the two scores are fused into one direction, the job goes out to several machines, and the receipt is written to the chain. A dashed line marks where our side stops, and the order appears only on the far side of it. A decision that cannot reach a majority halts at the mesh and never crosses at all. The copy was rewritten from the top, and there is a plain sequence written out further down with the same line drawn through it.
Why
Every trading product describes the same four things, and no description of them can be checked. What can be checked is the order they happen in, because it is either true that the record exists before the trade or it is not. That is a sequence, so it is drawn as one, and the direction on screen is worked out from the two score bars beside it rather than asserted next to them.
What it means
The honest note names four costs rather than one. Automation is not prediction, and any number quoted as a return in this category is either careless or a sales tactic. A timestamped record proves when a call was made and says nothing whatever about whether it was good. Waiting for several machines to agree is slower than one server deciding alone, and that delay does not go away. And because inference runs on the mesh, and the mesh at genesis is two consumer graphics cards, the first models will be small ones. The page is also explicit that non-custodial has consequences we would rather avoid: lost keys cannot be recovered, nothing can be placed while your machine is off, and support cannot look at positions it is unable to see.
addedsiteplan 0.1

Added a trail to every page and made the menu show where you are

What changed
Every page except the home page now opens with a small trail above the headline, saying which section it sits in and what it is called, with each step back up the site clickable. The menu changed to match. The section holding the page you are reading keeps a lit underline whether the menu is open or closed, the heading of the column that contains it turns blue, and the link to the page itself is marked as the one you are on. On a phone the menu now opens on the section you are already in rather than always on the first one.
Why
The site got wide enough that a reader four pages deep had nothing telling them where they were. The menu answered where you could go and then closed, taking the answer with it. Two changes were needed rather than one, because they are different questions: the trail says where you are standing, and the marked menu says where that is in relation to everything else.
What it means
The trail is worked out from the address bar by the same code the menu uses to decide what to light up, so the two cannot end up disagreeing after somebody renames a page. It is also emitted in the form search engines read, which is what puts a readable trail under a result instead of a bare address. Eleven pages lost the small label that used to sit above their headline: each one only ever repeated its own section name, and with a trail directly above it the page was opening by saying the same word twice.
changedsiteplan 7, 8

Rebuilt the trading engine FAQ

What changed
The page opens with the questions themselves, one at a time, each with its answer stamped across it in large letters. The list grew from eight questions to eleven and is now grouped: your money first, then what the thing actually does, then when it arrives and what it costs. Where an answer is flatly no, the question now carries that as a label so you can see it before reading a word of the explanation.
Why
A page of eleven paragraphs is a page nobody reads, and the most useful thing about this particular set of answers is how many of them are no. Putting that in the hero, in the largest type on the page, tells somebody what kind of company this is before they have scrolled anywhere. The three new questions were ones already answered elsewhere on the site and never collected here: whether we can recover your keys, what the public record says about you, and which exchanges will be supported.
What it means
The animation reads the same list the page renders, so the count of flat refusals in the hero is worked out from the answers rather than typed in. Add a question whose answer is no and the number goes up on its own; soften an answer and it goes down. Neither can drift away from what the page says underneath. The note at the end adds that none of this exists yet, that answers which stop being true get changed with a dated entry here rather than quietly edited, and that a missing question means nobody asked it rather than that we ducked it.
Changelog · Atheron