Releases: elkojo/xNotary
Release list
v0.4.6 — the release is the tag message now
Publishing a release used to be a manual gh release create and it drifted
the way manual steps do: v0.4.3 and v0.4.4 never got one, and v0.4.4 is not
even an annotated tag. The tag message is now the single place a release is
described — this text is the proof, since it became the release you are
reading.
The subject line becomes the title and the body becomes the notes. Where the
subject omits the version prefix, the job supplies it. A lightweight tag is
refused in the gating job, before anything is built and before GitHub Pages is
touched, because discovering it at the end would leave a half-finished release
that re-running cannot fix.
The first attempt at this tag caught a bug in the automation itself.
actions/checkout leaves the tag ref pointing at the commit rather than the tag
object, so the annotation was not there to read and every annotated tag looked
lightweight; left unguarded it would have titled this release after a merge
commit. Both places that read the tag now fetch it explicitly first.
The built site is attached here as a zip, so whoever deploys can take it from
the release page rather than digging a 90-day artifact out of a workflow run.
It also puts the object the AGPL and LGPL notices inside it describe next to
the source it was built from.
No application code changed since v0.4.5. This build differs only in the
revision stamp, so production does not need redeploying: xnotary.digital is
running v0.4.5 and that remains correct.
v0.4.5 — move to xnotary.digital
The app is served from its own apex. This tag builds the production artifact
for Cloudflare Pages (xnotary-dist, BASE_PATH=/, security headers included)
and replaces the old GitHub Pages instance with a redirect stub.
Certificates saved while the app was served from elkojo.github.io stay in that
browser under that origin and do not appear on the new one. The downloaded
Certificate 1 PDF is the real copy and verifies without xNotary.
v0.4.4 — the questions people actually ask
The questions people actually ask, in a panel of their own.
How it works explains the mechanism. It does not answer whether this replaces a notary, whether a displayed name proves who signed, or what happens if xNotary disappears. Those answers existed only in a draft document. They now live next to it as Q&A — twenty-one questions in five groups, folded, because twenty-one open answers would bury the page.
The draft's version references were dropped rather than updated: the claim is about the beta as a stage, and a number in the copy rots on every release.
Route id qanda is new and stable; the existing five are untouched. The page adds no CSS — it reuses the folded-case pattern How it works already uses. Also drops the old preview page, which the app had long since replaced.
Published retroactively on 2026-09-11. v0.4.4 is a lightweight tag and carries no message, so these notes were written from the commits it contains. From v0.4.6 on, releases are generated from the tag annotation and a lightweight tag is refused before anything is built.
v0.4.3 — scope the no-server claim, and say that withholding a name is not anonymizing
"No server" was never quite true, and the copy now says what is. The app talks to OpenTimestamps calendars and to block explorers; what is true is that none of them is ours. Every user-facing statement says "no xNotary backend" instead — including the meta description, which is what search results and link previews repeat.
Leaving a signatory off Certificate 2 does not anonymize them. The consent step let a reader believe it did. Certificate 2 embeds the signed document unmodified, and xNotary read the withheld name out of the signing certificate inside that document — which cannot be stripped without breaking the signature it belongs to. So the certificate leads with that in ink rather than burying it in 8.5pt grey, the consent step states the rule before the choice is made and marks each unticked signer, and Help carries the full version as a limit of its own.
A layout bug that tests could not see. The withheld-signers note had no height reserved in the page budget, so on a full page it could be drawn below the bottom margin — still in the content stream, so text assertions passed, while the page showed nothing. It is measured now, pinned by a page-count test.
Published retroactively on 2026-09-11. The tag was annotated with a subject but no body, so these notes were written from the commits it contains. Releases are generated from the tag message automatically from v0.4.6 on.
v0.4.2 — why no other chain, and the backlog written down
Still public beta. No security review, no Czech eIDAS counsel review. The timestamps are real
and independently verifiable; treat the app itself as unfinished.
Live at https://elkojo.github.io/xNotary/
Documentation only — the app is byte-for-byte the same product as v0.4.1. This release exists
so that the running build points at the source that describes it: every deployed build links the
exact commit it came from, which is what AGPL § 13 asks of anyone running this as a service, and
that link had fallen behind the documentation.
Why Bitcoin, and no other chain
Letting the user pick a chain was investigated and dropped. The README now records the findings,
re-measured rather than recalled, because the question recurs.
- Litecoin. Both public OpenTimestamps calendars have no DNS record at all, so there is
nothing to stamp against without running one — a server and a funded wallet, which ends the "no
backend" guarantee. And the reference client's
LitecoinBlockHeaderAttestation.verify_against_blockheader()raisesNotImplementedError, so
ots verify— the command printed on every Certificate 1 — fails on such a proof. Litecoin's
security is not the objection; nothing being able to check the proof is. - Bitcoin SV. OpenTimestamps has no BSV attestation type, so it would need a private tag no
other client can read — a certificate only xNotary could verify, which is the one thing this
project must never produce. It also runs at roughly 0.023% of Bitcoin's hashrate on the same
SHA-256 algorithm, with a documented 14-block reorganisation in 2021. A timestamp is worth
what it costs to rewrite the block holding it.
The stronger move, if redundancy is the goal
Not a second blockchain — a second kind of authority: a qualified RFC 3161 timestamp from a
trust service provider, alongside the Bitcoin anchor. Bitcoin gives independence from every
institution; a qualified timestamp gives standing with the institutions that matter in law. They
fail in unrelated ways, which is what redundancy is supposed to mean. Two chains fail the same way
and differ only in price.
It is the first item on the roadmap for that reason.
The backlog is now written down
The post-MVP plan was a single paragraph. It is now a prioritised roadmap that says what each item
needs and what blocks it — including that in-browser validation against the EU Trusted Lists is
blocked because the EU's own list server sends no CORS header, and that under eIDAS Art 33 only a
qualified provider may ever give a qualified validation, whatever xNotary implements.
Also recorded: the smaller engineering items, and one honest caveat — a paid archive would make
the "nothing is retained" principle false on the day it ships unless it is reworded first.
v0.4.1 — say less, and show what it is for
Still public beta. No security review, no Czech eIDAS counsel review. The timestamps are real
and independently verifiable; treat the app itself as unfinished.
Live at https://elkojo.github.io/xNotary/
Less text on every screen, and a section explaining what the thing is actually for. No behaviour
changed and no certificate format moved.
The maturity warning is now one gesture away
It used to be a banner on every screen. It is now behind the Public beta marker in the top
bar: hover it, or focus it with the keyboard, or tap it on a phone, and the full notice appears.
The wording is unchanged and it is not going anywhere — it comes off when the security review and
the Czech eIDAS counsel review are actually done, not before. What changed is that it no longer
competes with the screen you came to use.
Roughly 500 words came off the working screens
Nothing that carried information was cut; what went was repetition and words doing no work.
- Signatures opened with ~150 words explaining two choices most people never have to make —
whether to sign the document or its Certificate 1, and how parallel and sequential signing
differ. Both are now foldable, full text one click away. The screen is a heading, two drop zones
and two summaries. - The two notices about trust lists became one, and the four verdicts on Verify are about
half their former length. "Do not sign" is now the emphasised part of a mismatch rather than a
trailing sentence. - The same privacy fact appeared three times on one screen. Once is enough.
How it works was deliberately left long. It is the page people open because they want the
detail, and every shortened screen links to it.
What people use it for
A new section on How it works, five foldable examples: a contract signed in two countries with
no shared platform, a confidential record dated before you disclose it, proving what you
delivered, preserving a record before it changes, and a release anyone can still check in ten
years.
It also spells out what a signature adds that a timestamp cannot. A trust provider checks a
person's identity before issuing their certificate, so a signed document carries a name someone
stood behind rather than one typed into a form — and Certificate 2 records that name with the
authority that issued it.
That claim stays conditional throughout, because xNotary accepts self-signed certificates too and
flags them as vouched for by nobody. xNotary reports what a certificate claims and points you at
the check that confirms it. It does not verify identity itself, and does not say it does.
Fixed
A file name shown in the label column was being uppercased by the style meant for labels.
cert1-countersigned.pdf displayed as CERT1-COUNTERSIGNED.PDF — which nobody can copy, because
file names are case-sensitive.
144 offline tests, and Flow A driven end to end through a real browser against the production
build.
v0.4.0 — a new interface, and a list that finally shows up
Still pre-release. No security review, no Czech eIDAS counsel review. The timestamps are real
and independently verifiable; treat the app itself as unfinished.
Live at https://elkojo.github.io/xNotary/
A new interface, and one bug that had quietly broken a whole screen since it was written. No
certificate format changed and nothing you already hold is affected — existing Certificate 1 PDFs
and .ots proofs verify exactly as before, here and with the reference client.
The interface was rebuilt
A landing page, and the four working screens as guided flows: Timestamp, Signatures,
Verify, My certificates. Dark for the page that explains the product, light "paper" for
the screens where you work on a document.
Bookmarks still work. The route names did not change (#/notarize, #/attest, #/verify,
#/library, #/help), including the installed PWA's start URL.
Two changes are more than cosmetic:
- Timestamping now shows you the fingerprint before anything is sent. Choosing a file hashes
it locally and stops at a review step, so you can see the exact digest that is about to reach
the calendars — and back out if it isn't the file you meant. - The Signatures screen says what it is. It reads signatures out of files signed elsewhere; it
does not send signing invitations and never holds a key. That is now stated on the screen rather
than left to be inferred.
"My certificates" worked for the first time
The certificate list had never rendered. It sat behind a permanent "Loading…" with the records
already in memory, and nothing said why.
The cause was localStamp, which formats every time shown on screen. It asked
toLocaleString for dateStyle and timeStyle together with timeZoneName — a combination
ECMA-402 rejects with a TypeError rather than ignoring. Every call threw, in every browser and
in Node. Because one of those calls happened inside the list's render, the framework abandoned
that branch and left the spinner up.
Nothing was lost and nothing was wrong with the data: the digests, the proofs and the certificate
PDFs were all correct the whole time. The list simply never appeared. It does now, and a test
pins it.
Smaller
- The page description still advertised "collect qualified electronic signatures", a workflow this
app does not have. Corrected — the same wrong line that was fixed in the README in v0.3.1. - The certificate list no longer blanks itself while re-reading storage after an upgrade or a
delete, and no longer waits on a storage-quota estimate that some browsers are slow to answer. - New brand mark, favicon and PWA icons.
Deliberately unchanged
The redesign was applied as layout, not as behaviour. Certificate 1 is still a PDF with the .ots
embedded and the ots verify command printed on it; xNotary still runs with no server, holds no
copy of anything, and still reports what a signing certificate claims rather than ruling on its
legal status.
144 offline tests, and Flow A driven end to end through a real browser against the production
build and live OpenTimestamps calendars.
v0.3.1 — say what the licences are, and prove which source is running
Still pre-release. No security review, no Czech eIDAS counsel review. The timestamps are real and independently verifiable; treat the app itself as unfinished.
Live at https://elkojo.github.io/xNotary/
No product behaviour changed in this release. What changed is what the app tells you about itself.
The licences are now stated, and generated from what actually shipped
There were no third-party notices anywhere before this — not in the app, not in the bundle. Now THIRD-PARTY.txt lists every package your browser downloaded with its full licence text, and it is generated at build time from the modules genuinely present in the bundle rather than from a hand-kept list, so it cannot drift from what was served.
The OpenTimestamps client is linked, not bundled
It is the one copyleft dependency that reaches the browser (LGPL-3.0-or-later; everything else is MIT or BSD). Serving a static page is distributing it, so it is no longer folded into the app's own code: it is built on its own into vendor/opentimestamps.js — unminified, not tree-shaken, at a stable path — and loaded as a separate module. Anyone can build their own version of the library, drop it in place of that file, and have the app run against theirs instead. Instructions ship alongside it at vendor/README.md.
Every build links the source it was built from
How it works now shows the exact revision this page was built from and links that commit. Whoever runs xNotary as a service owes its users the source of that version, not a link to the project in general — so the app points at it itself. A build made from uncommitted changes says so and links nothing, rather than pointing at a commit it does not match.
Contributions: DCO, no CLA
CONTRIBUTING.md asks for a Signed-off-by line and nothing else. There is no copyright assignment: xNotary is meant to stay open source, and the services planned around it — archiving, printed certificates, delivery — do not require taking the code proprietary.
Corrected: the README described a product this is not
The lead sentence promised you could "collect qualified electronic signatures", which reads as sending a document out and gathering signatures back. xNotary has no such workflow and cannot have one without a backend. You sign with your own tools; xNotary reads the signed file and names who signed.
v0.3.0 — correct names, and signing the contract itself
Still pre-release. No security review, no Czech eIDAS counsel review. The timestamps are real and independently verifiable; treat the app itself as unfinished.
Live at https://elkojo.github.io/xNotary/
Fixed: certificates misspelled people's names
The standard PDF fonts are WinAnsi-encoded, and Czech straddles that boundary — á é í pass, ř ě č ů ť do not. Certificates printed Rehor Cízek for Řehoř Čížek and Účetní záverka for závěrka. Half a name rendering and half not looks arbitrary to whoever receives it, and a misspelled name undercuts the attribution the certificate exists to make.
The certificates now embed subsets of Liberation covering Latin, Greek and Cyrillic — metric-compatible with Helvetica, so the layout that is measured to fit one A4 page did not move. 408 KB, in a chunk fetched only when a certificate is built.
Certificate 1 also prints the real file name in its verification commands again, instead of degrading to <your document>.
New: attest signatures on the document, not on a certificate about it
Have everyone sign the contract itself, then give Attest signatures both the signed files and the .ots (or the Certificate 1 carrying it). xNotary finds which revision of the signed file the proof timestamps — a search against the digest the proof already commits to, so a match is evidence rather than an assumption — and Certificate 2 then states the signatures are over the document itself, with the proof attached alongside.
If no revision matches, the certificate says nothing about a timestamp and still attaches the proof, so a reader can see exactly what was offered.
Narrower claims throughout
- Certificate 2 ships; How it works still called it a coming milestone. That page now describes the real workflow, including that signing happens outside xNotary.
- Bank iD SIGN produces an advanced signature, not a QES. It is out of the qualified-provider list and has its own section explaining the difference.
- The consent list now shows each signature's qualified claim, not only its issuer. "Certified by PostSignum" alone was the impressive half without the qualifying half.
- eIDAS is the worked example, not the frame. No user-facing statement is now true only inside the EU.
- The Commission's DSS instance titles itself "DSS Demonstration WebApp". Calling it the official validator claimed an assurance nobody gave, and it pointed users at uploading their document to a third party. The certificate names DSS run locally, or a qualified validation service, instead.
- Nothing suggests xNotary substitutes for an officially verified signature — in Czechia, § 6(2) of Act 12/2020 Sb. requires verifying from population-register data that the certificate belongs to the signer, which no static page can do.
- Times on a certificate are UTC and say so; times on screen name their zone.
Under the hood
125 → 140 tests. Two dependencies added: @pdf-lib/fontkit (runtime) and subset-font (dev only).
v0.2.3 — say where the attachments are and how to detach them
Both certificates now name their attachments, explain that they live inside the
PDF rather than on the page, and give a command-line fallback for viewers that
do not show attachments at all.