About the project
AUDIT — Transparency you can verify
Donation auditability for political financing, built for Costa Rica's Supreme Electoral Tribunal on Tether's WDK and QVAC.
The trail is still paper
Costa Rica's Tribunal Supremo de Elecciones supervises how political parties are financed. Nobody can independently verify who gave what, when, or whether the money was legal — the controls that exist happen months after an election, once the result is decided and the money is spent.
The TSE's president and magistrates asked us to build this.
Sensitive off-chain, proof on-chain
Donor identity is legally protected. The fact of a donation must be publicly verifiable. Those requirements pull in opposite directions.
Sensitive data stays with the KYC provider that collected it; only hashes go on-chain. A regulator can verify that a donation was assessed, when, and against what evidence — without learning who the donor was.
The chain is the intake channel
A donor sends USD₮ from their own wallet straight to the party's address. No checkout, no intermediary. The wallet is built with Tether's WDK and is fully self-custodial.
An indexer reads ERC-20 Transfer logs from the chain, so a donation cannot arrive without being recorded — the record is built from what the chain says, not from what the party reports.
Non-compliant donations are flagged for return. The wallet is gated by a WDK returns-only policy: a treasurer moving funds where they don't belong doesn't get a failed transaction, they never get a signature.
A compliance agent that never leaves the device
Track: Small models, hard tasks — tool use & reliability
The QVAC agent reads each donation and its attestation and produces the compliance assessment an auditor actually reads. It runs entirely on-device: no cloud, no API keys, no data leaving the machine.
That isn't a cost saving. The agent reasons over KYC and source-of-funds data — sending it to a cloud API would hand a third party the donor list of every political party in the country.
What it takes to make a 1.7B model reliable
Every guard exists because of a failure we observed:
- Constraints in the system turn — in the user turn the model echoed them back as findings
- Two-shot examples — they also suppress Qwen3's reasoning block, cutting latency from 7.8 s to 1.7 s
- responseFormat schema — structured output instead of parsed prose
- predict: 800 — at 400 the JSON truncated mid-string
- 400-character ceiling — a rephrasing that ran long had stopped being one
- contradictsVerdict() — a model rendered a non-compliant donation as "foreign donor (US), not rejected"
The model can ask for a human, never clear one
When the agent disagrees with the deterministic engine, the disagreement raises a model_disagreement finding that moves the donation to pending and puts it in front of a person.
A violation outranks every other severity, so the agent can turn verified into pending — but can never turn non-compliant into approved.
Measured, not claimed
npm run qvac:bench runs every compliance scenario N times and reports what came back.
- 21 inferences across 7 outcomes: verified, non-compliant ×4, pending ×2
- Rationale accepted: 100% · overruled: 0% · unusable: 0%
- Mean latency: 1772 ms
Two failures worth reporting. Our first benchmark didn't measure what it claimed — its fixtures carried an invented attestation hash that tripped attestation_tampered on every scenario, so the verified path never executed. Corrected fixtures gave the numbers above.
The second isn't ours. On darwin-x64 the same SDK and weights produce degenerate output across two models and a clean download. On darwin-arm64 inference is coherent and stable across 46 runs. Reported to the QVAC team.
Model and hardware
- Model: Qwen3 1.7B, quantization Q4_0 (
QWEN3_1_7B_INST_Q4), ~1.0 GB - Also wired: Qwen3 4B Q4_K_M
- Machine: Apple M5, 10 cores, 16 GB RAM, macOS 26.5.2, arm64
- Runtime: Node v24.16.0,
@qvac/sdk0.17.1, prebuild darwin-arm64 - Mean latency: 1772 ms per assessment
Where inference happens
- SDK:
@qvac/sdk, capability: text generation with enforced structured output - Model load:
sdk.loadModel(), memoised once per process - Completion:
sdk.completion(), streamed token by token - Integration: https://github.com/Velar-Bonds/Velar-Audit/blob/3516891/src/compliance/qvac-agent.ts
- Call site: https://github.com/Velar-Bonds/Velar-Audit/blob/3516891/src/pipeline.ts
Run it from a clean clone
git clone https://github.com/Velar-Bonds/Velar-Audit.git cd Velar-Audit npm install cp .env.example .env npm run wallet:new # paste the phrase into .env as WDK_SEED_PHRASE npm run qvac:pull # downloads the weights once npm start # http://localhost:3400
Demo accounts use the password velar-demo-2026: tse@velar.cr sees every party, alfa@velar.cr and beta@velar.cr see only their own.
Hacki