JsonUI
← ConceptsImplementation contract checkDocs (spec / swagger / DB models) are the source of truth for what gets generated. `jsonui-doc check` verifies that the real running implementation still matches those docs — schema by schema, endpoint by endpoint. Here is the design and how to read the reports.~7 min read
The drift you can't see`jui verify --fail-on-diff` catches DTO regeneration drift inside the docs→code pipeline: if `docs/api/*.json` changes and the generated `User.swift` no longer matches, verify fails. But there is a second gap it can never see — the gap between the docs and the running server. A backend engineer renames a field, adds a required parameter, or ships an enum value that isn't in the swagger, and every generated DTO on iOS / Android / Web is now silently wrong. The same gap exists for `docs/db/*` versus the live database. Contract check exists to close that outer gap: it compares docs against what the real implementation reports about itself.
check is a producer, generate is a renderer`jsonui-doc generate html` never touches the network. Clone the repo, run `generate html`, and you get the site — no config, no credentials, no third-party code execution. That property is a hard invariant. `jsonui-doc check` is the opposite: it explicitly runs whatever commands your `jui.config.json` declares under `checks`, with timeouts and a `--list` that shows you what will run before it runs. Results are saved as `docs/**/.check-report.json`. `generate html` then reads those reports if they exist and renders them as an 'Implementation contract' page — but generate itself still executes zero external code. The split matches the pytest / mypy relationship: check is the test command, generate is the render command, and shipping a docs build never requires proving that the impl is currently in sync.
Three confidence levelsEvery result carries a `confidence` field with one of three values. `proof` means declaration-vs-declaration matching — impl-side OpenAPI vs docs/api, real RDB schema vs docs/db. If proof-level check says OK, the schemas match, full stop. `metadata` means partial matching against structural metadata — e.g. Firestore composite index definitions, DynamoDB DescribeTable output. `sampled` means the checker looked at real data (usually a bounded sample like 1,000 documents) and found no violations; this catches most drift but is not a proof — a rare violation could still be lurking. The distinction is honest: 'no mismatch on a sample of 1,000' is displayed differently in the HTML than 'the schemas are identical.'
Adapter type + full-checker typeTwo ways to plug in. Adapter type is the recommended entry point: you write a tiny command that just outputs facts in the required format (e.g. a Python one-liner that dumps FastAPI's `app.openapi()` to stdout, or a shell script that dumps the DB schema as JSON). The library takes it from there — comparison, judgement, report generation. Full-checker type is for cases adapter can't express: you write a command that does the comparison itself and outputs a result JSON that conforms to the `.check-report.json` schema. Full-checker is where you go when you need to hit a live API with auth, or verify a semantic invariant (like 'this column is null only when the order is refunded') that no declaration file captures. Builtin checkers (`builtin:openapi-diff`, `builtin:db-schema`) plug into the same socket as your adapter — they are 'bundled plugins,' not a privileged path.
Reading the implementation-contract pageWhen a report exists, `generate html` renders an extra 'Implementation contract' page with these elements: a verification timestamp (e.g. '2026-07-07 14:00 vs main (MySQL 8.0), matched ✓'), the checker name and target, a confidence badge on every row, a list of mismatches (target / status / expected / actual / message), and a freshness indicator — if the docs have changed since the report was saved, the page shows a 'this report may be stale' banner (compared via `input_hashes` in the report). Exit codes match the `check` command: 0 = OK, 1 = mismatch, 2 = execution error (connection failure, timeout, malformed plugin output). CI gates read the exit code; `generate html` succeeds even when check exits 1 or 2 — you specifically want to see the mismatch page when things are broken.Since jsonui-cli 1.7.20 the summary line says what it counted and out of how many: `ok=4 … [4/4 operation]`, and `[3/4 operation, 1 excluded by config]` once something is filtered out. The reason to read that bracket is the case where it reaches zero — exclude every path and the counts are `ok=0 mismatch=0 … skipped=0`, which is the exact shape of a clean pass. A warning now says so in words, but the exit code is still 0, so a pipeline gating on the exit code alone treats 'nothing was compared' and 'everything matched' identically. The report's `inputs` block names the provenance for the same reason: `impl_openapi_sha256` is the hash of the payload that was actually compared, so two runs agreeing on it compared the same implementation contract. What it deliberately does not give you is the implementation's revision — the command may run in a container or against another checkout, so `doc_source_rev` names the docs side and is labelled as such, and it is absent rather than guessed when the docs are not a git checkout (measured: the key appears only once the fixture is a repository).
jsonui-doc check — the same green, three different amounts of checking
# four operations in docs/api, nothing excluded
✓ ok ok=4 mismatch=0 missing_in_impl=0 missing_in_doc=0 skipped=0 [4/4 operation]
 
# ignore_paths: ["/internal/*"]
✓ ok ok=3 mismatch=0 ... skipped=0 [3/4 operation, 1 excluded by config]
 
# ignore_paths: ["/api/*", "/internal/*"] — every count is zero, and so is the exit code
✓ ok ok=0 mismatch=0 ... skipped=0 [0/4 operation, 4 excluded by config]
(warning) 4 operation(s) are declared in docs/api but NONE were compared —
every one was excluded by configuration. This is not 'the contract
matches'; nothing was checked.
What check cannot catchThe OpenAPI diff checker compares the impl's *declared* OpenAPI against docs — it does not send a single HTTP request. If the impl's OpenAPI says a response has fields `{a, b, c}` but the actual JSON on the wire has `{a, b, d}`, check will not see that. Verifying real responses (with auth, seed data, and network) is the full-checker territory. Similarly for DB: the schema checker verifies structure (columns, types, indexes) — it does not check that a given value in a given row is 'semantically correct.' The default type comparison is family-lenient (e.g. `integer` matches INT / BIGINT / SMALLINT); to force exact-match on a specific column, annotate it with `x-db-type: 'DECIMAL(10,2)'`. Naming the limits up front prevents false confidence — check is a strong tool, not a magic one.
Read next
DB schema check (docs/db ⇔ live DB)Deep dive on the DB side: the docs/db table JSON format with its x-* extensions, exactly what the checker compares, and how to declare and run it./concepts/db-schema-check
Verifying implementation against docsCookbook: declaring checks in jui.config.json, wiring up FastAPI / Spring / NestJS / Rails adapters, running check locally and in CI, and reading the resulting HTML./guides/verifying-implementation-against-docs
CLI command referenceComplete reference for `jsonui-doc check`, including all subcommands, positional filters (`db` / `api` / `db:main`), the `--list` and `--with-checks` flags, exit-code semantics, and the config schema for `checks` and `databases`./reference/cli-commands