Data & isolation

Every stage of the lifecycle reads and writes the same record. The data layer is what holds that record, scopes it, and keeps one request’s file from ever reaching another’s. The shape of the record itself is documented in Reader; this page is about how it’s stored and isolated.

One table, keyed by appeal

The store is a single-table design: every persistent record — document, annotation, tag, category, search index, audit event — is keyed by the appeal it belongs to. There is no record in the system that exists outside an appeal’s key prefix. Object storage (the PDF blobs, the search sidecars) uses per-appeal prefixes in the same way.

The schema enforces it: the primary key leads with the appeal ID, so every appeal-scoped read carries that ID. Cross-appeal aggregation runs on a separate, narrowly-scoped path — admin reporting, audit roll-ups — held apart from the Reader.

Two isolation boundaries

Adjudicate isolates data at two levels, enforced by different mechanisms.

The tenant boundary is the outer one. Each customer runs in its own isolated deployment — a separate cloud account with its own datastore, object storage, and identity realm. There is no shared multi-tenant table that one customer’s records sit in alongside another’s; a customer is an account boundary, not a row-key. Two customers cannot reach each other’s data because there is no network or IAM path between the deployments, not because a query filter holds the line.

The appeal boundary is the inner one. Within a single tenant, the appeal is the unit of privacy. A reviewer working on appeal A must not see anything from appeal B, even if both are handled by the same firm under the same login realm.

Adjudicate enforces the appeal boundary at the architectural layer rather than as a policy overlay. A policy overlay says “the application checks that the requester is authorized for this appeal before returning data.” An architectural boundary says “the data path itself is shaped so that cross-appeal access cannot be expressed.”

What “architectural” means here

Three things, working together:

  1. Every record is keyed by appeal ID. There is no record outside an appeal’s key prefix, so a query that doesn’t name an appeal can’t return appeal-scoped records.
  2. Queries are scoped at the query, not filtered at the result. The query layer takes an appeal ID as a required parameter; it does not accept “give me all documents matching X” and then filter down to the current appeal — that pattern leaves the cross-appeal path open and depends on the filter being correct.
  3. The access policy is checked at the storage layer. Object storage uses per-appeal prefixes, and the access policy is attached to the storage role, not the application. A bug that confuses appeal A’s ID with appeal B’s does not leak data — it produces an access-denied error from storage before any application code runs.

What this means in practice

  • Search is scoped to one appeal. Finding evidence across cases means opening each appeal — an occasional extra step, consistently safe.
  • Annotations don’t leak across appeals. Two reviewers at the same firm working different appeals see only their own appeal’s annotations.
  • Multi-party appeals — appellant, representative, POA, substitute — are modeled as relationships within an appeal, not across.

How it fits with access control

Per-appeal isolation is the storage boundary — it removes the most common failure mode, a confused-deputy bug that leaks one appeal into another’s view. Who may open which appeals is governed by Users & teams — roles, team assignments, share permissions — and every access is recorded in the audit log. Within an appeal a reviewer works the whole file; isolation holds the line at the appeal boundary, with access control and audit layered on top.

Why this is unusual

General-purpose document-management and eDiscovery systems typically organize around matters at a logical level, with policy enforcement layered on top — the data path itself supports cross-matter queries, and access control keeps queries on their own side of the wall. Adjudication work has a sharper privacy gradient: the cost of a cross-appeal leak is high, and the value of cross-appeal features inside one reviewer’s UI is low. The architectural-isolation posture trades a small amount of feature surface for a much harder-to-bypass boundary — and it is far easier to start with than to retrofit, because a system that begins shared-data-with-policy-on-top tends to keep that shape. Adjudicate began with the harder boundary and grew around it.

Retention and audit

Because every record carries an appeal ID, an actor, and a timestamp, the data layer is also where retention and audit live: every view, annotation, search, tag, and download is a stored event scoped to its appeal. Retention policy (how long a tenant keeps closed-appeal data) and export-for-records-request both operate over the same appeal-keyed records — there is no separate audit store to keep in sync. This is the substrate Reporting reads from.