Docs

What FastYoke records, how the audit chain is sealed, what the platform claims (and explicitly doesn't), and how a third party verifies a sealed envelope offline.

Trust and verify

This page covers what the platform records on every signing event, how the evidence is sealed, how a third party verifies a sealed envelope independently, and what posture FastYoke takes on the legal status of those signatures.

What the platform records

For every signer on every envelope:

  • Email of record (the address the invitation was sent to)
  • IP address at the time of signing
  • User-agent string
  • Consent disclosure version and content hash
  • Agreed-at timestamp (when consent was acknowledged)
  • Signed-at timestamp (when the signature was captured)
  • The signature image (PNG)

For the envelope as a whole:

  • The source document_sha256 — a SHA-256 of the exact bytes that will be sealed (post-overlay for form-generated PDFs; pre-stamping for uploaded PDFs is also recorded)
  • An append-only esign_audit_log containing every event in chronological order with a hash chain
  • The signing manifest (described below)
  • The ed25519 seal over the manifest

The Certificate of Completion

When the envelope finalizes, the platform renders a Certificate of Completion as a Typst-generated PDF. It lists every signer, every event from the audit log with timestamps, the document document_sha256, the manifest, and the ed25519 signature.

The Certificate is attached to the envelope as a separate file. The admin and every signer can download it from the public verify page.

The audit log structure

esign_audit_log is append-only at the database layer. Every row carries:

  • The event kind (signer-added, consent-agreed, signature-captured, voided, declined, finalized, …)
  • A canonical-JSON payload describing the event
  • A row_hash = SHA-256(prior_row_hash || canonical_payload)

This hash-chaining property means a single tampered row breaks the chain at every row that follows it. The final row's row_hash is the chain_head — the value folded into the manifest before the seal.

There is no UPDATE or DELETE path on the audit log table. The signature-sweep job only inserts rows for scheduled events; it never mutates existing rows.

Sealing — what we sign and how

When the last required signer signs, the platform builds a canonical-JSON manifest containing:

  • document_sha256 (hex)
  • chain_head (hex) — the final audit-log row hash
  • signers[] — for each signer: email, signed-at timestamp, consent disclosure version, IP, user-agent

The manifest is then serialized to canonical JSON exactly once; the resulting bytes are stored verbatim on the envelope as manifest_json, and the same bytes are signed with an ed25519 key. Verification recomputes the signature against the literal manifest_json bytes — never re-canonicalize, because any whitespace, key ordering, or escape difference would break the signature.

The signing key is platform-managed and rotated on a schedule. Past public keys remain available via the verify-keys endpoint so older envelopes stay verifiable indefinitely.

Public verify endpoint

A third party — a court, a buyer's counsel, a compliance system — verifies a sealed envelope without calling FastYoke at runtime. The endpoints below are public; no authentication is required.

Fetch the verification record:

GET /api/v1/public/esign/verify/:verification_id

Response includes the envelope summary, the list of signers, the manifest bytes, the ed25519 signature, the audit chain, and the public-key id used to sign.

To verify the document hash against a copy you have:

POST /api/v1/public/esign/verify/:verification_id
Content-Type: multipart/form-data

<the PDF you want to verify>

The platform computes the SHA-256 of the uploaded bytes and compares against the manifest's document_sha256.

Fetch the platform's published ed25519 public keys for offline verification:

GET /api/v1/public/esign/verify-keys

Response is a JSON list of { key_id, public_key_base64, valid_from, valid_to } records. A verifier reads the response, picks the key whose id matches the verification record, and checks the signature.

Offline-verification recipe (JavaScript)

import nacl from 'tweetnacl';
import { decodeBase64, decodeUTF8 } from 'tweetnacl-util';

async function verifyEnvelope(verificationId, pdfBytes) {
  const v = await fetch(
    `https://verify.fastyoke.io/api/v1/public/esign/verify/${verificationId}`,
  ).then((r) => r.json());

  const keys = await fetch(
    'https://verify.fastyoke.io/api/v1/public/esign/verify-keys',
  ).then((r) => r.json());

  const pk = keys.find((k) => k.key_id === v.key_id).public_key_base64;

  const sigOk = nacl.sign.detached.verify(
    decodeUTF8(v.manifest_json),
    decodeBase64(v.signature_base64),
    decodeBase64(pk),
  );

  const docHash = await crypto.subtle.digest('SHA-256', pdfBytes);
  const docHashHex = Array.from(new Uint8Array(docHash))
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');

  const docOk = docHashHex === v.document_sha256;

  return { sigOk, docOk };
}

The tweetnacl library implements ed25519 in pure JavaScript and works in Node, browsers, and Deno. Any ed25519 implementation that accepts raw 32-byte public keys and 64-byte signatures will also work.

What the platform captures on every signed envelope is intended to satisfy the evidence chain commonly accepted for ESIGN/UETA in US jurisdictions: a record of the signer's identity, the signer's intent to sign, the consent disclosure that was shown, the IP and user-agent at the time of signing, and a tamper-evident seal over the audit chain. The platform publishes the ed25519 public key so the evidence is independently verifiable.

Identity in the current implementation is email-control baseline — the signer demonstrates control of the email address the invitation was sent to. This is the same identity baseline most e-signature platforms ship by default. Stronger factors (SMS verification, knowledge-based authentication, PKI certificates) are described in the next section as explicit non-features today.

Use of FastYoke-sealed documents in a specific legal matter is for counsel to advise on. The platform produces the evidence; the sufficiency of that evidence for a particular dispute, in a particular jurisdiction, against a particular counter-party, is a legal judgment.

What's deferred

The following are explicit non-features today. Customers who need them should plan accordingly until the platform ships them.

  • PAdES — PDF Advanced Electronic Signatures. The platform's ed25519 seal is platform-managed and stored alongside the document, not embedded as a PAdES signature in the PDF itself.
  • RFC-3161 external timestamping. Timestamps are platform clock; no third-party time-stamping authority is invoked.
  • SMS identity factor. The signer is not required to receive or enter an SMS code to sign.
  • KBA — knowledge-based authentication. The signer is not challenged with identity-verification questions.
  • File-level attachment encryption. Envelope attachments are stored under the platform's standard encryption-at-rest, not with per-envelope wrapping keys.

Customers in regulated environments (some healthcare consent scenarios, some real-estate jurisdictions) should evaluate whether the email-control baseline is sufficient for their use case before adopting.