PDFPipe

Document security / How long it lives, and what happens after

Suspending deletion for documents under legal hold

The ability to stop deleting a defined set of documents indefinitely, which overrides every retention rule and has to be built before it is needed.

The exposure

A hold requires that documents relating to a matter stop being deleted, immediately, and stay that way until the hold is lifted. That is straightforward to state and impossible to do retrospectively in a system whose deletion is a scheduled job with no exceptions, because by the time somebody asks, the job has been running for years. It also has to survive being forgotten: a hold that expires because nobody renewed it has failed, and a hold nobody lifts keeps data forever.

The decisions

Reasons rather than a description of the code. Each one has a default that is fine for a page and wrong for a file somebody keeps.

  • Make the deletion path check for a hold, so applying one is a data change rather than a code change or an operational scramble.
  • Scope holds by matter, and let a document be under more than one, so lifting one does not release a document another still covers.
  • Never let a hold expire automatically. Expiry is the failure mode: it must be lifted deliberately, by somebody, with a record.
  • Make applying and lifting auditable, with who, when and which matter, because that record is frequently the thing being asked about.
  • Report on what is held, so a hold that should have been lifted years ago is visible rather than silently retaining data forever.
  • Test that the deletion job actually honours it, because a hold that is recorded and not enforced is worse than none: it produces confidence without protection.

In practice

A fragment, with the thing that goes wrong kept in a comment where it is the more instructive half.

js
// The deletion path checks. Applying a hold is a data change, not a
// code change made under pressure.
async function deleteExpiredDocuments() {
  const expired = await db.documents.find({
    expiresAt: { lt: new Date() },
    deletedAt: null,
  });

  for (const doc of expired) {
    if (await holds.covers(doc)) {          // the check that makes it possible
      await db.documents.update(doc.id, { deletionDeferredAt: new Date() });
      continue;
    }
    await hardDelete(doc);
  }
}

// Scoped by matter, and a document can be under several.
async function applyHold({ matterId, criteria, by, reason }) {
  await db.holds.create({ matterId, criteria, appliedBy: by, reason, appliedAt: new Date() });
  await audit.record({ action: "hold.applied", matterId, by, reason });
  // No expiry field. Expiry is the failure mode.
}

async function liftHold({ matterId, by, reason }) {
  await db.holds.update({ matterId }, { liftedBy: by, liftedAt: new Date(), reason });
  await audit.record({ action: "hold.lifted", matterId, by, reason });
}

// Visible, so a hold nobody lifted does not silently retain forever.
schedule.monthly(async () => {
  for (const h of await db.holds.active()) {
    if (olderThan(h.appliedAt, years(2))) {
      notify(h.appliedBy, "hold %s has been active for two years", h.matterId);
    }
  }
});

// And the test, because a recorded hold that is not enforced gives
// confidence without protection.
test("deletion honours a hold", async () => {
  const doc = await expiredDocumentUnderHold();
  await deleteExpiredDocuments();
  expect(await storage.exists(doc.storageKey)).toBe(true);
});

What people do instead

Treating a hold as an operational instruction rather than a system feature, so it is implemented by disabling the deletion job. That suspends deletion for everything, retention stops being enforced across the board, and nobody remembers to turn it back on.

How this is found out

When the first hold is requested, usually with a deadline attached and by people for whom the answer we cannot do that yet is not acceptable.

Frequently asked

Does this page tell me what the law requires?

No, and deliberately not. Retention periods, erasure obligations and residency rules vary by jurisdiction, by industry and by the kind of document, and they change. What these pages describe is the shape of the problem and the mechanisms a system needs in order to implement whatever answer your own advisers give you. Where a genuine tension exists, such as an immutable record against a right to erasure, it is named as a tension rather than resolved.

Why is so much of this about the contents rather than access control?

Because access control decides who can obtain a copy and has no opinion at all about what happens to the copy. Once a document is on somebody's laptop, forwarded to a colleague or printed, every control listed here has already stopped applying to it. What is inside the file is therefore the part that keeps mattering, which is the opposite of the balance you would strike for a page.

How much of this applies at a small volume?

Most of it, because these are decisions rather than infrastructure. Redacting by omitting rather than covering costs nothing. Deciding what goes on a template costs one review. Classification is one object in code. Legal hold and an audit trail are the two that take real work, and both are far cheaper to build before they are requested than under the deadline that comes with the request.

Related security topics

The decisions that depend on each other, then the rest of the same group.

Most of these are decisions rather than features, and the cheapest time to make them is before the first document is delivered rather than after one reaches the wrong person.