INV-ARC-018: PII Storage and Retention Enforcement
| Reference Number | Version | Effective Date | Supersedes | Authority Level |
|---|---|---|---|---|
| INV-ARC-018 | 1.5 | 2026-09-29 | 1.4 | Constitutional |
I. Purpose
This document defines the architectural enforcement rules for storing, encoding, and retaining personally identifiable information (PII) at rest. It governs which storage forms are permitted, which columns must exist, and when PII must be destroyed.
INV-ARC-005 governs PII leakage in the execution layer (request context, logs, telemetry). This document governs PII in the persistence layer (database columns, caches, files).
II. Scope
This document applies to:
- All database columns that store PII (names, display identity, claim verification artifacts)
- All domain services that read or write PII fields
- All FEAT executors that create, update, or delete records containing PII
- Schema migrations that add, rename, or remove PII columns
- Caching layers that may hold decrypted PII in memory
III. Authority Level
Constitutional (Tier 1). This document derives from INV-CORE-000 Section III.2, Minimal Use and Storage of PII, and is governed within the INV hierarchy described by INV-CORE-001. It is subordinate to those foundational invariants.
IV. Dependencies
docs/INVARIANT/CORE/INV-CORE-000_CORE_INVARIANTS.mddocs/INVARIANT/CORE/INV-CORE-001_CAPABILITY_BASED_ARCHITECTURE_AND_AUTHORITY_MODEL.mddocs/INVARIANT/ARCHITECTURE/INV-ARC-005_NO_PII_LEAKAGE_IN_EXECUTION_LAYER.mddocs/INVARIANT/ARCHITECTURE/INV-ARC-019_IDENTITY_AND_OWNERSHIP_MODEL.mddocs/SPEC/SPEC-SEC-001_CREDENTIALS_AND_IDENTITY_LOOKUP_CODE_CONTRACT.md(incorporated; see Section V)
V. Permitted Storage Forms
Every PII field at rest must use exactly one of two storage forms. No other form is permitted.
1. HMAC-Hashed (Lookup/Matching)
- Purpose: enable deterministic matching without exposing the original value.
- Implementation: keyed HMAC using the application’s pepper/HMAC key.
- The original value MUST NOT be recoverable from the stored form.
- Use for: roster claim name-matching hashes on
seats, username lookup hashes.
2. Symmetrically Encrypted (Recoverable Display)
- Purpose: store a value that must be decrypted for in-app display.
- Implementation: Fernet symmetric encryption using the application’s encryption key.
- Plaintext MUST NOT be written to the database, even transiently.
- Use for: first name and last name in
identity_profiles, any display-purpose PII.
Dual-Column Rule
When a single PII value serves both lookup and display purposes, it MUST be stored in two separate columns — one hashed, one encrypted. A single column MUST NOT serve both purposes.
External hall-pass verification compares decrypted display names within a class scope already established through the verification capability and join code (INV-ARC-019 §X). This is a scoped check for a same-day entry, not a stored identity-lookup index or economic actor resolution. It creates no persistent lookup digest and never authenticates or authorizes from a name.
Incorporation of SPEC-SEC-001
This section names the two permitted forms. The code-level construction of each — normalization
through one canonical documented function, HMAC-SHA-256 under PEPPER_KEY with a field/domain
separation label, non-recoverability of the digest, and separate fields where both purposes exist —
is SPEC-SEC-001 Section V.2, which is incorporated here and binding.
SPEC-SEC-001 Section V.5 is likewise incorporated as the code-level expression of Section VIII of
this document: security event records may carry a request identifier, owning FEAT identifier,
outcome class, and non-sensitive technical metadata, and must carry no PII, credential material,
raw lookup inputs, digests, or keys. A security artifact must not outlive the identity it describes
absent an explicitly authorized, separately scoped requirement.
One boundary is worth stating explicitly, because the two keys are easy to conflate when reading
this section alongside INV-ARC-019: the HMAC key named in Storage Form 1 is a lookup key.
SPEC-SEC-001 Section V.1.3 prohibits it — and every other application secret — from being an
input to password hashing. The forms governed here and the credential primitive governed by
INV-ARC-019 Section VI use different keys with independent rotation semantics, and no code may
fall back from one to the other.
VI. Permitted PII Fields
Only the following PII fields are permitted in the v2 schema. Any PII column not listed here requires an amendment to this document before it may be added.
| Field | Table | Storage Form | Purpose |
|---|---|---|---|
| First name (encrypted) | identity_profiles |
Encrypted | In-app display; scoped hall-pass verification per INV-ARC-019 §X |
| Last name (encrypted) | identity_profiles |
Encrypted | In-app display; scoped hall-pass verification per INV-ARC-019 §X |
Teacher-entered seat context (notes) |
identity_profiles |
Encrypted | Class-scoped contextual display for the teacher; free text with no assertion about its contents |
| First name hash | seats |
HMAC-hashed | Roster claim verification |
| Last name hash | seats |
HMAC-hashed | Roster claim verification |
| Pending signup class/display metadata and username | teacher_signup_attempts.payload_encrypted |
Encrypted | Temporary initial provisioning only; no lookup or actor authority |
| Username hash | users |
HMAC-hashed | Login lookup |
VII. Prohibited Storage Patterns
- Plaintext PII at rest — no column, row, file, or cache entry may contain PII in plaintext form on disk or in the database.
- PII in non-governed columns — PII must not appear in ungoverned
description,notes,metadata, JSON blobs, or free-text columns. The explicitly governed, encryptedidentity_profiles.notesfield is teacher-entered class-scoped seat context. Its contents are not inspected or classified, and no claim is made that the text does or does not contain PII. It is not an identity index and is not automatically included in support payloads. - PII in indexed plaintext — database indexes must not expose plaintext PII. Indexes on hashed columns are permitted.
- PII in backup-only retention — PII must not survive in database backups beyond the retention window of the owning record. Backup retention policy must align with class lifecycle deletion (INV-CORE-000 §III.5).
- Redundant PII — the same PII value must not be stored in multiple tables unless each instance serves a distinct invariant-defined purpose with a distinct storage form.
VIII. Retention and Deletion Rules
PII retention is governed by the lifecycle of its owning identity record:
- Successful claim — clear seat claim name hashes, roster fingerprint, and deduplication code atomically with binding. These fields must not survive as hall-pass or roster-import lookup indexes.
- Seat deletion — when a seat is deleted, all PII on that seat (claim hashes) and its associated
identity_profilesrow (encrypted names) MUST be deleted in the same transaction. - User deletion — when a user is deleted (no remaining seats in any class, per INV-CORE-000 §III.5), all PII on the
usersrow (username hash) MUST be deleted. - Class deletion — class deletion cascades seat deletion, which cascades PII deletion per rule 1 above.
- No orphaned PII — no PII column may reference a deleted seat, user, or class. Foreign key cascades or explicit FEAT-managed deletion must enforce this.
IX. Domain Responsibilities
Temporary student credential setup
Student setup MAY retain its generated username only in a bounded, server-side, volatile memory store shared by application workers. No student username staging column or recoverable persistent copy is permitted. The value is encrypted with the approved Fernet facility inside this volatile store; this does not authorize encrypted disk persistence. RDB/AOF, replication, backups, swap, core dumps, slow logs and command capture must not persist this state. An unavailable or unsafe store MUST fail closed, with no cookie/database/worker-local fallback.
The browser holds only a random capability. The server binds the record to the current claim generation or recovery authorization; the fixed lifetime is at most 30 minutes and never exceeds the recovery deadline. Completion consumes the proof and removes the recoverable username before credentials are activated. Restart, owner deletion and recovery reissuance erase the state. Username collisions may restore the same attempt only within its original deadline. GET cannot create, refresh or verify an attempt. FEAT-IDEN-002 incorporates SPEC-IDEN-001 for this flow.
Temporary teacher signup
Before a User, Class or Seat exists, Identity may stage only the submitted class label, section, time zone, teacher first/last display names, username and pending TOTP seed in the encrypted signup payload. A hashed random capability binds that staging row to its browser. No plaintext staging values may enter the session cookie. The row is not an identity or participant and has no user_id or seat_id. It expires 30 minutes after creation without extension. Completion and restart delete it atomically; an hourly Identity cleanup deletes expired rows. No staging value is copied to logs or authority context.
Identity Domain (DOM-IDEN)
- Owns
identity_profilesand the encrypted display-name columns. - Owns the encryption/decryption boundary: decryption happens at read time within the identity service, not in templates or routes.
- Must not expose decrypted values beyond the service return boundary except as display-ready strings.
Seat Provisioning and Claim (FEAT-IDEN-006, FEAT-IDEN-001, FEAT-IDEN-002)
- FEAT-IDEN-006 (roster provisioning and Unclaim) owns the creation of the HMAC-hashed claim verification columns on
seats. It must hash incoming roster names at write time; plaintext must not persist beyond the FEAT transaction. - FEAT-IDEN-001 (claim verification) hashes the names a student enters only to compare them with those columns. It persists neither the names nor their digests.
- FEAT-IDEN-002 clears the claim verification columns in the binding transaction (INV-ARC-019 §X).
Amended in 1.5 (2026-09-29): versions 1.4 and earlier attributed column creation to FEAT-IDEN-001. FEAT-IDEN-001 v3.0 is read-only verification, and FEAT-IDEN-006 has always written these columns. The storage rule itself is unchanged.
Operations Domain (DOM-OPS)
- Audit records MUST NOT contain PII. Actor attribution in audit logs uses
seat_idorseats.public_id, never names.
X. Migration Requirements
Any migration that adds, renames, or removes a PII column must:
- Identify the column as PII in the migration description.
- Specify the storage form (encrypted or HMAC-hashed).
- Include a data migration step if converting existing plaintext to an encoded form.
- Verify that no plaintext residue remains after the migration completes.
XI. Relationship to INV-ARC-005
INV-ARC-005 and INV-ARC-018 are complementary:
| Concern | Governing Document |
|---|---|
| PII in request context, logs, telemetry | INV-ARC-005 |
| PII in database columns, caches, files | INV-ARC-018 |
| PII in error messages, URLs, query params | Both (INV-ARC-005 at runtime, INV-ARC-018 at storage) |
Together they enforce INV-CORE-000 §III.2 across both the execution layer and the persistence layer.
XII. Amendment
Revisions to this document must increment the version number, update the effective date, and remain consistent with INV-CORE-000 §III.2 and the foundational documentation standard (SOP-DOC-000).
Last updated: 2026-09-29