truveil

Truveil audit node for n8n: register, log action, log decision, request approval, request a consequential-decision checkpoint, check run readiness. Types and orders the events it writes so they can be scored; asks for the judgments it cannot know. Fail-so

Package Information

Released: this month
Downloads: 0 weekly / 0 monthly
Latest Version: 0.3.0

Documentation

n8n-nodes-truveil

A Truveil audit node for n8n. One node, six operations: register the agent,
log an action, log a decision, request an approval, request a
consequential-decision checkpoint, and check run readiness — a read-only ask
of whether the run's audit record is complete enough to be graded, which
names what is missing instead of leaving you to find out from a report.

Status

This is 0.3.0 (adds Check Run Readiness to 0.2.0). Which version the
registry serves is a question this file cannot answer without going stale, so
it does not try. One command answers it:

npm view n8n-nodes-truveil version

If that prints 0.1.2, you are looking at the defective version. 0.1.2 was
published 2026-08-05 and has the ten defects listed under "What 0.2.0 fixes".
The worst of them means no run logged through it can be graded at all: 0.1.2
never typed an event and never sent a step ordinal, so every audit comes back
insufficiently_instrumented while every call returns ok. Upgrade as soon as
0.2.0 is on the registry.

This section is written as a question-and-a-command rather than as a claim for a
specific reason. The README shipped inside 0.1.2 said, in this spot, "Not
published to the n8n community registry. Pending a reviewed working local
install." That was true when written and false by the time it was published, and
it is why nobody remembered the package was live for 46 days. npm always ships
the README whatever the files field says, so this is the section a stranger
reads, and a snapshot of publication state is the one thing it must not contain.
tests/n8n_node_contract.test.js enforces that: it fails if this section
asserts a publication state instead of pointing at the registry.

Install

In n8n: Settings, Community Nodes, Install, then enter n8n-nodes-truveil. That
installs whatever the registry currently serves — see Status above.
Create a Truveil API credential with your per-user API key, which the node
sends as the x-client-secret header. The endpoint defaults to
https://mcp.truveil.app/mcp.

Then read the next section before you build anything. The shape of the
workflow decides whether your run can be graded at all, and the most common way
to get an ungradable run is to skip one step.

LOCAL_TEST.md and docker-compose.yml cover running it against a local n8n.

The workflow this is built for

One step decides whether your run can be graded. A run with no event typed
ingestion evidences nothing at all in Data Trust, and a grade that presents
itself as describing four dimensions is withheld when a record can only speak
to three. The audit comes back insufficiently_instrumented even if every
other field is filled and your coverage is above the threshold. That is the
engine refusing to overstate what your logs support, not the node failing.

Step 2 below is that step. Put a Log Action with Event Type ingestion
straight after the node that reads your data, and Data Trust has something to
stand on.

Measured end to end by test_n8n_node_earnings.py at the repository root. Six
steps, and the order matters:

  1. Register Agent — fill Intended Purpose and Owner Email. They are the only
    evidence for two signals, and a blank field is now omitted rather than
    written over what you registered last time.
  2. Log Action, Event Type ingestion — right after the node that reads
    your data. This is where provenance is credited.
  3. Log Decision — the classification, with Sources filled in if it cited
    anything. Auto types it synthesis, which is where citations count.
  4. Request Approval — records the request. It does not record a human
    answer; Truveil never sees one.
  5. Log Decision, Human Checkpoint on — after the approval, once a
    person has actually decided. It inherits the approval handle from step 4
    automatically, which is what links the request to the decision.
  6. Log Action — the send, with AI Disclosure Text if the output carried one.
  7. Check Run Readiness — at the end. Read-only: it writes nothing and
    returns no grade. truveil.readiness and truveil.summary on the item say
    whether the record clears the grading floor, and each floor condition still
    in force names exactly what clears it. One node at the end of the flow, not
    one after every write.

Measured on that workflow, engine 2.0.0 / bible-2.0, category 1:

scenario signals earned coverage status
every field left at its default A3 T6 8.70% insufficiently_instrumented
four operations, every field filled A1 A3 R2 T1 T2 T3 T4 T6 34.78% insufficiently_instrumented
the six steps above A1 A3 A4 A5 D1 R2 T1 T2 T3 T4 T6 45.83% graded, 46.2, D

Two of those rows are worth reading carefully.

A run with everything left at its default earns two signals and is refused a
grade. That is correct, not a defect: nothing was stated, so there is nothing to
grade, and the only way to a grade would be to put back exactly what defect 10
removed.

The middle row clears the 33.33% coverage threshold and is still floored, on a
structural condition: Data Trust is empty. D2 went with the hardcoded
freshness_check, and D1 is credited only on an event actually typed ingestion,
which a four-step workflow has no step for. That is the price of not
manufacturing a signal, and it is why step 2 above exists.

What it does

Auto-derived from the execution context, because n8n genuinely holds these:

  • run_id — the n8n execution id, so every Truveil call in one run groups
    together on the dashboard.
  • step — each Truveil node reads the step the upstream Truveil node put on
    the item and adds one, so the run is ordered 1, 2, 3 without anyone typing it.
    Overridable, and the override is ignored unless it parses as an integer.
  • action — derived from the upstream node when left blank, and never
    inherited from a Truveil node's own name.
  • data_provenance — [{source, fetched_at, version}], where source is the
    upstream node and fetched_at/version come from the item itself. Omitted
    rather than invented when the item carries neither.
  • event_type — Auto types a decision as synthesis, and an action as
    disclosure when it carries disclosure text, otherwise output. It never
    guesses ingestion: that is the one type the node cannot tell from outside.
  • JSON-RPC envelope, run_id placement inside arguments, SSE de-framing and
    serialization, all internal to src/transport.ts.

You supply the judgments n8n cannot infer: risk, reversibility, oversight mode,
human checkpoint, AI disclosure text, the sources you actually cited, and the
event type when Auto would be wrong.

The node makes no claim on your behalf. It does not assert that data was checked
for freshness, does not cite the upstream node's name as a source, and does not
stamp the current clock as a fetch time. Those are things the audit is about, so
they are yours to state.

Fail-soft by construction: the node catches its own errors, surfaces them on its
own output (json.truveil.ok = false with json.truveil.error), and never
throws into the host workflow.

What 0.2.0 fixes

All ten defects measured at S9-IB-1, each pinned by
test_n8n_node_earnings.py.

  1. No event_type on any call. sources and data_provenance are read
    only on a typed row, so both were stored and neither credited. Now every
    logging call is typed. → T4, D1
  2. No step on any call. T6 (workflow_trace) is scored on step. The node
    counts now. → T6
  3. The run could not be graded. Consequence of 1 and 2. The documented
    workflow is graded.
  4. oversight_mode: 'none' was offered and is outside the server's enum, so
    the call was rejected and fail-soft hid it. Replaced with human_on_loop.
  5. approval_id was unreachable. It sat on Log Action, whose schema does not
    declare it, so it was stripped. It is on Log Decision now, inherited
    automatically, and lands on both approval_id and checkpoint_ref. → A5
  6. human_checkpoint was inert on both operations that offered it, for the
    same reason. It is on Log Decision only. → A4
  7. risk, reversible and human_checkpoint on Request Approval were sent
    and stripped: the gate declares none of them. Removed from that operation.
  8. jurisdiction was free text against a closed nine-value server enum.
    Now a dropdown.
  9. intended_purpose and owner_email were sent as empty strings, so a
    re-registration overwrote stored values. Blank fields are omitted.
  10. freshness_check: true was hardcoded. Removed, not moved to a field: a
    node asserting a check nobody performed is manufacturing a signal its user
    never claimed. D2 is now unevidenced unless a deployer states it through
    another path, which is the honest reading.

Two related changes not in the numbered list, both the same principle as 10:
sources is no longer derived from the upstream node's name (it is asked for),
and fetched_at is no longer filled from the current clock when the item has no
timestamp (it is omitted).

Layout

src/        TypeScript sources — the source of truth
dist/       compiled output, tracked (see below); what package.json ships
scripts/    build asset copy, the offline args test, the live harness

Build

npm install
npm run build     # rimraf dist && tsc && node scripts/copy-assets.js
npm test          # 63 offline unit checks, no n8n and no network

Why dist is in the repository

package.json declares files: ["dist"], so dist/ is exactly what npm
publishes. Tracking it means three things the repo needs:

  • what the registry serves can be diffed against HEAD without a build;
  • test_n8n_node_earnings.py and tests/n8n_node_contract.test.js load the
    node directly, so npm test at the repository root needs no npm install
    here and no TypeScript;
  • the contract guard pins every tracked file's digest, so a source change
    committed without a rebuild fails, and — when TypeScript is installed in this
    directory — rebuilds from src/ and asserts the output is byte-identical.
    Without TypeScript that limb skips, which is the same posture the repository
    takes for pytest and for the live schema-drift guard.

Fix bugs in src/, run npm run build, and commit both.

Discussion