# Offline: install once, run with no network The deadline example evaluates locally: after install, the run path uses no network. This page splits install from run, names the artifacts the run needs, says what `offline: true` means, and shows how to confirm the split — plus what still needs the network. ```text docs/build/examples/deadline-app ``` ## Install versus run Two phases, different needs: - **Install** needs the network: `npm ci` fetches `@arxo/law 0.3.3` and `@arxo/canon-bgb-fristen 0.1.5`, and the Python setup fetches `arxo 0.2.0`. This phase runs once per machine (and again on upgrades). - **Run** needs no network: opening the pinned model, validating forms, evaluating, reading answers, storing and replaying captures. Every answer on this path reports `via` as `local`. Keep the phases apart in procedure as well as in prose: install with the network up, then evaluate with it down when you want proof. ## Artifacts the run needs The run reads only what install placed on disk: - `node_modules` with `@arxo/law` and `@arxo/canon-bgb-fristen`: the facade, the canon bindings, and the engine files the canon package ships, including its packed runtime (wasm) — no download at open time. - the example sources: `src/*`, `public/index.html`, and for the Python path `python/deadline.py` with its installed `arxo` package. - a writable directory for `captures/.json`. Nothing else is read at run time: no registry, no remote canon, no assistant host, no serving endpoint. ## What `offline: true` means The model opens this way: ```text open('de.bgb.fristen@0.1.0', { offline: true }) ``` The flag pins the call path to local execution: the engine runs the installed canon build in-process and refuses any step that would reach the network. An answer from this path carries `via: local`, the three hashes, and the canonical document bytes — the same bytes on every run of the same inputs within one SDK. `offline: true` does not change the law: the collect value stays `2026-03-20` with `COMPUTED`, the truth stays `TRUE_ONLY`, and the wrong-date and partial cases stay `NEITHER`. It changes only where execution happens and what it may touch. ## How to confirm no network Two checks, both cheap: 1. **Read the run path.** From `src/server.ts` (or `src/cli.ts`) through `validateFormInput`, `toFacts`, `toCaseInput`, `buildCollect`, `buildTruth`, the `runtime` evaluate calls, `read-result`, `toViewModel`, `capture`, and `replay`, there is no registry call, no assistant-host call, and no serve call. The only I/O is the HTTP listener, the form file, and the capture files. 2. **Run with the network disabled.** Disconnect the machine (or run with loopback only) and evaluate the worked case: event `2026-03-06`, `14` days, candidate `2026-03-20`, legal time `2026-09-17`. Expect `COMPUTED` with `2026-03-20` and `TRUE_ONLY`, then replay the stored capture and expect the replayed `result` hash to match the recorded one. That comparison — result hashes, not byte-equality of the two documents — is the whole replay contract; the checksum step separately verifies the stored bytes. If either check fails, the failure names the leak: a network call in the path, or an answer that differs with the network down. ## What still needs the network Per operation, not per slogan — the two explanation calls are different operations with different network behavior: | Operation | Network? | Why | |---|---|---| | `npm ci` and package installs | Yes | fetch the pinned SDK and canon packages | | Python package installs | Yes | fetch `arxo` and the canon bindings | | `explainWhy` = `model.whyNot(...)` | No | local blocker graph from the installed engine | | `explainDraft` = `model.whyNot(...)` on a draft | No | same local call, fewer facts sent | | `explain()` through an MCP host | Yes | runs through the host over the carried document | | serving the canon over HTTP (`law serve`) | Yes | a network service by definition | | TS serve mode / router | Yes | every question goes to the server | The application never calls the network rows in its run path: the only explanation calls it makes are `explainWhy` and `explainDraft`, and both stay local — no case data leaves the process through them. The full matrix, both SDKs, lives on the compatibility page. Plan upgrades accordingly: bring the network up for install, confirm the pins, then bring it down again before the answers that must prove locality. ## Check your understanding Predict before you open each answer.
Does `explainDraft` need the network? No. It is the same local `model.whyNot(...)` call as `explainWhy`, with fewer facts sent. Only `explain()` through an MCP host needs the network.
With the network down, what does the worked case answer? The same as with it up: collect computes `2026-03-20` with `COMPUTED`, and truth on that date answers `TRUE_ONLY`. `offline: true` changes where execution happens and what it may touch, not the law.
After an offline run, what does replay compare? The replayed `result` hash with the recorded one — not byte equality of two documents. The checksum step separately verifies the stored bytes.
## Next - [Questionnaire: fields to facts, then follow-ups](/build/patterns/questionnaire/) - [Versions and capability matrix](/build/sdk/compatibility/) for the full per-operation network table