Skip to main content

Sealing Contracts

The problem

You need to prove a document hasn't been tampered with since it was signed. If someone changes a word, you need to know.

The solution

dotit seal computes a SHA-256 hash of the document content, adds a sign: block and a freeze: block. dotit verify checks the hash against the current content.

Seal

dotit seal contract.it --signer "Ahmed Al-Rashid" --role "CEO"

This adds to the document:

sign: Ahmed Al-Rashid | role: CEO | at: 2026-03-22T15:00:00Z | hash: sha256:a1b2c3d4e5f6a7b8
freeze: | status: locked | at: 2026-03-22T15:00:00Z | hash: sha256:a1b2c3d4e5f6a7b8

Verify

dotit verify contract.it

Output when valid:

✓ Document sealed at 2026-03-22T15:00:00Z
✓ Hash valid: sha256:a1b2c3d4e5f6a7b8
✓ 1 signature: Ahmed Al-Rashid (CEO)
✓ No amendments

Output when tampered:

✗ SEAL BROKEN
Expected: sha256:a1b2c3d4e5f6a7b8
Actual: sha256:9c8d7e6f5a4b3c2d
The document has been modified since sealing.

What the hash covers

The hash is computed from document content above the history boundary, excluding trust metadata lines:

  • title:, summary:, meta: blocks
  • All section content
  • All block content and properties
  • approve: blocks
  • Layout blocks (page:, font:, etc.)

The hash does not cover:

  • sign: lines (stripped before hashing)
  • freeze: lines (stripped before hashing)
  • amendment: lines (stripped before hashing)
  • The history: boundary and revisions below it

This is what makes amendments possible: amendment: lines are stripped before hashing (like sign: and freeze:), so adding one never breaks the original seal.

Multiple signatures

A document can have multiple signers:

# First signer
dotit seal contract.it --signer "Ahmed Al-Rashid" --role "CEO, Acme Corp"

# Second signer (adds another sign: block, re-computes freeze:)
dotit seal contract.it --signer "Maria Santos" --role "COO, GlobalTech"

After both:

sign: Ahmed Al-Rashid | role: CEO, Acme Corp | at: 2026-03-22T10:00:00Z | hash: sha256:a1b2c3d4
sign: Maria Santos | role: COO, GlobalTech | at: 2026-03-22T14:30:00Z | hash: sha256:e5f6a7b8
freeze: | status: locked | at: 2026-03-22T14:30:00Z | hash: sha256:e5f6a7b8

Verification in code

import { verifyDocument } from "@dotit/core";

// verifyDocument takes the raw .it source string — the hash covers exact bytes
const result = verifyDocument(source);

if (result.intact) {
console.log("Seal intact:", result.hash);
for (const s of result.signers ?? []) {
console.log(`${s.signer} (${s.role}) — valid: ${s.valid}`);
}
} else {
console.log("SEAL BROKEN");
console.log("Expected:", result.expectedHash);
console.log("Actual: ", result.hash);
}

To seal in code, use sealDocument(source, { signer, role }) — it returns { success, hash, source, at }. Store the returned source exactly as-is (no trimming, no CRLF conversion): the hash covers the exact bytes.

Complete workflow

# 1. Write the contract
# 2. Review and add approvals (manually or via editor)
# 3. Seal
dotit seal contract.it --signer "Ahmed Al-Rashid" --role "CEO"

# 4. Send to counterparty, they seal too
dotit seal contract.it --signer "Maria Santos" --role "COO"

# 5. Verify at any time
dotit verify contract.it

# 6. View full history
dotit history contract.it

Next steps