This is the full specification of the mechanism behind attested writing. We publish it deliberately and in enough detail to be implemented, rather than patenting it. The reasoning behind that choice is in our post Proof of Authorship; this article is the reference.
Nothing here is secret and nothing is simplified for comfort. If you find a flaw in it, we want to hear about it.
The property we are claiming
A writing record that cannot be constructed after the fact, at any computational cost.
That is a stronger claim than tamper-evidence, and a stronger claim than cost-bounded forgery. We separate them carefully below, because the distinction is the entire point of the design.
Why weaker constructions do not reach it
| Construction | Order fixed | Time witnessed | Cannot be built late |
|---|---|---|---|
| Client-supplied timestamps | No | No | No |
| Server-supplied timestamps | No | Yes | No |
| Hash chain over snapshots | Yes | No | No |
| Hash chain, server-stamped | Yes | Yes | No |
| Sequential-effort (memory-hard) chain | Yes | No | No, only expensive |
| Server-nonce commitments | Yes | Yes | Yes |
Client timestamps are writer-controlled. Server timestamps are honest about receipt but not about creation: an entire fabricated history can be submitted in one sitting and every arrival time will be truthful.
Hash chaining fixes ordering. It is old and good work (Haber and Stornetta, 1991) and it underpins Git and Bitcoin, but it proves only that a sequence was fixed at some moment.
Sequential-effort schemes make forgery cost real wall-clock computation. They do not close the gap, because the adversary chooses when to spend it: next month, on rented hardware, in parallel. Cost is a tax on forgery, not a prohibition.
The protocol
While the author writes, the client periodically sends a digest of the document. The document text never leaves the browser on this path.
On each request the server:
- Verifies the presented nonce is the one currently outstanding for this document.
- Assigns a timestamp from its own clock.
- Computes the commitment.
- Generates a fresh 128-bit random nonce and returns it in the response.
Step 4 is the mechanism. The following commitment must incorporate the nonce just issued and is rejected otherwise, so link n+1 cannot exist before the server has issued its challenge for it.
The committed value
entryHash = SHA-256(canonicalJSON({
seq, // 0-based position in the chain
timestamp, // ISO-8601, assigned by the server
contentHash, // SHA-256 of the canonical document, computed client-side
prevHash, // previous entryHash, or 64 zeros at genesis
meta: {
doc, // document id
kind, // 'attest'
nonce, // the nonce this commitment consumes
v, // format version
wordCount // client-reported, see the trust boundary below
}
}))
canonicalJSON serialises with recursively sorted keys, so identical values always yield an identical digest regardless of construction order. The consumed nonce is inside the hash, which is what makes it structural rather than a transport detail.
contentHash is computed in the browser over the canonicalised document, with trailing whitespace normalised so a cosmetic re-save does not change the digest.
Why it cannot be backdated
To produce a record claiming a past interval, an adversary needs the nonces issued during that interval. Those were random values generated server-side and disclosed only to whoever held the session at the time. They are not derived from anything, so they cannot be recomputed, and they were never published, so they cannot be looked up.
The consequence: a record spanning an interval takes that interval to produce. The missing input is not computation, it is a value that existed only at a moment that has passed.
Operational behaviour
The protocol is a paragraph. Making it survive real editing sessions was the work.
Folded round trip. Requesting a challenge and then answering it would double the request count on something that fires every few seconds. Each acknowledgement instead carries the next nonce, so steady state is one request per save.
Concurrent tabs. Exactly one nonce is outstanding per document. A second tab presenting a consumed nonce receives HTTP 409 with a freshly rotated nonce, adopts it, and retries once. Worst case is two requests instead of one; there is no retry loop and nothing surfaces to the writer. The nonce is held in memory per tab, never in localStorage, which is shared across tabs and would worsen the contention.
Idempotent replay. Each committed link stores the nonce it consumed. If the acknowledgement is lost in transit and the client retries with an already-consumed nonce and the same digest, the server recognises the replay, returns the original result, and writes nothing.
Offline intervals. Commitments stop and resume on reconnection. They are deliberately not queued: a queued commitment would assert a time no server witnessed. A gap is honest; a backfilled gap is not.
Unchanged saves. Autosave fires regardless of edits. When a digest matches its predecessor the server rotates the nonce and records nothing, so an idle tab does not accumulate identical links.
No nonce expiry. The security property comes from one-outstanding-nonce plus server-assigned time. A nonce held for a week still yields exactly one link, stamped at redemption. Expiry would only add failure modes for people who close their laptops.
Concurrency control. Commitment is a single compare-and-swap transaction on (nonce, seq), with the primary key on (document, seq) as a backstop, so two concurrent requests cannot both extend the chain.
Verification
Verification recomputes every entryHash from the stored fields and checks every prevHash link, reporting the first break. A verifier therefore detects reordering, editing, insertion, or deletion anywhere in the chain, including changes to wordCount, because meta is inside the hash.
At certificate issue we freeze the chain head and length. Verification at view time is against the prefix the certificate attested to, so an author continuing to write afterwards is growth rather than tampering.
External anchoring
The protocol above still asks you to trust our clock. Once a day we build a Merkle tree over every certificate issued since the previous run and submit the single root to OpenTimestamps, which anchors it into the Bitcoin blockchain. Each certificate retains its inclusion proof: the sibling hashes needed to recompute the root from that record alone.
Anyone can verify the inclusion path and check the timestamp with any OpenTimestamps client, without contacting us and without trusting us. This construction is not ours and we claim nothing on it; it is Certificate Transparency applied to documents.
Be precise about what anchoring proves. It establishes that a record existed by a given time. It is an upper bound, not a lower one. What constrains backdating is anchor density: against a daily anchor, a record claiming January that first appears in April's anchor is visibly wrong. Anchoring does not make us incapable of lying; it bounds how far, and it leaves evidence.
Trust boundary
| Value | Source | Trust |
|---|---|---|
timestamp | Server clock | Server-assigned |
nonce | Server CSPRNG | Server-assigned |
seq, prevHash | Server | Server-assigned |
contentHash | Client | Client-computed digest of the real document |
wordCount | Client | Client-reported |
Only timestamps, nonces, and chain structure are ours. wordCount is self-reported, which is why the process signal derived from it is corroboration rather than proof, and why the certificate says so.
Commitment rows are written exclusively by the service role. No client-side write path exists, which is what makes the record server-attested rather than server-stored.
What the protocol does not establish
Comprehension. It records that a document took shape over time, in a particular rhythm. A person can write steadily and understand none of it.
Resistance to real-time replay. An adversary can paste generated text and retype it over three days, and the record will show three days of writing, because that is what happened. What the protocol removes is the cheap path: forgery now costs roughly what the work costs. Contract cheating is a market and markets respond to unit costs, so this is a tax rather than a wall. We will not describe it as a wall.
A verdict. Our process bands describe shape, and only one of them is unfavourable. Plenty of honest work is written in one sitting. An institution treating a band as a finding is using it incorrectly.
Privacy
Document text is never transmitted on the attestation path. Commitment rows store digests, timestamps, chain structure, and a word count. The record can prove properties of how a document was written without disclosing what it says.
Implementing it yourself
Everything required is above. If you are building in this area we would rather you implemented it than worked around it, and we would rather compare notes than compete on secrecy. Reach us through the careers page or the address in the footer.