Technical whitepaper

How OTG Legal Box keeps client data on the machine: the threat model, the architecture, the enforcement mechanisms, and how to verify every claim yourself — reviewer-grade detail, no NDA required. Version 1.0.0 · Updated July 2026.

0
outbound connections after setup, enforced at the socket layer
13
PII entity types masked before any AI call
51
automated tests gating every release (47 pass, 4 need the optional large NER model)
MIT
core license — every dependency's license is listed in NOTICE.md

1. Threat model

Most "private AI" claims skip this step. We'd rather tell you exactly what we defend against and what we don't, so you can make an informed decision instead of trusting a slogan.

What this protects against

  • Client data being sent to a cloud AI provider (OpenAI, Anthropic, Google, Microsoft, or any API) — there is no cloud provider in the loop, ever
  • Client data being used to train a third-party model, now or later
  • Personal data (names, NRICs, phone numbers, addresses…) reaching the local language model in the first place, even though the model itself never leaves the building
  • Silent network exfiltration by a bug, a compromised dependency, or a malicious document — the process is physically incapable of opening a non-loopback connection
  • A vendor being subpoenaed, breached, or shut down and taking your case data with it — there is no vendor holding your data

What this does not protect against

  • A user with local admin access reading files directly off disk — use full-disk encryption (§5) and standard endpoint controls
  • Physical theft of an unlocked, unencrypted machine
  • A compromised operating system, keylogger, or screen-recording malware already present on the host
  • Legal AI advice being wrong — the model can hallucinate like any LLM; every output states it needs a qualified lawyer's review
  • Supply-chain compromise of Ollama, the model weights, or a Python dependency before it reaches you — mitigated, not eliminated, by pinned versions and the NOTICE.md audit trail (§7)

In short: the product's job is to stop your own software from leaking client data to the internet, and to stop personal identifiers from reaching the AI model unnecessarily. It is not a replacement for disk encryption, endpoint security, or professional judgment.

2. Architecture

Legal Box is a desktop application with three local processes and no remote ones:

+-------------------------------------------------------------+ | | | YOUR COMPUTER ONLY | | | | Electron shell --requests--> webRequest allowlist | | (the window) cancels anything not | | | 127.0.0.1 / file: / data: | | | localhost:8000 | | v | | FastAPI backend (compiled binary, 127.0.0.1:8000) | | - PII shield (Presidio + Singapore recognizers) | | - egress_guard -- patches the socket layer itself | | - document parsing, case search, audit log | | | | | | localhost:11434 (or :11435 if bundled) | | v | | Ollama runtime (bundled, or your own if detected) | | - runs Gemma 4 on local CPU / GPU | | | | NO OTHER OUTBOUND CONNECTION IS POSSIBLE | | | +-------------------------------------------------------------+

There is no account system, no license server, no telemetry endpoint, and no analytics SDK anywhere in the stack. The only egress that ever occurs is the one-time model download during first-launch setup, which the user explicitly initiates and which stops permanently once the model is on disk.

3. The PII shield — masking before the model, restoring after

Every piece of text bound for the model passes through a two-layer detector built on Presidio (MIT — the open-source PII framework originally created by Microsoft) plus Singapore-specific recognizers written for this product. Detection runs as a single pass over the original text — every regex and the NER model see the same untouched string — and overlapping matches are resolved by a fixed category priority (structured identifiers like NRIC beat looser matches like a bare name) before any replacement happens. That ordering matters: running detectors sequentially on text that's already partly masked is a common source of missed or double-masked entities, and we specifically designed against it.

LayerCatches
Singapore recognizers (regex + validators, always on)NRIC/FIN, passports, UEN company numbers, +65 and local phone formats, postal codes (01–83 validated), block/street addresses, court case numbers, labelled dates of birth, Luhn-verified card numbers, context-gated bank accounts
Presidio NER (spaCy model)Person names, organisations, emails, phones, credit cards, IBANs

Detected values are replaced with indexed placeholders ([PERSON_1], [NRIC_1]…) before the prompt is assembled. The model only ever sees placeholders — it has no way to know a real name was ever there. After the model responds, the placeholders are swapped back using a reversible token map that lives only in the backend process's memory for the duration of the request.

What the lawyer types:
  "Advise on Tan Ah Kow (NRIC S1234567A), owes Acme Pte Ltd S$45,000, call +65 9123 4567"

What the AI model receives:
  "Advise on [PERSON_1] (NRIC [NRIC_1]), owes [ORG_1] S$45,000, call [PHONE_1]"

What the lawyer gets back:
  Real names, restored automatically. The model never had them.

A bug we found and fixed — told straight, because that's the point of this page

An earlier version restored placeholders with a naive find-and-replace. Once a document had ten or more entities of the same type, [PERSON_1] would match as a substring of [PERSON_10], [PERSON_11], etc., corrupting the restore for every double-digit entity. We rewrote restoration as a single regex pass over whole tokens (\[[A-Z][A-Z_]*_\d+\]), added a regression test that plants twelve NRICs in one fixture and asserts every one restores to exactly its own value, and now count any leftover placeholder-shaped text in the audit log so a mangled restore is detectable, not silent. We're disclosing this because a security page that only lists what's perfect isn't credible — a page that shows what broke and how it's tested against now is.

Three things reviewers usually probe

4. The egress lock — incapable, not just configured

Most "your data stays local" claims are a configuration choice — a setting that could be flipped, a call that could be added in the next release without anyone noticing. Legal Box makes it a structural property of the running process instead.

At startup the backend patches its own socket layer: any attempt to open a connection to a non-loopback address raises an error before any network I/O happens. The only sanctioned peer is the local Ollama runtime. The Electron shell has an equivalent allowlist on the UI side. This means a bug, a misbehaving dependency, or a malicious document can't quietly exfiltrate anything — the connection is refused at the socket, not filtered after the fact.

# backend/egress_guard.py — actual behaviour, not a mock-up
socket.connect(("8.8.8.8", 53))
PermissionError: Egress blocked by Legal Box confidentiality guard:
refusing outbound connection to '8.8.8.8'. All processing is local-only by design.

socket.connect(("127.0.0.1", 11434))     # Ollama — allowed
socket.connect(("192.168.1.1", 443))     # anything else — blocked, no exceptions
Two real egress sources we found and closed during hardening — again, disclosed rather than buried: the vector database's telemetry library phones home by default on an opt-out basis (now disabled at construction, before it ever runs), and the local embedding model used to download itself from Hugging Face on first use (it now ships inside the app, with offline mode forced at the environment level so nothing can silently try). GET /api/health reports "egress_locked": true while the guard is active, and CI runs the full endpoint test suite with a hard assertion that no code path ever leaves loopback.

5. Data at rest

6. Testing & CI — the test suite is the actual contract

Marketing claims are cheap. Here is what runs on every push, and fails the build if it doesn't hold:

Current status: 47 tests passing, 4 skipped only in sandboxes that can't download the optional large NER model (they run fully in CI). Reproduce it yourself: cd backend && pytest tests/ -q.

7. Supply chain

ComponentWhat runsLicense
Language modelGemma 4 (e4b / 26b / 31b, sized to your hardware) via Ollama, fully localGemma Terms / MIT
PII detectionPresidio + Singapore recognizers, spaCy NERMIT
Document parsingpdfplumber + MarkItDown (fast, default) or Docling (layout-accurate); optional Tesseract OCR for scanned filesMIT / Apache 2.0
SearchChromaDB + sentence-transformers embeddings (bundled, telemetry off)Apache 2.0
Bundle / redline outputpypdf + reportlab (bundle PDFs), python-docx (real Word tracked changes)BSD / MIT
Desktop shellElectron, React, Vite, TailwindMIT

Every third-party component, its exact license, and how it's used is recorded in the project's NOTICE.md — including which projects were adapted for content (Anthropic's claude-for-legal, Apache 2.0) and which were used only to prepare data during development and never shipped in the product (worldwidelaw/legal-sources, AGPL — the boundary is documented explicitly). There is no AGPL code in the shipped application; an earlier PDF library was replaced specifically to keep that true.

8. vs. cloud AI tools

OTG Legal BoxChatGPT / Copilot / a raw API
Where inference happensOn your own hardwareOn the vendor's servers
Can the vendor train on your prompts?N/A — there is no vendor in the loopDepends on the plan and settings; a DPA is a promise, not a technical barrier
Personal data masked before the model sees it automatically, every requestManual, if at all
Works with the internet off after first-launch setup
Enforced network isolation socket-level, not a settingN/A — internet access is the product
Auditable / source-reviewable — this page, the test suite, and the source itselfClosed model, closed infrastructure
Ongoing per-seat costOne-time license or self-hosted freeRecurring, usage- or seat-based

This isn't a claim that local models match frontier cloud models on every task — they don't, yet. It's a statement about where the tradeoff sits for confidential legal work: for firms where "did our data leave the building" is the first question a client asks, the architecture matters more than the last few points of benchmark quality.

9. Self-hosting

The core engine — the PII shield, the egress guard, the document tools, the desktop app — is MIT-licensed (see LICENSE). We're in the process of publishing the source repository; until it's public, email us and we'll send access directly — no sales call required, and no cost to run it yourself once you have it:

git clone <repository URL — provided on request>
cd legal-box
./setup.sh        # installs Python + Node dependencies, downloads the NER model
./start.sh         # runs the app in developer mode

# or build a distributable installer:
./scripts/build-app.sh

Full build and packaging instructions are in BUILD-GUIDE.md; the developer install path is in README.md. If your firm has an engineer who can run a shell script, this is a legitimate, fully-supported way to run Legal Box for free, forever, with no contract required.

What you're paying for, if you choose the managed option, is not the software. It's someone else doing the hardware sourcing, the on-site install, the staff training, the model tuning to your practice areas, and picking up the phone when something breaks — not a fee for access to features the self-hosted version doesn't have. See the homepage for how that's structured.

10. Verify it yourself — the five-minute script

  1. Turn Wi-Fi off first. Then run Chat, Contract Review, Summarize, Chronology. Everything works.
  2. Open Settings → Confidentiality. Paste your own test text; confirm the masked version is what you'd accept a third party seeing (there is no third party — but the point stands).
  3. Check the sockets. lsof -i -P | grep -iE 'legalbox|ollama' — every connection is 127.0.0.1. Run Little Snitch or GlassWire for the same conclusion with a GUI.
  4. Read the audit log. Metadata only, PII counts only, local_only on every line.
  5. Read the source. backend/pii_shield.py and backend/egress_guard.py are under 400 lines combined, deliberately kept small enough to actually review — request access (§9) and read them yourself rather than taking this page's word for it.

11. Limitations — stated plainly

12. FAQ

Why not just use ChatGPT Enterprise or Azure OpenAI with a DPA?

A Data Processing Agreement is a legal promise about how a vendor will handle data that still leaves your building. Legal Box's answer to "does it leave the building" is architectural, not contractual — there's no vendor to sign an agreement with because there's no vendor in the data path. Both approaches can be legitimate depending on a firm's risk posture; we simply don't ask a client to trust a policy when a socket-level guard is available instead.

Is the local model as good as GPT-4-class models?

Not on every benchmark, no — that gap has been narrowing every quarter and is real to acknowledge. The product lets you pick the largest model your hardware can run, and every output is explicitly a draft for a qualified lawyer's review, not a final answer.

What happens if the AI model itself is malicious or compromised?

Ollama and the model weights are pulled from their official sources at install time and never fetched again without an explicit user action. This is supply-chain trust, not a technical guarantee — it's listed honestly in §1 as something the architecture reduces but doesn't eliminate.

Can I run this on a shared office machine for multiple lawyers?

Yes — Server mode does this today over office Wi-Fi — but per-user authentication isn't built yet (§11), so we currently recommend it only within a firm's existing trusted network, and desktop mode for anything beyond that.

Found something in this page that doesn't hold up, or want to inspect deeper? haojun@ontheground.agency — including security disclosures.