A certificate carries a QR code, and a printed code is the one link nobody can correct afterwards. On 6 September we wrote that certificates issued by the learning core moving into VARGATES Medical had no public verification page yet, and that the code printed on them sent a scanner to the platform's own certificate check, which would not find them. We called that open work, not a closed defect.
Since 15 September those certificates have a public page. This is what it took, what the checks caught on the way, and what the change still does not reach.
The worst answer is a confident one
The platform already had a verification address, /verify/<code>, and it belongs to the platform's own certificates. It upper-cases the code and asks its own records. A certificate identifier from the other half does not fail to load there. The page opens normally and says, in red, that the certificate is invalid: not found, or the link is wrong.
That is worse than a broken link. A broken link tells the person holding the paper that something is off with the link. A red verdict tells them, and whoever they showed it to, that the certificate is not genuine. The learner's cabinet had already stopped linking there rather than send people to that verdict.
Why the new page is not at /verify
The obvious fix is to teach /verify to recognise both kinds of identifier. Two facts ruled it out. The two halves of the product share no top-level address, and that separation is what lets an automated check decide which half owns a given page. And the framework refuses two differently named variables at the same position in a path when it serves a request, not when it builds: earlier in this same move, a production build accepted exactly that, and when the stack started, every request on the site answered with an error. The check before merging caught it before it went live.
So the page lives at /certificate/<id>, a name that is free in both halves.
Absent is not unreachable
A mistyped identifier and a verification service that is down are two different events. One screen for both would tell the holder of a valid certificate, during an outage, that it does not exist. The page keeps them apart:
| Situation | Status | What the visitor sees |
|---|---|---|
| Certificate exists | 200 | Holder, course, issuer, dates, a QR code back to this page, and two downloads |
| No such certificate | 404 | Not found |
| The check could not be made | 200 | An amber notice that verification is unavailable right now, not a rejection; the page asks search engines not to index it |
The page also had to be placed where it can send a real 404. Part of the application streams its page shell before the content is decided, and a “not found” raised inside it arrives after the status line has already gone out as 200. Anyone can type an arbitrary identifier into that address, and crawlers try addresses too, so a soft 404 would leave every such attempt looking like a real document.
The page always states what it certifies. By default that is completion of training, and explicitly not a licence.
What the ported code would have printed
In the project this learning core came from, the QR address was built from a setting that is set nowhere in this deployment, with that project's own domain as the fallback. Carried over literally, every certificate page would have displayed a scannable code pointing at another company's website. It would have looked perfect.
The page's QR code now carries this site's own address. The path is defined once on the server, where the downloadable diploma and knowledge-check protocol are produced, and once on the page, which draws its own QR code. A test reads both definitions and fails if they stop spelling the path the same way. The test compares the path, not the domain in front of it: the server takes the domain from its deployment setting, and that half is still only as right as the setting.
Three things the checks caught first
The public page asked a stranger to sign in. The new address was on neither the list of public pages nor the list of protected ones, and the sign-in logic reads “not public” as protected. Everyone scanning a printed code would have been sent to a login form. Nothing was red: the page rendered, the build was clean. A comment in the code said a test walked every page and failed on exactly this case. That test did not exist. It does now.
The page said “verification unavailable” over a certificate that existed. The server renders the page inside a container and was reaching the API through the public port address, which from inside the container points back at the container itself. The same mistake had been fixed before in two places, but the address stayed written out in six neighbouring files, and the new page picked one of them up. That spelling is now forbidden in that part of the code, with one declared exception for a call made from the browser, where the public address is the correct one.
The test data could report success having written nothing. Two of the assignments in the setup script targeted names that are not database columns. The database layer accepts that silently, so the certificate holder's name would have shown as a dash. The script now reads the data back and stops loudly if it is not there.
The proof is a browser, not a build
A clean build cannot show that a page loads a library only when it runs. So the acceptance test opens the page in a browser with no session at all, as a stranger holding a printed diploma would, against the full stack running locally. It checks the holder's name, the course, that the QR code was generated on the server, that both downloads are real buttons, and the “not a licence” statement.
On production, we checked what can be checked without a real person's certificate. On 16 September an unknown identifier at /certificate/ answered with the application's own not-found page, while an address that matches no route at all still answers with the framework's generic one. The route is served, not merely built. What we have not done is open a real certificate on production: the successful render is proven in the browser against the local stack, not on the live site.
If you are evaluating a supplier
Any supplier that issues credentials can be asked three questions that take a minute to answer honestly: where does the printed code resolve, what does the page say when the verifier is down, and what happens to copies printed before the address last moved. The first two now have answers here. The third is the one we are still working on.