feat(coach): store a turn's result tables encrypted beside the message

A new nullable column, coach_messages.results_encrypted, holds the tables
of values a turn read as AES-GCM ciphertext; only their metadata rides the
plaintext provenance. readMessageResults serves them to the owner, each
table whole or withheld with a reason. The provenance restore learns the
steps, method, table metadata, chips, clarification and the forced-answer
marker, entry by entry against the wire schema.

The column is registered for key rotation, carried through both backup
formats (ciphertext in a disaster-recovery file, readable JSON in a
portable one) and goes with its row on conversation delete and wipe.
This commit is contained in:
Marc-André Bombeck committed 2026-09-27 10:36:27 +02:00
1 parent cde7af3cf8
commit a8b3e5d7f4
9 files changed
+714 -1

No files matched your search

+4
View File
@@ -1354,6 +1354,10 @@ const coachMessageBackupSchema = z
promptVersion: z.string().nullable().optional(),
tokensUsed: z.number().int().nullable().optional(),
model: z.string().nullable().optional(),
// v1.39.4 — the turn's tables: ciphertext on a disaster-recovery file,
// readable JSON on a portable one, absent on every file written before.
resultsEncrypted: base64BytesSchema.nullable().optional(),
resultsJson: z.string().nullable().optional(),
createdAt: isoDateTime,
})
.passthrough();