The Vault ← The Vault
Onyx Legal · Technology · ModuleDWG · ONYX-BILLS · SHEET V

A single module, in detail

Onyx BillS

Local-first legal-billing infrastructure, built for the firm's own work.

Sheet
V · Onyx BillS
Module
Billing
Status
Internal
Rev
2026.06

The premise

Determinism you can audit.
The same inputs always yield the same number — and the number can be recomputed by hand.
AI that cannot act alone.
It may only propose; booking, finalising and moving money stay with a person.
Local-first
Silent by default; the network is opt-in.
OnyxSecurity.swift:174-225
Propose-then-seal
Every consequential act carries its proposer, then is written and sealed.
AdminCore.swift:1838-1902
Re-verifiable
A finalised invoice attests to its own totals under a hash seal.
AdminCore.swift:1540-1588
The mathematics

The architecture

The mathematics it is designed around

Every figure on this page is computed by a fixed rule. Sheet A is shipped and carries its line of code; Sheet B is specified-but-not-yet-wired — named as design, with no code mark claimed.

SHEET AComputed & shipped — as-builteach figure carries its file · line; re-verifiable against source
FIG. A·01Reciprocal-rank fusionAS-BUILT
hybrid lexical ⊕ vector retrievalOnyxSearch · Rag.swift:314
FIG. A·02Wilson 95% lower boundAS-BUILT
small-sample retrieval-eval gate floorai-eval · EvalMetrics.swift:8
FIG. A·03Write-off recordAS-BUILT
enumerated reason, atomic with the audit eventAdminCore.swift:3335
FIG. A·04VAT — basis pointsAS-BUILT
subtotal · VAT · total residuals reconcile to 0AdminCore · VatEngine.swift:26
FIG. A·05Time → tenths → billedAS-BUILT
6-minute units, truncated client-favourableAdminCore.swift:94
FIG. A·06Rational minor-unit scalingAS-BUILT
one half-away rounding over a 128-bit productOnyxCommon · RationalMath.swift:143
FIG. A·07Physical day-capacity gateAS-BUILT
> 24h booked on one day blocks finalisationBillingIntegrity+DayCapacity.swift:50
FIG. A·08HMAC-SHA256 audit chainAS-BUILT
append-only; HKDF keys; SQLite refuses editsOnyxAudit.swift:200
FIG. A·09Cosine vector legAS-BUILT
brute-force over local embeddingsOnyxSearch · Rag.swift:375
FIG. A·10BM25 lexical legAS-BUILT
FTS5, k₁ = 1.2, b = 0.75OnyxPersistence.swift:414
FIG. A·11Frozen nearest-centroid classifierAS-BUILT
rules-first; proposal-onlyPhaseClassifierML.swift:48
FIG. A·12Confidence-margin gateAS-BUILT
5-clause envelope, or it declines to guessPhaseClassifierML.swift:157
FIG. A·13Conflict match — layered scoreAS-BUILT
deterministic; matched on keyed tokens; ≥ 70 flags for reviewAdminCore.swift:2614
SHEET B — FOR REFERENCE
SHEET BThe architecture it is designed aroundspecified in full, not yet wired into the build — named to its design spec, no code mark claimed
FIG. B·01Maximal marginal relevanceFOR REFERENCE
relevance vs diversity rerank, λ = 0.7SPEC · C4 · Phase 1
FIG. B·02Robust outlier — modified z / MADFOR REFERENCE
median-based; flags entries for reviewSPEC · C3 · Phase 1
FIG. B·03Contingency expected valueFOR REFERENCE
PPM-weighted scenarios, integer-exactSPEC · B5 · Phase 3
FIG. B·04Interest present valueFOR REFERENCE
30E/360 day-count; legal-cap clampSPEC · B4 · Phase 3
FIG. B·05Retainer depletion & top-upFOR REFERENCE
burn-rate runway forecastSPEC · B2 · Phase 2
FIG. B·06Capped-fee burn — budget-at-completionFOR REFERENCE
conservative overrun forecastSPEC · B1 · Phase B
FIG. B·07Markov collections agingFOR REFERENCE
one-step matrix; absorbing “paid” stateSPEC · C5 · Phase 4
FIG. B·08Kaplan–Meier time-to-paymentFOR REFERENCE
median collection time, right-censoredSPEC · C5 · Phase 4
FIG. B·09Multicurrency FX — audit-preservingFOR REFERENCE
exact rational rate; triangle checkSPEC · B3 · Phase 4
Section I — Layer I

Layer I

Deterministic billing arithmetic

Every billed figure is computed by a fixed, floored rule in minor units.

Time is billed in tenths of an hour. Minutes are floored to a six-minute tenth — never less than one — the gross line is taken in minor units (truncated toward zero, so the firm never over-bills), and any discount is subtracted to a floor of zero. VAT is computed on the rounded base in basis points, then reconciled so subtotal, VAT and total agree to the fil. The whole computation is integer arithmetic on minor units; there is no floating ambiguity to drift, and the result reproduces under recomputation by hand.

worked — one timesheet line, recomputed 180 min @ AED 1500.00/hr → floor((180+3)/6) = 30 tenths 30 × 150000 / 10 = AED 4 500.00 net VAT 5% = round_half_away(450000 × 500 / 10000) = AED 225.00 total = AED 4 725.00 → sealed OL-INV-2026-0001
AdminCore.swift:94-102, 830-838, 855 · billed-tenths / gross (truncated) / billed minor-unit formulae
VatEngine.swift:26-31, 114-141 · VAT in basis points with minor-unit subtotal / VAT / total reconciliation
OnyxCommon · RationalMath.swift:143-199 · the single money-scaling primitive — one half-away rounding over a 128-bit product
BillingIntegrity+DayCapacity.swift:50-112 · AdminCore.swift:4034 · a physically-impossible day (> 24h booked) hard-blocks finalisation
Section II — Layer II

Layer II

The tamper-evident ledger

The ledger is append-only; the store refuses edits. Each event hashes the one before it into a chain.

Every event embeds the hash of its predecessor and contributes its own; the sequence, the previous-hash links and the tip hash are all checkable, so any break is detectable. The store blocks mutation outright: immutable SQLite triggers abort update and delete, and the sequence is unique, so the chain cannot fork. Each hash is an HMAC-SHA256 over canonical content under keys derived through HKDF from the vault key, so a record cannot be silently rewritten and re-signed.

OnyxAudit.swift:200-311 · eventHash = HMAC-SHA256 over canonical content embedding prevEventHash
OnyxAudit.swift:345-425 · verifier checks sequence, previous-hash binding and tip-hash truncation
OnyxAudit.swift:680-709 · 313-343 · immutable SQLite triggers (UPDATE/DELETE abort); HKDF-derived audit keys
Section III — Layer III

Layer III

The exact-token retrieval floor

Search fuses lexical and vector ranking; a floor keeps exact tokens in the results.

Retrieval is hybrid: a reciprocal-rank fusion of a BM25 lexical leg and a brute-force cosine vector leg, with a fusion constant of k = 60 (the active default weights the two legs equally; a protected-retrieval profile weights the lexical leg higher to harden exact-token recall). Above the fusion, a floor holds: under the protected guard, exact tokens — UTBMS codes, clause references, dates, AED amounts, percentages — are pinned into the top results with recall 1.0, so semantic ranking can only add to the floor. The guarantee is enforced as an evaluation gate: a new method must beat the floor with no exact-token regression, measured against a Wilson 95% lower bound alongside Recall@k and nDCG@k.

OnyxSearch · Rag.swift:314-323 · RRF score, k=60; default weights equal, protected-hybrid weights lexical higher
Rag.swift:375-389 · OnyxPersistence.swift:414-429 · brute-force cosine vector leg; FTS5 BM25 lexical leg
Rag.swift:1052-1073, 911-914 · exact-token preservation pins lexical hits into top-k under the protected guard
ai-eval · EvalMetrics.swift:8-16, 22-41 · Wilson 95% lower bound · Recall@k · nDCG@k — the adoption gate
Section IV — Layer IV

Layer IV

The cryptographic perimeter

The vault is local and quiet; outbound traffic passes one audited gate.

The default network policy blocks inbound and outbound traffic and ships an empty allowlist — the billing core consults this contract before it ever reaches the network. At rest the database is sealed with SQLCipher, the cipher version checked on open; backups travel in AES-GCM envelopes under an Argon2id-derived key, and vault blobs are content-addressed by a keyed SHA-256 hash. The recovery key is minted once, returned for a printed sheet; the system keeps no copy. An enabled off-device feature routes every send through a single audited gate that refuses secrets, records a payload hash, and fails closed when offline; vault sync itself is opt-in, default-disabled and ciphertext-only.

SQLCipher
database at rest · version checked on open
HMAC-chain
tamper-evident append-only ledger
HKDF
domain-separated audit keys
Argon2id
portable-backup key derivation
AES-GCM
sealed backup envelopes
Keyed SHA-256
content-addressed vault blobs
Deny-by-default
no inbound / outbound · ∅ allowlist
Audited off-device gate
refuses secrets · fails closed · hashed
OnyxSecurity.swift:174-225 · default network policy: no inbound, no outbound, empty allowlist
OnyxPersistence.swift:112-167 · 632-708 · OnyxSecurity.swift:132-172 · SQLCipher at rest; AES-GCM backup envelopes under Argon2id
OnyxVault.swift:102-144 · OnyxSecurity.swift:561-572, 659-691 · keyed-SHA-256 content addressing; one-time recovery key never persisted/logged
OffDeviceSendGate.swift:35-104 · VaultCloudPortabilitySync.swift:5-15, 64-107 · audited off-device funnel; vault sync opt-in, default-disabled, ciphertext-only
Section V — Layer V

Layer V

AI that cannot act alone

Classification is rules-first; any model that assists is frozen, gated, and proposal-only. The model proposes a code; a person decides.

Any model that assists is a small, frozen, on-device classifier, trained only on synthetic data. Its corpus is 440 constructed legal narratives across the 22 phases, built with deliberate hard-negatives, so the model learns the shape of the work. A new model is admitted only after it clears an offline evaluation gate bound to its own weight-hash; until those weights are vendored the classifier runs rules-first. In assist mode it scores a suggested UTBMS code by cosine behind a five-clause confidence margin, and below that margin it declines. A draft becomes a record only by passing back through the deterministic workflow with a step-up, leaving conflicts, AML, sanctions and payment outcomes with the operator.

FIG. V·aMac · AI Assist — refusalprint surface
Onyx BillS AI Assist refusing to act: payment decisions remain operator-only; AI cannot decide conflict, AML, sanctions, acceptance or payment outcomes.
FIG. V·bMac · assistant chat proposalreal capture
Mac assistant chat panel showing a reviewed AI proposal held as an operator-confirmed draft.
predict(x) : if rule_match(x) → rule_label else ĉ = argmaxc cos(tfidf(x), μc) gate : adopt only if the 5-clause envelope holds emit : suggestion(proposedBy = model) — never a commit
A101A102A103A104A105A106A107A108A109A111

UTBMS activity codes, hash-pinned (A110 is intentionally absent); the 22-phase DIFC/ADGM taxonomy maps to UTBMS L-codes.

PhaseClassifierML.swift:48-95, 157-176 · frozen TF-IDF nearest-centroid + five-clause confidence-margin gate
ActivityClassifier.swift:29-86 · onyx_billing_rules.json:214 · MatterPhase.swift:8-77 · A101–A109/A111 from a SHA-256 hash-pinned ruleset; 22-phase taxonomy → L-codes
AssistantActionDraft+Confirm.swift:31-75 · a model draft commits only through the deterministic workflow — re-checked, re-validated, step-up required
phase-training-data/…_exemplars_440.jsonl · ModelEvalClearance.swift:18-60 · OnyxSecurity.swift:227-242 · 440-example synthetic training corpus (22 phases, hard-negatives); adoption clears an eval-gate bound to the weight-hash; rules-first until the model is vendored (PhaseExemplars.swift)
Section VI — Layer VI

Layer VI

Conflicts screened, parties cleared

Every client and counterparty is checked before a matter opens — names matched as keyed tokens — and no engagement proceeds until each party clears.

Conflict screening is deterministic, and runs without a model. Each name on a matter — the client, every co-client, the counterparties, a third-party payer, and any name typed in by hand — is folded to a normalised form and reduced to a keyed token: an HMAC-SHA256 of that normalised string under a key derived from the vault's admin key. Names are compared as tokens, so the screening index can match an alias or a transliteration without storing the name itself. A candidate is scored by the strongest of five signals: an exact normalised match, a keyed-token alias, a script transliteration so a name written in Arabic or Cyrillic meets its Latin spelling, a corporate-group root shared by two different entities, and an edit-distance near-match. Anything at or above 70 is surfaced for a person to clear or block, the reason written to the audit ledger.

Screening is one clause of a wider gate. A matter cannot be engaged until every client on it — the lead and any co-clients — has cleared client due diligence and carries an anti-money-laundering decision scored against a single versioned rubric; sanctions, source-of-funds and the payer are weighed in the same pass. Due diligence and the money-laundering rating are held per client, so a joint matter clears each on its own evidence. Those per-party fields were added as nil-omitting optionals — a record sealed before they existed re-encodes to the same bytes — so the audit chain over older entries still verifies.

worked — a counterparty screened against the firm's book screen : "Аль-Фулан Холдингс" vs. existing clients & matter parties token : HMAC-SHA256( admin-key, normalise(·) ) — compared as tokens, not names signal : transliteration → Latin ⇒ meets "Al-Fulan Holdings" score 88 decision : 88 ≥ 70 → surfaced → operator clears or blocks (reason → ledger)
AdminCore.swift:2504-2533 · deterministic match — exact / keyed-alias / transliteration / group-affiliate / near, surfaced at score ≥ 70
AdminCore.swift:2586-2587 · 2236-2249 · names reduced to HMAC-SHA256 keyed tokens; key derived from the admin field-encryption key
AdminCore.swift:2604-2653 · Unicode transliteration to Latin; Levenshtein near-match; a corporate-group root shared between distinct entities, generic descriptors excluded
AdminCore2B.swift:1310-1361 · 67-89 · 190, 246 · engagement gate — every client clears CDD and a per-party AML decision (versioned AML-RUBRIC-v1) before a matter is engaged; per-party fields are nil-omitting, so sealed audit hashes still verify
Section IX — Operational proof

Operational proof

Invoices, proformas & controls

The rules hold up on a real invoice.

Invoices are gap-numbered as OL-INV-YYYY-NNNN and, once finalised, carry a re-verifiable SHA-256 economic seal over identity, lines, VAT, subtotal and total — the document attests to its own totals. A proforma advances through separated, audited steps — create, review, approve, finalise — with a step-up before a final invoice issues. Voiding requires a reason; write-offs carry minutes, value, description and an enumerated reason. The billing-item taxonomy is fixed at three kinds: fixed-fee, external-at-cost and disbursement.

FIG. IX·bPDF · invoice templatereal capture
Rendered invoice PDF from Onyx BillS — client identity, bank accounts, IBANs and SWIFT redacted; totals and matter kept.
FIG. IX·cPDF · timesheet templatereal capture
Rendered timesheet PDF page generated from Onyx BillS with activity rows and totals visible.
fixed-feeexternal-at-costdisbursement
AdminCore.swift:2487-2490 · 1540-1588 · gap-numbered OL-INV-YYYY-NNNN; finalised SHA-256 economic seal over identity/lines/VAT/subtotal/total
AdminCore.swift:3440-3542 · proforma create / review / approve / finalise; step-up authorizer before a final invoice issues
AdminCore.swift:3277-3290 · 958-995, 3335-3360 · voiding requires a reason; write-offs carry minutes / value / description / enumerated reason
AdminCore.swift:998-1002 · BillingItemKind has exactly three cases (fixed-fee / external-at-cost / disbursement) — no separate “expense” kind
Section X — At the edge

At the edge

Biometric unlock & signed pairing

The same local-first guarantees, across devices. A companion joins through a signed pairing ceremony; the running timer shows only a non-secret matter code.

The vault opens by biometrics. Step-up re-authentication is built on one-time proofs that are bound to their target and expire — a proof cannot be replayed, reused, or aimed elsewhere. A companion device joins through a signed pairing ceremony: import an invitation, create a device response, save the signed handoff; the verification code is the first eight characters of a SHA-256 over the pairing secret, responses are HMAC-signed and compared in constant time, and the secret never leaves the Keychain. Capture surfaces show no client identity: the running timer and its Live Activity carry only a non-secret matter code — so a glance at a lock screen can’t reveal who the work is for.

step-up : one-time · target-bound · expiring pairing : import .onyxpair → device response → save .onyxdevice display code : SHA-256(secret).prefix(8) (HMAC-signed, constant-time verify) live activity: MAT-2231 (matter code only)
OnyxSecurity.swift:245-378 · biometric step-up — one-time, target-bound, expiring proofs
DevicePairingService.swift:103-181, 237-309 · display code = SHA-256(secret).prefix(8); HMAC-signed responses; constant-time verify; Keychain ThisDeviceOnly
VaultCloudPortabilitySync.swift:5-15, 64-107 · offline by default; any cloud sync is opt-in, flagged, ciphertext-only
In brief

In brief

Questions, answered

What is Onyx BillS?

Onyx BillS (the Onyx Billing System) is local-first legal-billing infrastructure built by Onyx Legal. Every billed figure is computed by a fixed, floored rule in integer minor units, and every consequential action is written to a tamper-evident, HMAC-SHA256 hash-chained audit ledger that admits additions and refuses edits.

Is the AI in Onyx BillS autonomous?

No. AI in Onyx BillS only ever proposes. Classification is rules-first; where a model assists it is a frozen, on-device classifier — trained on a synthetic corpus, no client data — held behind a confidence gate. A suggestion becomes a record only after human review and a step-up, and conflicts, AML, sanctions and payment outcomes are refused outright.

Where is data stored, and does anything leave the device?

Data is stored locally by default. The default network policy is no inbound, no outbound and an empty allowlist; the database is encrypted at rest with SQLCipher and backups travel in AES-GCM envelopes under an Argon2id-derived key. Where an off-device feature is enabled, every send passes a single audited gate that refuses secrets and fails closed when offline.

How is an Onyx BillS invoice verified?

Each finalised invoice is gap-numbered as OL-INV-YYYY-NNNN and carries a re-verifiable SHA-256 economic seal over its identity, lines, VAT, subtotal and total, so the document attests to its own totals.

How does Onyx BillS check for conflicts of interest?

Before a matter opens, every client, co-client, counterparty and payer is screened against the firm's existing clients and matter parties. Names are compared as HMAC-SHA256 keyed tokens — so aliases and transliterations match without the screening index holding a name in the clear — and any candidate at or above the deterministic threshold is surfaced for a person to clear or block, with the reason written to the audit ledger. A matter cannot be engaged until every client clears due diligence and a per-party anti-money-laundering decision.

What platforms does Onyx BillS run on?

Onyx BillS runs natively on macOS, with an iPhone and iPad companion vault. It is built local-first, for a firm's own work.

What is The Vault?

The Vault is Onyx Legal's suite of local-first legal instruments, built for the firm's own practice and not offered for sale. Onyx BillS is its billing module; the suite also includes Onyx ATXE (an evidence workbench) and Onyx LiTT (litigation technology).