---
title: "How it works"
url: https://kysigned.com/how-it-works-technical.html
---

# How it works

The technical version: what kysigned proves cryptographically, what it can’t, and how anyone can verify a signed document without trusting us, offline, forever. This page is the complete trust thesis; there is no separate write-up to chase down.

## The trust model in one sentence

A kysigned signature is a real email your provider cryptographically signed on your behalf. kysigned just collects those signed emails, timestamps them, and packages them into one self-contained PDF, the **signing record**, that anyone can re-verify with public math and public archives. kysigned is **not** in the trust set: the trust comes from your email provider’s DKIM signature, the public DKIM-key archives, the timestamp authorities, and the maths.

## The signing flow

1. **Sender uploads a PDF.** kysigned computes its SHA-256, the original-document hash **A** that uniquely identifies the exact document, and for each signer prepends a one-page cover (consent language + that signer’s name) to produce the canonical PDF that signer will sign.
2. **Each signer receives an email** with the document **attached** and a short routing token in the subject. To sign, the signer **forwards that email back** to the signing address and types `I sign this document` as the first line.
3. **The signer’s email provider (Gmail, Outlook, Apple, …) DKIM-signs the outgoing forward**: an RSA signature, automatic on every major provider, that proves the message genuinely left that mailbox and that its headers and body weren’t altered in transit.
4. **kysigned verifies the forward classically** (with `mailauth`, the mainstream open-source DKIM/SPF/DMARC library, never self-rolled crypto): the DKIM signature is valid over the full body, the forwarded PDF attachment is byte-identical to the canonical PDF we sent, and the first line is exactly the intent phrase. It then records the signed reply and timestamps the hash of the raw email twice (RFC 3161 TSA + an OpenTimestamps Bitcoin anchor: [why two](#why-two-timestamps)).
5. **When everyone has signed, kysigned assembles the signing record**: one PDF holding the document, a signature page, every signer’s original DKIM-signed email, and the timestamp proofs, then emails it to every party. The working copy on our servers is then deleted.

That signing record is the signature. Anyone you show it to can re-verify every claim with no kysigned account, no API key, and no cooperation from us, even if kysigned no longer exists.

## Anatomy of the signing record

The signing record is a single, ordinary PDF. It carries no digital-signature dictionary and no certificate, so it opens with **no warning and no red-X** in any viewer, and it embeds the complete machine-checkable evidence as attachments:

| Embedded | What it is |
| --- | --- |
| document-original.pdf | The original document **D** alone (the file the sender uploaded), embedded once. It is _not_ cover-plus-document: each signer’s cover is the separate cover-<n>.pdf, and what a signer signed is the reconstruction cover-<n>.pdf followed by this file. Its SHA-256 is the original-document hash **A** the verifier reports. |
| cover-<n>.pdf | Each signer’s own cover page (page 1 of the PDF that signer signed), embedded separately so the verifier can reconstruct exactly what each signer signed. |
| signer-<n>.eml | Each signer’s original forwarded email, byte-complete: the DKIM-signed message itself, re-verifiable on its own. |
| proofs/signer-<n>.tsr \+ .ots | The RFC 3161 timestamp token and the OpenTimestamps Bitcoin proof over the SHA-256 of that .eml. |
| keys.json | The DKIM public keys the signatures verify against, with their provider, selector, and archive provenance. |
| VERIFY-README.txt | Plain-language verification instructions and the exact trust set. |

The rendered pages are a human-readable **signature page** (per-signer verdict block + a SHA-256 **verification code** computed over the whole embedded evidence set), followed by the cover and document. The code is printed on the page and recomputed by the verifier: an integrity check, never a trust anchor.

## What is cryptographically proven

Each claim below is checkable using only public math and public archives. Zero trust in kysigned or the operator is required.

| Claim | How it’s proven | Can the operator fake it? |
| --- | --- | --- |
| **“I sign this document” came from the signer** | The provider’s DKIM signature covers the email body. Changing one character invalidates it. | No, needs the email provider’s private key. |
| **The signer’s email address** | The From header is covered by the DKIM signature. | No, same DKIM constraint. |
| **The exact document being signed** | The forwarded attachment is SHA-256 byte-identical to the reconstruction cover-<n>.pdf ++ document-original.pdf, and because every signer reconstructs against the same document-original.pdf, all signed the same document **A**. | No, a different document is a different hash. |
| **When it was signed** | An RFC 3161 TSA token and an OpenTimestamps Bitcoin anchor, both over the hash of the raw email. | No, you can’t backdate a Bitcoin block. |
| **The DKIM public key was the provider’s real key** | The signature verifies mathematically against the embedded key, and that _exact_ key matches the provider’s key in a public archive that fetched it from the provider’s own DNS. | No: a self-minted key produces math that checks out against itself, but it will not match the provider’s real key in the public archive, so the verdict is FAILED, never confirmed. |

## The intent grammar

A signature is recognised only when the **first non-empty line** of the forwarded email is exactly:

I sign this document

Matching is case-insensitive and whitespace-normalised (so `I SIGN THIS DOCUMENT` and a leading-space variant both count), but nothing else qualifies: a reply without that line, or with other words ahead of it, is not a signature and is bounced back with instructions. This keeps the act of signing deliberate and unambiguous, and it is the same line a verifier reads back out of the embedded `.eml`.

Because the phrase is singular and binds to exactly one canonical PDF, each signing request carries exactly one document. A provider could DKIM-sign several attachments at once, but kysigned deliberately accepts only one, so there is never any doubt about which document the intent line refers to. If you have several documents, combine them into a single PDF (within the size limit) or send each one separately.

## How to verify a signature independently

You don’t need to trust kysigned.com, the operator, or anyone. Drop the signing record PDF onto [the verify page](/verify): the check runs **entirely in your browser** (the file never uploads) and works with no internet and even if kysigned is gone. For each signer the verifier:

1. Extracts the embedded files from the PDF.
2. Verifies the signer’s `.eml` DKIM signature against the embedded `keys.json` (no live DNS needed).
3. Reconstructs that signer’s canonical PDF (`cover-<n>.pdf` ++ `document-original.pdf`) and confirms the forwarded attachment is SHA-256 byte-identical to it, which also proves every signer signed the same document **A**.
4. Confirms the first line is the intent phrase.
5. Confirms the timestamp proof commits to the SHA-256 of that `.eml`.
6. Confirms the DKIM public key is present in the public archive (key authenticity, below): from the archive’s signed statement the record carries, with no network request, or else by an online lookup.
7. Recomputes the verification code and matches the value printed on the signature page.

When every check except the key’s holds, a signer authenticated by their provider sent exactly this document, with the intent line, at the timestamped time: that is the **INTEGRITY VERIFIED** tier below. The key check raises a genuine record to **PROVIDER KEY CONFIRMED**, with no network request when the record carries the archive’s signed statement and with one online lookup when it does not; the Bitcoin anchor, confirmed online, then raises it to its highest tier. Any tampered byte, in the document, an `.eml`, or a proof, fails the relevant check and changes the verification code.

### The verdict is an assurance tier, not a yes/no

kysigned does not return a single green “PROVEN”. It returns one of four **assurance tiers**, because “the signature math is internally consistent” and “the signing key was genuinely the provider’s” are different facts, proven by different evidence. Showing which one you actually hold is the honest thing to do, and it is what stops a forgery from borrowing a real signature’s credibility.

| Tier | What it means |
| --- | --- |
| **FAILED** | A required check did not hold: an invalid DKIM signature, an attachment that is not byte-identical to the document, a missing intent line, no timestamp, or a public archive that publishes a _different_ key than the one embedded (a forgery signal). |
| **INTEGRITY VERIFIED** | Every offline check holds: valid DKIM signature, matching document, intent line, and a timestamp token present. Anyone can reach this with no network. It proves the record is internally consistent and unaltered, but not, on its own, that the key was the provider’s genuine key. |
| **PROVIDER KEY CONFIRMED** | Plus: the public DKIM-key archive confirms the _exact_ key that signed was the provider’s real published key, which the signature math cannot establish alone. A record that carries the archive’s signed statement confirms it with no network request; one without confirms it with an online archive lookup. |
| **PROVEN (DURABLE)** | Plus: the time is durable, the OpenTimestamps proof is confirmed in a Bitcoin block and agrees with the RFC 3161 token, and the signing time falls within the window the archive observed that key live. Authentic key, un-back-datable time, re-verifiable by anyone forever. |

A genuine record reaches **PROVIDER KEY CONFIRMED** at once, offline from the archive’s signed statement it carries or online through the archive lookup, and **PROVEN (DURABLE)** once its Bitcoin anchor settles (a few hours after signing). Offline, the Bitcoin anchor reports pending, and the key archive reports pending unless the record carries the archive’s signed statement, which confirms the key with no network request: a record with the statement reads **PROVIDER KEY CONFIRMED** offline, and one without it sits honestly at **INTEGRITY VERIFIED**. Whatever is still settling reads pending, never failed.

Two of these checks reach past the offline record when the network is available: the **key-archive gate** and the **Bitcoin timestamp**. The verify page runs both automatically the moment you open a record. The Bitcoin confirmation shows grey (“pending”) until the OpenTimestamps proof is provably committed to a Bitcoin block, then green (“confirmed”, with the block height and time), which for a freshly-signed record typically happens within a few hours. It is the only part that contacts the Bitcoin network, and it sends just the timestamp’s hash, so your document still never leaves your device. The key-archive gate turns green (“key in public archive”, with the date it was registered) when the record carries the archive’s signed statement for the _exact_ key, with no network request, or when the live lookup finds that exact key; shows grey (“pending”) when the record has no statement and the archive is unreachable or has not yet recorded the key, which never fails the result; and fails the verdict in one specific case, when the archive publishes a _different_ key for that domain and selector than the one embedded, which is the fingerprint of a forged, self-minted key. So a record that passes the offline checks is already **INTEGRITY VERIFIED** the instant you open it, and **PROVIDER KEY CONFIRMED** if it carries the archive’s signed statement; going online then raises a genuine record toward **PROVEN (DURABLE)** and exposes a forged key as **FAILED**.

### Why each check is there

Each check closes a specific gap. Drop any one and a particular forgery becomes possible, which is why the verifier insists on all of them.

| Check | What it confirms | Why it is needed |
| --- | --- | --- |
| **Email signature** (DKIM) | The signer’s provider authenticated this exact email. | The provider’s DKIM signature _is_ the signature; without it there is nothing to trust and anyone could forge a message in your name. |
| **Document matches** | The forwarded attachment reconstructs to that signer’s cover plus the shared document **A**. | Stops the operator showing one signer a different document behind a matching cover, and proves every signer signed the same **A**. |
| **Intent line** | The signer typed the exact phrase as the first line. | Makes signing a deliberate act, so an ordinary forward is never mistaken for a signature. |
| **Timestamp** | The signed email existed by a proven time (RFC 3161 token + Bitcoin anchor). | Freezes the evidence in time, so a forgery made later with a leaked, retired key cannot be back-dated into the signing window. |
| **Key authenticity** | The _exact_ signing key matches the provider’s key recorded in the public DKIM archive. | Proves the key was the provider’s genuine published key, not one an operator minted and merely _claimed_ was the provider’s. A mismatch fails the verdict; a match raises it above INTEGRITY VERIFIED. |
| **Verification code** | The whole embedded evidence set is intact. | Catches any byte edited after assembly, even bytes outside an individual .eml. |

### Three ways to run the verification

The same documented algorithm runs from three independent surfaces, so our verifier is never the only word on what “verified” means:

- **The verify page** ([kysigned.com/verify](/verify)): drag the signing record in and it runs entirely in your browser, no account, offline. The easiest check, for anyone.
- **The command-line verifier**: `verify-bundle <record.pdf>` in the open-source repository, the canonical reference implementation. One command with an exit code and a full report, scriptable, and it queries the public key archive directly with our server out of the path.
- **The independent verification toolkit** (`scripts/verification-tools/`): a _separate_ reproduction of the algorithm that shares none of the verifier’s code, so running it _cross-checks_ the reference verifier rather than re-running the same engine. Its self-test reproduces the verdict on the bundled examples. For auditors and forkers who want a genuinely independent second opinion.

Separately, the [hash-check tool](/hashcheck) answers a related question for whoever sent a document: it confirms your _original_ file is the one carried inside a signing record (a byte-exact match) or a sign-request (a content match), also entirely in your browser.

Sample files for both tools ship in the repository under [docs/test-assets](https://github.com/kychee-com/kysigned/tree/main/docs/test-assets): a genuine signed record (which verifies to the top tier, PROVEN (DURABLE), once online), tampered bundles that each fail one named check, and original + sign-request pairs for the hash-check tool. The folder README lists the expected verdict for every file, and the toolkit’s self-test reproduces them all offline.

## Why two timestamp anchors

Each signature is timestamped **twice**, over the SHA-256 of the raw signed email: once with an **RFC 3161** token from a timestamping authority, and once with an **OpenTimestamps** proof anchored in the Bitcoin blockchain. This is deliberate. The two anchors prove the same fact through two independent trust roots, and each one covers the other’s single weakness.

A timestamp proves one narrow thing: that a specific hash _existed no later than_ a given moment. That matters because the whole signature rests on the provider’s DKIM key, and DKIM keys are eventually retired. Freezing the signed email in time is what defeats the “publish a retired private key later and forge a back-dated signature” attack: a forgery minted after a key is retired cannot also carry a timestamp from before it was. The timestamp is therefore a clock, and the only real question is how much you must trust that clock.

The two anchors answer that question in opposite ways, with opposite blind spots:

| Property | RFC 3161 (TSA token) | OpenTimestamps (Bitcoin) |
| --- | --- | --- |
| Trust model | A trusted timestamping authority and its CA. | Trustless: Bitcoin proof-of-work and public math, with no authority to trust. |
| Availability | Instant: a complete, verifiable token in one round trip. | Pending for a few hours, until a Bitcoin block confirms the proof. |
| Can the time be back-dated? | Yes, if the authority is compromised, colludes, or is coerced. | No: placing a hash into a past block would mean rewriting Bitcoin’s history. |
| Time precision | Exact, signed wall-clock time. | Coarser block time, but unforgeable in the “no later than” direction. |
| Recognition | The conventional format that courts and eIDAS already recognise. | Technically airtight, anchored in a public chain anyone can re-check. |
| Cost | Free (public TSA). | Free (public calendars and chain). |

Read down either column and the trade is plain: wherever one anchor is strong, the other is weak, and they alternate row by row. That alternation is the whole reason to carry both rather than pick one.

- **OpenTimestamps backstops a misbehaving TSA.** The authority’s one flaw is that you must trust it not to lie about the time. Because a Bitcoin block cannot be back-dated, the chain anchor is an honest second opinion that needs no trust: if a TSA ever issued a back-dated token, the Bitcoin anchor would contradict it.
- **The TSA covers the OpenTimestamps confirmation gap.** An OpenTimestamps proof is “pending” for the first few hours, before a Bitcoin block confirms it. The RFC 3161 token is complete the instant a signature is recorded, so it covers exactly the window in which the chain anchor has not yet settled.

The deeper point is that the two anchors are complementary, not interchangeable. They reach the same conclusion from two unrelated foundations, one institutional (an authority backed by a CA) and one physical (the energy cost of Bitcoin’s proof-of-work), but they enter the verdict at different strengths. The RFC 3161 token gives an instant time, but one you must trust the authority not to have back-dated, so on its own it supports the **INTEGRITY VERIFIED** tier. The **PROVEN (DURABLE)** tier additionally requires the Bitcoin anchor to have confirmed in a block and to agree with the token, because a Bitcoin block cannot be back-dated: that is what makes the signing time truly un-back-datable. Until the block settles, a fresh record rests on the token at the lower tier rather than being falsely elevated; and if the two times ever contradict each other, the timestamp is graded _inconclusive_ instead of durable. Both anchors are free to obtain, so the signing record simply carries both proofs (`proofs/signer-<n>.tsr` and `proofs/signer-<n>.ots`) for every signer.

One honest caveat: Bitcoin block times are not precise to the second, so OpenTimestamps establishes “existed by about this block”, not an exact instant. The direction kysigned relies on is the unforgeable one, proving a signature existed _no later than_ a confirmed block, while the RFC 3161 token supplies the precise wall-clock instant alongside it. Pairing a coarse but trustless anchor with a precise but trusted one is exactly what makes the two anchors complementary rather than redundant.

## Key authenticity: the public archives

DKIM keys rotate, and a provider only publishes the _current_ key in DNS. To prove the key that signed was the provider’s real key **at the time of signing**, not one fabricated later, kysigned anchors it in two independent public records:

- **A community DKIM-key archive** ([archive.prove.email](https://archive.prove.email)), which fetches providers’ published keys from DNS itself and records when it saw each one.
- **kysigned’s own observation**, captured at receipt and timestamped with the same RFC 3161 / OpenTimestamps anchors as the signature.

The archive _independently fetches the provider’s DNS itself_ and timestamps the key it sees, so its record does not depend on anything kysigned says (we also submit the domain and selector so a rarely-seen key gets recorded, but the archive still fetches and verifies it on its own). Verification is a **gate on the exact key**, not a loose “is the domain in there” check: the verifier confirms the precise embedded key is the one the archive recorded for that domain and selector, and shows the date it was registered. If the archive instead holds a _different_ key for that domain and selector, the embedded key was never the provider’s, and the verdict is **FAILED**. This is the check that catches an operator who minted its own key. Newer records carry this confirmation inside the record itself: the archive signs a statement of its own live-DNS observation of the key, kysigned anchors those exact statement bytes with the same dual timestamps, and the verifier checks the archive’s signature on that statement with no network request, so such a record confirms the key fully offline, long after either service. For a record without a statement, if the archive is simply unreachable or has not yet recorded the key, the check shows “pending” and never fails the result, so an honest record is not punished for the archive being slow. There is no key registry that kysigned controls.

The archive’s timestamps also bound the key’s life. It records when a key was _last seen live_ in DNS, and a signature reaches **PROVEN (DURABLE)** only if its anchored time is at or before that last-seen point (plus a grace margin). A key resurrected long after the provider stopped publishing it, the rotate-and-publish forgery, therefore cannot reach the top tier. The bound is one-sided on purpose: signing _before_ the archive first noticed the key is fine, so we never require a lower bound.

One practical note on the web check: a browser cannot read the public archive directly, because the archive does not send the cross-origin headers browsers require. So the verify page asks our server to forward the lookup, passing only the public domain and selector, never your file. The command-line verifier queries the archive directly, with our server out of the path, so a fully independent check is always available to anyone who wants one. Either way the verdict is the offline maths running in your browser, not our word.

## What kysigned (the operator) can see

Being honest about exposure: to assemble a signing record, kysigned necessarily sees the **document content** and the **signing metadata** (who, what, when) while a signing is in progress. Three things bound that exposure:

- **Ephemeral retention.** The working copy of a document is deleted once the signing record is delivered, typically within hours, with a hard cap of 30 days. After that only non-content metadata (document name, hashes, signer status, timestamps) remains.
- **No keys, ever.** kysigned holds no signing keys of any kind, not for users and not for “sealing”. There is no signing secret to steal, and the signing record itself is unsigned: it carries an integrity code, not a certificate.
- **Nothing central to trust.** Once a signing record is delivered it lives in each party’s own inbox, wholly beyond operator reach. A malicious or breached operator cannot forge a signature (no provider key), cannot swap the document (the signer’s DKIM signature byte-binds it), and cannot alter a record already delivered. At most it can refuse to process or fail to deliver.

## What kysigned does NOT prove

- **That the human behind the mailbox authorised the signing.** kysigned proves _mailbox control_, not physical identity. If someone takes over your email account and forwards the intent line, that is a valid signature from kysigned’s perspective, the same way a forged handwritten signature is valid if it fools the notary. This limitation applies to every digital signing product.
- **Legal enforceability in your jurisdiction.** Electronic-signature law varies. kysigned provides cryptographic evidence (an SES/AES-grade record under eIDAS; not a QES); legal interpretation is jurisdiction-specific. See our [Terms of Service](/terms.html).

## Threat model

| Concern | What stops it |
| --- | --- |
| The operator forges a signature | Impossible: a valid signature needs a DKIM signature from the signer’s provider, and the operator doesn’t have the provider’s private key. |
| The operator alters the document after signing | Impossible: the signer’s forwarded attachment is byte-bound by SHA-256 to the reconstruction cover-<n>.pdf ++ document-original.pdf; any change to the document is a different hash and fails verification. |
| The operator claims someone signed when they didn’t | Impossible: no forward from the signer’s mailbox means no DKIM signature, so there is nothing to put in the signing record. |
| The operator mints its own DKIM key and forges a signature | An operator can generate a keypair, list the public half in keys.json, and sign with the private half, which _is_ mathematically valid against that embedded key, so the signature math alone does not stop it. The **key-provenance gate** does: the archive holds the provider’s real published key, not the operator’s invention, so online the verdict is **FAILED**, and offline the record rises no higher than **INTEGRITY VERIFIED** and never claims the key was authentic. (A key genuinely compromised _during_ its live window is the same residual risk as any PKI.) |
| The provider rotates its DKIM key before you verify | The key is embedded in keys.json and confirmed against the archive’s historical record (which the archive built by fetching DNS itself), so the verifier never needs live DNS; a later rotation changes nothing. |
| A colluding or breached timestamp authority | Timestamps are dual: an RFC 3161 token _and_ an OpenTimestamps Bitcoin anchor. The durable tier requires the chain anchor, which can’t be back-dated, so a back-dated token the chain contradicts is graded _inconclusive_ rather than trusted; the TSA in turn gives an instant time for the few hours an OpenTimestamps proof is still pending. [Why two anchors](#why-two-timestamps). |
| Replaying or backdating a signature | Each .eml is unique and its timestamp is anchored to a Bitcoin block; the signing time can’t be moved. |
| Tampering with the signing record PDF itself | The verifier recomputes the evidence-set verification code; any edited byte diverges from the value printed on the signature page. |
| Spam or oversize abuse | Sending is credit-gated (spam is uneconomic), and a creation-time size guard rejects any record that would exceed the deliverability ceiling. |
| A stranger emails or probes the signing mailbox | Inbound is dropped unless it is a reply from a known participant on that document; bounces never leak signing state to non-participants. |
| A compromised signer mailbox | Out of scope for any e-signature product: see “What kysigned does NOT prove”. The evidence still records exactly which mailbox signed and when. |

## Forked instances stay independent

kysigned is Apache-2.0-licensed and self-hostable. A forked instance shares **no secret and no central registry** with kysigned.com or any other deployment. Each operator runs under its own domain and infrastructure, and trust still flows only from the signer’s provider, the public archives, the timestamp authorities, and the math. A signing record produced by any instance verifies the same way, anywhere, offline, because the trust set never included the operator in the first place. Forking changes who runs the plumbing; it changes nothing about what a signature proves or how it is checked.

## Open-source components and audit posture

Everything on this page is meant to be checked, not taken on faith. The whole stack is Apache-2.0 open source, and the parts that matter for trust are deliberately **not** things we invented:

- **DKIM / SPF / DMARC verification:** `mailauth`, the mainstream, widely-deployed open-source mail-authentication library. We do not hand-roll signature verification.
- **Timestamping:** standard RFC 3161 tokens and the public OpenTimestamps protocol (Bitcoin), both open, independently-implemented standards.
- **PDF assembly and parsing:** `pdf-lib`, a widely-used open-source library.
- **The verifier:** the browser verifier, the command-line verifier, and an independent verification toolkit all run the same documented algorithm and are open source, so anyone can read exactly what “verified” means, reproduce it, and cross-check one implementation against another.

Our audit posture is honesty about it: we rely on **widely-used components that get many eyes** from the broader ecosystem rather than commissioning private audits, and we keep all trust-boundary code (verification, crypto, key handling) on those reviewed libraries, never on self-rolled substitutes. Because a signing record is verifiable from public inputs alone, the ultimate audit is the one you can run yourself: re-verify any record and check our claims against the math directly. The full source lives in the Apache-2.0 public repository.

kysigned is not a substitute for legal advice. It is your responsibility to determine whether electronic signatures are legally valid for your use case, jurisdiction, and industry. See our [Terms of Service](/terms.html).
