Skip to main content

Forms, Review & Compliance

The capabilities that let .it replace the business documents people still keep in Word and PDF — all on the same queryable, sealable format. Everything here is in @dotit/core (1.12+) unless noted.

Forms — fillable, signable documents

A meta: type: form document with input: fields is a form: design it, send it, a recipient fills it, and a complete form (all required fields answered) stops being a template and becomes a final, signable record. (For the conceptual introduction, see Concepts §7 — Forms.)

meta: | type: form
title: Vendor Onboarding
input: Legal name | key: legal_name | type: text | required: yes
input: Country | key: country | type: choice | options: KW, SA | required: yes
input: VAT number | key: vat | type: text | show-if: country = SA
input: Quantity | key: qty | type: number | value: 4
input: Total | key: total | type: number | compute: qty * 250
input: Evidence | key: cr | type: attachment
output: Net total | value: {{total}}
import {
isFormComplete, missingRequiredFields, applyAnswers, formAnswers,
formVisibility, computeFormValues, buildSubmission, submitForm, sealDocument,
} from "@dotit/core";

missingRequiredFields(form); // ["legal_name","country"] (hidden/computed skipped)
const filled = applyAnswers(form, { legal_name: "Dalil", country: "KW" });
isFormComplete(filled); // true → signable
formAnswers(filled); // { legal_name, country, qty, total: "1000" }
formVisibility(filled).vat; // false (country ≠ SA → field hidden)
computeFormValues(filled).total; // "1000"
const record = sealDocument(filled, { signer: "Dalil" }).source; // tamper-evident

// Hand the answers to a backend:
buildSubmission(filled); // { source, answers, hash, submittedAt } — no network
await submitForm(filled, { endpoint: "https://api/forms" }); // validates complete, then POSTs

Field types: text, textarea, date, number, choice, checkbox, signature, table, attachment. show-if: shows a field only when a condition holds; compute: derives a value from other fields (a safe arithmetic evaluator — never eval); output: displays a computed/summary value. buildSubmission packages the answers (with the content hash) for your backend; submitForm checks the form is complete and POSTs that payload, returning a structured result (it never throws on an HTTP error — check ok).

Two-party trust

A sent form has two trust questions, by two parties. The author seals the blank form's structure (a hash that ignores answers, so it survives filling); the filler seals the completed record (the answers).

import { sealFormStructure, verifyFormStructure } from "@dotit/core";
const { source: blank } = sealFormStructure(form, { sealer: "Acme HR" });
// recipient fills + seals; both layers verify, independently:
verifyFormStructure(record).intact; // structure unchanged (author's claim)
verifyDocument(record).intact; // answers untampered (filler's claim)

Attachments — .it as a container

A form (or any document) can carry a file. Prefer href: (a reference) to keep the document lean and queryable; embed (base64, ≤ 1 MiB) only when it must be self-contained — the seal then covers the bytes.

import { addAttachment, getAttachment, attachmentDataUri } from "@dotit/core";
let s = addAttachment(form, { key:"cr", name:"cr.pdf", mime:"application/pdf", size:0, href:"https://store/cr.pdf" });
s = addAttachment(s, { key:"id", name:"id.png", mime:"image/png", size:1234, data: base64 });
attachmentDataUri(getAttachment(s, "id")); // a data: URI for download/preview

Redline & version compare

Word-style tracked changes ([new]{track: ins} / [old]{track: del}) + comments, and a one-call diff of two versions:

import { compareVersions, acceptChanges, rejectChanges } from "@dotit/core";
const redline = compareVersions(oldVersion, newVersion); // a tracked-changes .it
acceptChanges(redline); // === newVersion (rejectChanges → old)

In the editor, <IntentTextWorkbench mode="review"> renders the accept/reject UI; File ▸ "Compare versions" runs compareVersions for you.

Co-authoring (async merge)

mergeThreeWay(base, mine, theirs) merges two independent edits into one redline: non-overlapping edits apply automatically; a region both sides changed differently is a conflict offering both variants for a human to resolve. "Git-merge for documents, but readable."

Redaction

Legally remove content (FOIA / privacy / discovery) — not CSS-hide it.

import { applyRedactions, verifyRedaction } from "@dotit/core";
// author marks: text: The agent [John Carter]{redact: PII} met the source.
const { source, receipts } = applyRedactions(marked); // the text is GONE; black-bar marker
verifyRedaction(receipts[0].commit, "John Carter", receipts[0].salt); // prove coverage later

Each marker commits to a salted hash of the original; keep the receipts private to later prove a redaction covered exactly a given text, without the document revealing it.

Math

math: E = mc^2 # a display equation
mass-energy [E = mc^2]{math: tex} is famous. # inline

Core marks math (a dependency-free data-tex placeholder); render it with @dotit/math:

import { renderMathInHtml } from "@dotit/math"; // lite MathML, or full KaTeX if installed
const html = await renderMathInHtml(coreHtml);
  • PAdES — export a sealed .it as a PDF signature Adobe and courts recognize (@dotit/pades / @dotit/pdf renderSignedPDF): ECDSA P-256 + X.509 + CMS, optional RFC-3161 timestamps. A signing cert can be self-issued or issued by the UTS X.509 CA.
  • PDF/A — archival PDF for auditors (@dotit/pdf toPdfA): XMP + sRGB OutputIntent
    • document ID; compliance validated in CI with veraPDF.
import { renderSignedPDF, toPdfA } from "@dotit/pdf"; // Node
await renderSignedPDF(src, { signer: { certPem, privateKeyPem, tsaUrl } });
await renderPDF(src, { pdfA: { iccProfile, conformance: "3B" } });

Embed it all

Every mode is in one React component — <IntentTextWorkbench mode="edit|fill|view|review|auto">. See the full developer guide at ERP integration and Ecosystem.