All Docs
FeaturesCalmony Sanctions MonitorUpdated March 11, 2026

HIPAA-10: Minimum Necessary Access & Field-Level Controls

HIPAA-10: Minimum Necessary Access & Field-Level Controls

Compliance framework: HIPAA Control: HIPAA-10 — Minimum Necessary Access Introduced in: v0.1.11

Overview

The HIPAA Minimum Necessary Standard requires that access to protected information is limited to only the data elements needed to fulfil a specific request. Prior to v0.1.11, all API endpoints returned full database rows — including internal metadata columns — regardless of what the caller actually needed. This release closes that gap by introducing explicit column projection and response DTO types across the People API.

What Changed

Column Projection in Drizzle Queries

Previously, queries used an unscoped .select() which fetched every column from the database:

// Before — returns all columns including internal metadata
const person = await db.select().from(people).where(eq(people.id, id));

Queries are now projected to return only the fields required for the operation:

// After — returns only client-facing fields
const person = await db
  .select({
    id: people.id,
    fullName: people.fullName,
    status: people.status,
  })
  .from(people)
  .where(eq(people.id, id));

DTO Types for API Responses

Each API route now uses a dedicated Data Transfer Object (DTO) type that explicitly excludes internal fields such as userId and system metadata from the serialised response. This provides a compile-time guarantee that sensitive internal fields cannot leak into client-facing payloads, even if the underlying schema evolves.

Admin Access Restrictions

Admin users previously received unfiltered records with no additional field-level restrictions. Field-level controls are now applied consistently regardless of the caller's role.

Affected Endpoint

EndpointChange
GET /api/people/[id]Now returns projected fields only; internal metadata excluded

Security Posture

This change reduces the blast radius of any future data exposure by ensuring that:

  1. Internal identifiers and metadata are never transmitted to clients.
  2. Each API shape is defined explicitly as a DTO rather than inferred from the full database schema.
  3. Column projection is enforced at the query layer, not just at serialisation time.

Recommendations for Contributors

When adding new API routes or modifying existing ones, follow these patterns:

  • Always use Drizzle's column projection syntax rather than bare .select().
  • Define a DTO interface for every API response shape.
  • Exclude any column that is not directly consumed by the client (e.g. userId, createdById, internalNotes).
  • Review the updated src/app/api/people/[id]/route.ts as a reference implementation.