Notarizing documentation
User guide
Developer preview. Not yet production ready. This page is rendered from docs/user-guide.md of the Notarizing repository at revision d23579e737f9d1e16e07b3c6b21a51282d64027e. It describes the behavior of that revision.
Contents
This guide shows how to review an OpenSpec change with notarizing. You need Go to build the binary and Git to read your repository. To build the browser interface from source, you also need Node 22.12 or later at build time. You need no broker, no model and no Node at run time.
Notarizing reads your repository. It never edits it, never runs your checks and never fetches anything. Imported reports, repository text and review text are data. Notarizing never runs them as instructions.
1. Set up a workspace
A workspace is one directory that holds one SQLite database. One process owns it at a time.
Build and install the binary with the browser assets:
make installmake installrunsmake weband thengo install.make webinstalls the locked npm packages and builds the browser interface intointernal/web/dist.go installembeds it into the binary and writes the binary to the directory ofgo env GOBIN. IfGOBINis empty, the directory is thebindirectory ofgo env GOPATH.Add the install directory to
PATH, and check that the shell finds the binary:export PATH="$(go env GOPATH)/bin:$PATH" notarizing versionIf you set
GOBIN, add that directory instead. The rest of this guide runsnotarizingfromPATH. To use the build ofmake webandmake buildin its place, runbin/notarizing, or add thebindirectory of the checkout toPATH.Without the browser assets,
serveshows a notice page in place of the review interface, anddoctorgives the warningbrowser assets. The CLI and MCP work without the browser assets.If you cannot read the private ka2a module, install the build without the ka2a adapter:
make install TAGS=noka2a. That build needs no ka2a module. Itska2a reloadandserve --ka2a-configfail with exit code 5.Create the workspace:
notarizing --workspace ~/reviews/ws initThe command makes the directory with mode 0700 and the database with mode 0600. If the directory holds other files, the command refuses it.
Set the workspace for the shell (optional):
export NOTARIZING_WORKSPACE=~/reviews/wsWithout
--workspaceand without this variable, notarizing uses./.notarizing.Check the setup:
notarizing doctorThe exit code is 1 when a check has the status
error.
2. Import a change
Register the local Git repository under an ID of your choice:
notarizing repo add payments ~/src/paymentsThe ID has 1 to 64 characters: lower-case letters, digits,
.,_and-. The first character is a letter or a digit. If the checkout moves or you clone it again, point the ID to the new path:notarizing repo set-path payments ~/work/paymentsThe command checks that the new path is a Git repository with the same object format that contains the latest imported commit. Otherwise it changes nothing and fails with
conflict. There is norepo remove, because stored history names the repository ID. If the workspace may no longer show a repository, the operator revokes its source access withnotarizing repo access revoke. See the operator guide.List the changes at a commit:
notarizing change list --repo payments --at mainImport one change at an exact revision:
notarizing change import --repo payments --at main --change add-refund-limits--attakes a branch, a tag or a full commit ID.HEADis not accepted. The import resolves the locator to one commit and stores that commit. If the branch moves later, the stored target does not change. Import the change again to review the new commit. If the locator does not resolve, nothing is stored.Read the change report:
notarizing change report --repo payments --change add-refund-limitsThe report is useful before any check report exists. Each requirement without a binding or a report shows as unassessed.
A requirement without a Requirement-ID line has a provisional identity. A provisional
requirement never matches a requirement of another revision. Review shows the badge
Provisional in place of the ID, the inspector shows its provisional reference, and the
Diagnostics tab lists it. The same block at two revisions is two objects.
3. Review in the browser
Start the owner process:
notarizing serveservelistens on 127.0.0.1 only. It prints the URL and a one-time viewer secret to standard error. Use--opento open the system browser. Use--port 0to select a free port.Open the URL, paste the secret and select Open workspace. The secret works once. The page shows your browser session ID (
ses_...) under "This session". To open another tab or browser, use the next secret in the fileviewer.tokenof the workspace.
While serve runs, other notarizing commands send their imports through its control API.
Read commands read the workspace read-only.
Address, localhost and SSH
The server answers only at the exact address that serve prints, for example
http://127.0.0.1:7373/. It refuses every other host name with 403, also localhost and
127.0.0.1 with another port. This check stops DNS rebinding attacks, so it cannot be
turned off. A page request with a wrong address shows a short page that names the correct
address. An API request gets the error forbidden with the reason host.
If serve runs on another computer, forward the same port number over SSH:
ssh -L 7373:127.0.0.1:7373 build-host
Then open http://127.0.0.1:7373/ on your computer. The local port must be the same as the
server port. A different local port, for example -L 8080:127.0.0.1:7373, gives the refusal
page, because the browser then sends the Host 127.0.0.1:8080.
Review mode
Review lists each effective requirement of the selected change and snapshot. Each row shows:
- the exact source of the requirement;
- the assessment rows: the reported claim beside the evaluated status;
- the missing dimensions, for example a required configuration without a report;
- the stale reasons, for example a report about an older implementation revision;
- the contradictions: applicable failing and passing reports of one row;
- the assumptions, premise groups and open questions.
Select a row and press Enter to open the inspector. The inspector shows the requirement block, its evidence history, its review history and its relationships. Press Escape to go back.
A reported pass is not an established pass. The evaluated status is pass only when the
policy trusts the source for the method and an approved evaluator made the report.
Each row of the evidence history names the source that submitted the receipt: the channel, the source namespace and, for a signed ka2a transport, the verified principal. A verified principal names the producer. It is not evaluator evidence: a signed proof claim stays unknown until a report of an approved evaluator exists.
A review decision applies only to the target where it was made. When the wording of a premise changes, the new target starts the premise open. The inspector shows the earlier decision with the warning that the source changed after it, and the Diagnostics tab lists the premise. Record a new decision at the new target.
The selected change, target, item and review checkpoint are in the address of the page. After an import with the CLI, reload the page: it opens the same selection with the new evidence.
Explore mode
Explore shows a bounded dependency graph around a root. Select the view:
dependencies: the premises and requirements that the root depends on;impact: the claims that depend on the root;gaps: requirements without approved coverage and assumptions without review;selection: every relationship near the root.
Use the depth control and the relation filter to make the graph smaller. Machine suggestions are hidden by default. Select "Show suggestions" to see them. A suggestion is never followed as a dependency and never gives coverage. Select "Table" for a table of the same nodes and relationships.
The product makes one kind of suggestion: similar wording (notarizing.suggest/1). It
compares the wording of the requirements of the target in the search index. The lexical
method always runs. The embedding method runs only when the operator enabled the local
provider and built the semantic index; it uses the stored vectors and never calls the
model. Without a current search index there are no suggestions. In the inspector of a
requirement, select "Show suggestions" under "Suggested similar wording" to see its
suggestions with their method and score. A score is a similarity, not a confidence. To
keep a suggestion, a reviewer selects "Propose a link". This sends the normal
relationship.propose command with a rationale. The suggestion itself never becomes a
link, a binding or coverage. In the CLI, run
notarizing graph suggestions --target tgt_ID. A view that does not fit its limits says that it is partial
and gives a next page. A graph is a review aid. Its reachability never establishes correctness.
Compare mode
Compare shows two exact revisions of a change side by side. It keeps these aspects apart: wording, assumptions, premise groups, relationships, review decisions and evidence applicability. Rows match only by stable ID. A model result can stay valid while the implementation correspondence is stale. Compare shows these two facts in separate columns.
To compare from the CLI:
notarizing compare --left tgt_LEFT --right tgt_RIGHT
To see which claims a draft change of assumptions could affect, use a what-if draft. The draft stores nothing:
notarizing graph whatif --target tgt_ID --remove ASM-FSYNC
notarizing graph whatif --target tgt_ID --replace "ASM-FSYNC=Fsync survives power loss on ext4"
The result lists the claims that are potentially affected under recorded dependencies and the premise groups that change. It does not invalidate historic evidence.
4. Give a review grant
A browser session can read. It can write review records only with an expiring grant from
the CLI. Run the grant in a second terminal while serve runs:
notarizing review session grant --session ses_ID --ttl 30m --scope change:payments/add-refund-limits
The scope is workspace, repo:<repository_id> or change:<repository_id>/<change>. The
time to live is 1 minute to 8 hours. A reviewer can add notes, decide on assumptions,
propose and review relationships, and write wording drafts. A grant never allows imports,
policy or binding changes, purges, provider changes or source edits.
To name the reviewer in review events and exports, add --actor "Alice Example". The events
then show Alice Example (unverified, browser session XXXX). Notarizing does not verify the
name, and the name changes nothing that the grant allows.
To end a grant:
notarizing review session revoke --session ses_ID
A restart of serve ends every session and grant.
If two tabs change the same object, one save commits and the other gets a conflict. The conflict shows the current revision and keeps your text. Read the new version before you submit again. A draft is "saved" only after the server confirms it.
5. Import evidence
Evidence is a notarizing.check-report/1 report with its artifacts. See the
evidence producer guide for the format.
notarizing evidence import --artifacts ./artifacts report.json
Every declared artifact must be below the --artifacts directory with its declared length
and SHA-256. A bad artifact rejects the whole report. The same report again returns the
same receipt. The same report_id with other bytes is a conflict.
To import several reports, name several files, or name the output directory of the example
producer gotestreport with --dir:
notarizing evidence import --dir ./reports
Each report is imported alone with its own receipt. If one report fails, the others are
still imported. The command then lists each file with accepted, failed or
not_attempted and exits with a non-zero code. The exit code is 4 when an outcome is
unknown. Run the same command again: accepted reports return the same receipts.
A local file import has the attribution manual_unverified. Its reported pass is "not
independently established" until the policy trusts the local source.
To read a receipt:
notarizing evidence show RECEIPT_ID
notarizing requirement history --repo payments RFD-R1-001
A producer cannot change a report. To retract or correct a result, the producer submits a new report. Then record the relation:
notarizing evidence correct --corrected OLD_RECEIPT --correcting NEW_RECEIPT --relation corrects --reason "Wrong fixture."
A correction keeps both receipts. It does not retire a failure.
Only a reviewed supersession retires a corrected failure. A reviewer with a review grant
opens the requirement in the browser inspector and selects Retire failure by
supersession. The button shows only for a failing receipt that a correction links to a
later receipt. Enter the reason. The browser records two review events: a supersedes
proposal from the later receipt to the failure, with the requirement and the dimension, and
its acceptance. The server accepts them only when the correction exists and the later
receipt is eligible and applicable under the policy. Then:
- the assessment of that target uses the later receipt and marks the failure "Superseded by review";
- both receipts stay, and the evidence history names the reviewer and the reason;
- a view at an earlier review checkpoint shows the earlier result;
- a later
rejectorinvestigatedecision on the relationship revokes the effect.
evidence show, requirement history and change report show the supersession. The CLI
cannot record one: it is a review decision.
6. Set the policy and the bindings
A binding links a requirement to an approved check. Only binding import creates bindings.
A declared Evaluated-By link, a review decision or a report never does.
notarizing binding import bindings.json
notarizing binding show --repo payments
A bindings file (notarizing.bindings/1) names the repository, the checks with their method,
target kind and approved evaluators, and the bindings with their required configurations.
See examples/refund-limits/evidence/bindings.json.
The JSON Schema bindings.schema.json describes the format for
editors and other tools; binding import checks more, for example a check ID defined twice.
The policy (notarizing.policy/1, JSON Schema policy.schema.json) names
the trusted source namespaces with their methods and the reviewed not_applicable exclusions:
{
"schema_version": "notarizing.policy/1",
"trusted_sources": [
{"source_namespace": "local", "methods": ["implementation_test", "integration_test"]}
],
"exclusions": []
}
notarizing policy set policy.json
notarizing policy show
A policy set with a changed policy stores a new version. The same policy again keeps the
current version. The browser can never change the policy.
7. Read the change report
The change report gives no overall pass badge. Read each requirement row by row.
| Evaluated status | Meaning |
|---|---|
pass | An eligible, applicable and complete report supports this dimension under the selected policy. |
fail | An eligible, applicable and complete report establishes a scoped violation under the selected policy. |
stale | The report is about another revision of the requirement, the implementation, the model inputs, the correspondence inputs or the checker. |
unknown | No conclusion: for example an untrusted source, an incomplete run, an integrity failure or a report without a conclusion. |
not_applicable | A reviewed policy exclusion makes the check not applicable. |
Each row also has a coverage value: complete, partial or not_assessed. The reasons
of a row say why it has its status. A later pass never clears an earlier failure. Both
stay visible as an unresolved contradiction until a reviewed supersession resolves it.
Use --format markdown or --format json for a document. Use --checkpoint N to pin a
review checkpoint.
8. Export
Each export pins one target and one review checkpoint. The same request gives the same bytes.
A graph as Mermaid, Markdown or
notarizing.graph/1JSON:notarizing graph export --target tgt_ID --object RFD-R1-001 --view dependencies --format mermaid --output graph.mmdThe review of a change as Markdown or JSON:
notarizing review export --repo payments --change add-refund-limits --checkpoint latest --output review.mdA wording draft as a proposal-only patch:
notarizing review patch --draft EVENT_ID --base FULL_SHA --output change.patchThe patch replaces the complete requirement block and keeps every other byte of the file. If the base file differs from the file of the draft, the command reports a conflict. Notarizing never applies the patch. Review it against your working tree, then apply it yourself.
In the browser, the Export dialog writes the same formats for the current view. The inspector shows the before and after text of a wording draft.
9. Search
notarizing search --query RFD-R1-001
notarizing search --query "refund limit" --kind requirement
An exact Requirement-ID or declaration ID comes first. Then come FTS5 full-text results.
A missing or stale index shows as coverage pending, partial or stale, never as "no match".
To rebuild an index, run notarizing search index --target tgt_ID.
An index is partial when it omits part of the selected source, for example a change document that is not valid UTF-8. A search with no result in a partial index says that the index is incomplete. It does not say that nothing matches.
In the browser search dialog, the scope "Only the items of this change" reads like a change-scoped graph: the accepted baseline and the review notes on it are hidden. Search then shows no result, excerpt or count of a hidden item.
Without a configured embedding provider, the browser search dialog disables the Hybrid option and says why. Lexical search works without a network call.
The query is sensitive. Shell history can keep --query. For sensitive text, use
--query-stdin.
Hybrid search (--mode hybrid) needs the local Ollama provider. It is off by default. See
the operator guide.