Skip to content
docs
Arxo ↗

Start: install, run, and test

For LLMs13 sections

This chapter runs the example application; the following chapters take it apart layer by layer. You start from an empty directory and finish with the form open in your browser and the first two answers on screen.

  • Goal: get the example application running from an empty directory and see the first computed answers — the end date of a 14-day period, and the check of a candidate end date.

  • You need: Node.js 20 or newer with npm, plus curl and unzip; no repository checkout. Versions are listed in the route overview.

  • Run:

    Terminal
    curl -O https://docs.arxo.io/build/deadline-app-0.1.0.zip
    curl -O https://docs.arxo.io/build/deadline-app-0.1.0.zip.sha256
    shasum -a 256 -c deadline-app-0.1.0.zip.sha256
    unzip -q deadline-app-0.1.0.zip
    cd deadline-app
    npm ci
    npm test
    npm start
  • Files: this chapter changes nothing; it runs what the archive contains.

    Output
    (none — this chapter only runs what the archive contains)
    deadline-app-0.1.0.zip 29 files: package.json, package-lock.json, tsconfig.json,
    README.md, src/*.ts (13), test/*.test.ts (8),
    test/fixtures (1), python/*.py (2), public/index.html

The starter needs Node 20 or newer (engines: >=20); for a new setup use a supported LTS, with Node 24 recommended. Any unzipper works in place of unzip: the archive is a plain zip. Python 3.12 or newer is needed only for the Python mirror. The section Choose a Node version below gives the support dates.

Download and verify the versioned archive (example 0.1.0):

Terminal
curl -O https://docs.arxo.io/build/deadline-app-0.1.0.zip
curl -O https://docs.arxo.io/build/deadline-app-0.1.0.zip.sha256
shasum -a 256 -c deadline-app-0.1.0.zip.sha256

Expected: deadline-app-0.1.0.zip: OK. (On Linux the same check is sha256sum -c.) The verified checksum is:

Output
4b8186fcb2caedf9316ff02fdca7032c6713bddc1fe672713fb44eebe0950411

Unpack and install the pinned dependencies from the lockfile:

Terminal
unzip -q deadline-app-0.1.0.zip
cd deadline-app
npm ci

npm ci installs exactly what package-lock.json pins: @arxo/law 0.3.3 with @arxo/canon-bgb-fristen 0.1.5, plus the pinned tsx/typescript/@types/node toolchain. No version floats.

Run the suite — a separate step, before any server:

Terminal
npm test
Output
# tests 63
# pass 63
# fail 0

63 tests pin every answer shape the later chapters rely on: 2026-03-20 for the example case, TRUE_ONLY on the right candidate, NEITHER on a wrong candidate and on partial facts, the draft questions from explain, and the capture/replay matrix. If any line fails here, stop: every later chapter assumes this suite holds. The last chapter lists each symptom with its cause and fix.

Start the server and see the first answers

Section titled “Start the server and see the first answers”

Start the server — it runs in the foreground, so this is its own terminal step:

Terminal
npm start
Output
deadline-app listening on http://localhost:8787/

Open http://localhost:8787/: the form, pre-filled with the example case. The command line runs the same two calls without the browser:

Terminal
node --import tsx src/cli.ts evaluate --event 2026-03-06 --days 14
node --import tsx src/cli.ts evaluate --event 2026-03-06 --days 14 --candidate 2026-03-20

The first call computes the end date. It prints the app’s reading of the answer — headline, rules, hashes; the full SDK shape arrives in chapter 3:

Output
The period ends on 2026-03-20
Computed from the event date and the duration by the pinned canon.
rules: TagesfristEnde
sources: No sources anchored in this canon build — the rule names above carry the provenance.
program: sha256:468e3fe17c6a28371c6ce8258ae7928c496593673d4b73a52c2a9c40cb50ee33
semantic: sha256:90e53b453107dfee9422a5fe24774823430b5c82c3577b9321a0838a6e54c695
result: sha256:ae9a7b19361154c868eb9b28d5ac6395b1d2844053259a1abfd6f75e300de812

The second call checks the proposed date and establishes it:

Output
Yes — the proposed date is established
The engine established that the period ends on the proposed date.
rules: TagesfristEnde

TRUE_ONLY behind that headline means the computation supports the claim and not its negation. A wrong candidate answers NEITHER, which is a normal inconclusive result, not an error — chapter 3 explains why it is not FALSE_ONLY.

The model spec names the exact canon version the application pins. It lives in one place, beside the deadline policy and the time zone:

src/to-case.ts
export const MODEL_SPEC = 'de.bgb.fristen@0.1.0';
export const POLICY = 'urn:de:corpus:clir:bgb-fristen#BGB_FRISTEN_TAG';
export const TIMEZONE = 'Europe/Berlin';

The application opens the model once and asks two questions. buildCollect asks “which dates end this period”; buildTruth asks “does the period end on this date”. Both read the same facts. Opening with offline: true resolves the pinned canon locally:

src/runtime.ts
// Calculate mode: which date(s) end the period?
export async function evaluateCollect(input: FormInput): Promise<Answer> {
const model = await openModel();
const query = buildCollect();
return model.collect(query.predicate, query.args, toCaseInput(input));
}
// Verify mode: does the period end on the proposed date?
export async function evaluateTruth(input: FormInput, candidate: string): Promise<Answer> {
const model = await openModel();
const query = buildTruth(candidate);
return model.truth(query.predicate, query.args, toCaseInput(input));
}

Two versions matter. The minimum the starter supports is Node 20 (engines: >=20), but for a new setup use a supported LTS — Node 24 (“Krypton”, Active LTS until 20 October 2026, supported until 30 April 2028) is the recommended route; Node 20 itself is EOL since April 2026.

The same checksum, file list, and version are bound together in the release manifest beside the archive, deadline-app-0.1.0.release.json. If any copy of these numbers — on a page, in a text export, in a cached view — disagrees with the manifest, that copy is a stale edition, not a second release: published bytes never change under their name.

The server binds loopback only and defaults to port 8787; HOST and PORT override both:

src/server.ts
// Loopback only by default: the printed URL is the reachable URL.
// Set HOST=0.0.0.0 deliberately to expose the example on the network.
const host = process.env.HOST ?? '127.0.0.1';
const port = Number(process.env.PORT ?? '8787');
createServer().listen(port, host, () => {
console.log(`deadline-app listening on http://${host === '127.0.0.1' ? 'localhost' : host}:${port}/`);
});
  • A checksum mismatch on the download means a corrupt or substituted file: re-download, do not proceed.
  • PackageNotFound on openModel means the canon package is not installed. The fix is npm ci, not a looser version.
  • Node below 20 is outside engines (>=20), but that field alone only warns: without engine-strict npm installs anyway, and the starter ships no .npmrc to harden it. If old-Node symptoms appear, the fix is upgrading Node — not a looser version. The troubleshooting chapter lists each symptom with its fix.

The suite you ran above (npm test, 63 of 63 passing) is this chapter’s test run; there is no need to repeat it. Run one call the chapter has not shown yet — the check of a wrong candidate:

Terminal
node --import tsx src/cli.ts evaluate --event 2026-03-06 --days 14 --candidate 2026-03-21
Output
Not established either way
The engine neither established nor refuted the proposed date; it does not guess.
rules: TagesfristEnde

Before reading each answer, predict it.

The CLI prints an end date for one command and a yes-or-no headline for the other. What decides which question it asks?

The --candidate flag. Without it the CLI calls evaluateCollect, which asks for the end date (buildCollect). With it the CLI calls evaluateTruth, which checks that one date (buildTruth).

What happens if you run the first command without the --days flag?

The CLI refuses it before any engine call. It prints error: durationDays must be an integer from 1 to 36500 and exits with code 2; no answer is computed and nothing is guessed in place of the missing duration.

The lockfile installs the canon package at 0.1.5, but the model spec says de.bgb.fristen@0.1.0. Is that a mismatch?

No. 0.1.5 is the version of the package that delivers the canon; 0.1.0 is the version of the canon itself, which the application pins in MODEL_SPEC. Package 0.1.5 carries model version 0.1.0.

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.