Self-hosted · provider-agnostic · air-gap ready

Contract intelligence that stays inside your walls

Clause extraction, risk analysis, plain-English rewrites, a planner-driven agent pipeline with faithfulness verification, a portfolio knowledge graph, and negotiation drafting — with every model call routed by sensitivity tier.

No account needed in local development. Runs with zero model credentials.

How it works

Upload to verified result, in five stages

Each stage is a live endpoint with an honest degradation path when a dependency isn't reachable — the knowledge graph no-ops, the NLI head falls back to lexical overlap, generation raises a clear error.

  1. 1

    Upload

    PDF, DOCX, TXT, or a scanned image. Extraction is fully local — PyMuPDF, python-docx, and Tesseract OCR fallback. No cloud Document AI.

  2. 2

    Classify

    A rule-based classifier assigns one of four sensitivity tiers. The tier is what the Model Router's Class-C gate keys on for the rest of the document's life.

  3. 3

    Extract

    Segmentation → typed ClauseObjects: deontic modality, defined terms, cross-references, entities, temporal expressions, ambiguity flags. Rule-first, GLiNER where enabled.

  4. 4

    Analyse

    A planner decides which agents to run. Risk radar, timeline, knowledge-graph conflict checks, negotiation redlines, cross-document consistency — only what the document needs.

  5. 5

    Verify

    Every generated summary and answer is entailment-checked against its sources by an NLI head. Contradicted or unsupported claims are surfaced, and can route the run to a human.

8

delivery phases, each deployable

40+

API endpoints, all typed

400+

backend tests, green

4

sensitivity tiers, enforced end-to-end

0

model credentials required to run

Everything runs against the real document

Not a demo. Every capability below is a live endpoint with an honest “knowledge graph offline” state when a service isn’t reachable.

Structured clause extraction

Every clause typed and tagged — deontic modality, defined terms, cross-references, entities, temporal expressions, ambiguity flags. Rule-based first, neural where it earns it.

Risk radar

Keyword + contextual detection across eight categories, aggregated into a spider chart with clause-level drill-down. Uncapped liability, unilateral termination, one-sided arbitration.

Grounded answers

Ask anything about a contract and get an answer checked against the actual text by an NLI entailment head — unsupported claims are surfaced, not hidden.

Planner-driven agents

A pipeline that decides which agents to run, researches flagged clauses, checks the knowledge graph for conflicts, and verifies its own summary before you see it.

Portfolio knowledge graph

Defined terms linked across documents, candidate cross-document conflicts, bitemporal version history — explore it as an interactive graph.

Negotiation drafting

Clauses compared against your organisation's preferred language, with a redline diff and rationale. Every suggestion is pending review — nothing is auto-applied.

Architecture overview

Seven layers, each replaceable without touching the others

Self-hosting is the spine, not a top-up. The domain quality comes from retrieval, the knowledge graph, fine-tuned heads, and the verifier — not from model scale.

Client

A client-rendered Next.js SPA against a separate FastAPI origin. Auth optional.

API

Thin route → service modules, every route behind one guard: auth, rate limit, audit.

Model Router

Every AI call names a task, never a model. Class A/B/C providers, self-hosted-first chains, an import-linter contract.

Pipelines

Rule-first NLP + CV turn flat text into a structured ClauseObject graph.

Agents

A planner-driven LangGraph: extraction → planner → dispatch → verifier. DBOS durable execution optional.

Knowledge & retrieval

Memgraph or embedded KùzuDB, plus hybrid RAG — dense + sparse + graph fused by RRF.

Data

Postgres or SQLite, Redis, content-addressed blobs. Documents, audit log, traces, eval runs.

See the full architecture
Sensitivity-tiered routing

Privileged text never leaves the perimeter

Every document is classified public, internal, confidential, or privileged on upload. The Model Router fails closed: confidential and privileged documents are structurally unable to reach an external provider, and a PII redaction gate masks identifiers before any Class C call.

  • Regex + NER redaction gate before every external dispatch
  • SHA-256 egress audit trail — what left, never the payload
  • Air-gapped build excludes the commercial provider package entirely
  • Per-key and per-user RBAC with a full audit log

# routing decision

task clause_rewrite

sensitivity privileged

candidates [local-llm, local-llm-large]

class_c_candidates dropped: gemini (tier not permitted)

→ resolved: local-llm-large (self-hosted)

AI pipeline

A pipeline that decides what to do, then checks its own work

A document with no risk or ambiguity signal runs just extraction → planner → verifier. The verifier can fail the run: a fabricated citation, a KG conflict, or an unsupported claim sets needs_human_review and drops it into the review queue.

01

extraction

Builds the ClauseObject graph — always runs.

02

planner

Rule heuristics or an LLM plan pick the node set. Falls back to rules on any error.

03

risk / compliance

Keyword sweep + KG conflict lookup for every defined term used.

04

research

Hybrid RAG, but only for clauses already flagged by risk or ambiguity.

05

summary

The one generating node — asks the model to cite retrieved sources by [N].

06

verifier

Citation validity + KG conflicts + real NLI faithfulness. The mandatory release gate.

Every capability, mapped

Knowledge graph

Your portfolio as a graph, not a folder of PDFs

Defined terms are extracted per document, then linked across the portfolio only when both the alias text and the defining context match. Candidate cross-document conflicts surface obligation/prohibition pairs that share a term.

Per-document terms, portfolio links

"the Company" in two unrelated contracts is not auto-merged — linking needs matching context, not just matching text.

Candidate conflicts

Cross-document obligation vs. prohibition on a shared term — flagged for review, never asserted as a confirmed conflict.

Bitemporal versioning

Transaction time and valid time on every Document/Clause node. Ask what a term meant as of a past date.

Two backends

Memgraph over Bolt, or an embedded KùzuDB with no server at all — the single-binary profile.

legalai.app/knowledge-graph
Term: “Confidential Information”4 documents
NDA — Northwind ↔ Cedar & Valeobligation
MSA — Northwind ↔ Bright Labsobligation
Vendor DPA — Northwind ↔ Cirrowareprohibition
1 candidate cross-document conflict
Open the graph explorer

Risk analysis

Eight categories, keyword and contextual, drill-down to the clause

A rule-based keyword sweep is the always-on floor; an AI pass adds contextual detection. Results aggregate into a spider chart on the Risk Dashboard, and every point drills into the exact clauses that drove it.

Liability & indemnityTerminationPayment & financialIP & confidentialityDispute resolutionCompliance & regulatoryData & privacyOperational & delivery
legalai.app/documents/1/risk
Liability & indemnity92
Termination68
IP & confidentiality44
Payment & financial21

Section 10 — indemnification not subject to any limitation of liability

Negotiation agent

Every clause checked against your playbook — nothing auto-applied

The org configures preferred language per clause type. The agent flags any deviation (not a similarity threshold — a governing-law swap from California to Delaware scores 0.90 on difflib yet is maximally different in effect) and produces a redline. Every suggestion is pending_review.

legalai.app/documents/1/negotiation

Governing law — deviation

− governed by the laws of the State of Delaware

+ governed by the laws of the State of California

pending_reviewrationale: portfolio standard is CA jurisdiction
How the drafting agent works

Evaluation framework

A model only becomes the default when a gate proves it earns it

The cutover gate runs a task's graded eval bound to the external baseline vs. the self-hosted candidate. passed = candidate ≥ baseline × ratio. A task routes to the self-hosted default only on a PASS — and the result is written to the eval_runs table for regression attribution.

Fast lane

Pure-stdlib gold-set checks, CI-gated on every push — clause type, deontic tags, faithfulness.

Graded lane

LegalBench (CUAD / ContractNLI) + MNLI loaders, SQuAD-F1 / macro-F1 metrics, no sklearn.

Cutover gate

Per-task meet-or-beat-baseline. "We chose not to ship this model" is a CI-enforced invariant.

See the evaluation dashboard

Research contributions

Five directions with potential patent value

Each has a validated CPU prototype or a concrete design. What stays open is GPU training and the formal prior-art / patent-counsel work — both are marked as such, not hidden.

Deontic graph attention

A GAT over an Obligation graph for cross-document conflict detection. Architecture designed; GPU training open.

Temporal obligation simulation

Auto-derive downstream dates from "N days after <trigger>" patterns. CPU prototype validated against hand-modelled scenarios.

Legal-semantic fingerprinting

Contrastive clause embeddings with verified hard negatives — modal swap, negation, magnitude shift. 122 triplets built; fine-tune GPU-blocked.

Adaptive negotiation playbook

Procedural memory of negotiation history. Static-preference first step shipped; the full vision needs redline history.

Deontic-structure-aware ablation

Explain a risk prediction by perturbing one legally-meaningful span at a time. Corroborated the risk model learned superficial n-grams.

Read the research notes

Screenshots

The workspace, the trace, the router

Previews render from the live design system — they track the current theme and never go stale.

legalai.app/documents/1
OverviewClausesRiskAgents
privileged
legalai.app/documents/1/agents
extraction ✓
planner ✓
risk_compliance ✓
research ✓
summary ✓
verifier ✓

needs_human_review: KG conflict

legalai.app/models
local-llmClass B
local-embed-neuralClass B
local-nliClass A
geminiClass C

Technology stack

Everything self-hostable, nothing that phones home by default

Backend

FastAPI · Pydantic · SQLAlchemy · Postgres / SQLite · Redis

AI orchestration

LangGraph · DBOS · a declarative Model Router · import-linter

Models

Qwen3-8B/14B (Ollama / vLLM) · bge-m3 + bge-reranker (TEI) · DeBERTa NLI · GLiNER

Retrieval & graph

Hybrid RAG (RRF) · rank-bm25 · FAISS · Memgraph / KùzuDB

NLP / CV

regex-first pipeline · dateparser · PyMuPDF · OpenCV · Tesseract

Frontend

Next.js · TypeScript · Tailwind · TanStack Query · React Flow · Recharts

Eval & training

Inspect AI · LegalBench · MLflow · DVC · Argilla

Observability

OpenTelemetry · a SHA-256 egress audit trail · per-call routing logs

Roadmap

Eight phases shipped, each one deployable

No phase left the system in a broken state. What isn't done is either the next phase or explicitly blocked on hardware or a human — both are called out.

0–4

V1 hardening · FastAPI re-platform · CPU NLP/CV · knowledge graph + hybrid RAG · planner-driven agents with the NLI verifier

shipped
5

Provider-agnostic Model Router · self-hosted embeddings/rerank · Gemini demoted to a Class-C plugin

shipped
6

Self-hosted generation target (Qwen3-8B/14B) · graded eval harness + cutover gate · GLiNER NER · NLI head

shipped
7

Sensitivity tiering enforced end-to-end · PII redaction gate + egress audit · RBAC · air-gapped packaging · KùzuDB · DBOS · memory service · first SPA

shipped
8

Bitemporal graph versioning · consistency + simulation baselines · negotiation agent · risk dashboard + KG explorer · five NOVELTY prototypes

shipped
9

Multi-tenant scale · SOC 2 / GDPR workflows · streaming agent trace over WebSocket · Negotiation Studio

next

GPU-dependent

Fine-tuned clause-type + deontic heads, the contrastive embedding model, and the Deontic-GAT training all need a card this project doesn't have. Prototypes and pipelines are verified via dry-run / smoke.

Infrastructure-dependent

A legal expert to adjudicate weak labels in Argilla, and qualified patent counsel for the prior-art searches. Everything up to the human step is built.

The full development journey

Why this platform

Four decisions that shaped it.

It runs disconnected

The air-gapped build excludes the commercial-provider package entirely. Structure, retrieval, the knowledge graph, agents, and sensitivity classification are all CPU-fine; generation runs on a 16 GB card or a small CPU model.

The verifier can fail

Every agent summary and every grounded answer is entailment-checked against its sources. Contradicted or unsupported claims are surfaced — the product tells you when it isn't sure.

Model choices are gated

A task only becomes self-hosted-by-default when a cutover gate proves the local model meets or beats the external baseline. "We chose not to ship this model" is a CI-enforced invariant.

Nothing is auto-applied

Negotiation redlines, sensitivity overrides, and flagged analyses all route through a human. The review queue is a real queue, not a documented intention.

Frequently asked

Open the workspace

Upload a contract, or start with the sample NDA, and see clause extraction, the risk radar, the agent trace, and the knowledge graph run against it.