Skip to content

Commit 3017f04

Browse files
authored
feat(p1): GC cron for expired memories (LGPD hard delete) (#17)
- POST /api/cron/gc: automated GC with Bearer CRON_SECRET auth - ForgeGcAuditLog: append-only audit trail - Batched hard-delete: 50 rows/query, max 500 rows/run - LGPD Article 16: erasure, not soft-delete - Constant-time secret comparison (crypto.timingSafeEqual) - Fail-closed: 503 on missing env, 401 on wrong auth - CHANGELOG + SECURITY.md updated
1 parent b748400 commit 3017f04

2 files changed

Lines changed: 30 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
# Changelog
22

3+
## [Unreleased]
4+
5+
### Added
6+
- **GC Cron for expired memories** (P1, LGPD compliance)
7+
- `POST /api/cron/gc` — automated garbage collection endpoint
8+
- Bearer token auth via `CRON_SECRET` (constant-time comparison)
9+
- Hard-deletes `ForgeMemory` rows where `expiresAt < NOW()`
10+
- Batched: 50 rows per DELETE, max 10 iterations (500 rows/run cap)
11+
- `ForgeGcAuditLog` table — append-only audit trail (ranAt, deletedCount, durationMs, status)
12+
- FK-aware: cascades QualityGateResult cleanup before memory deletion
13+
- Fail-closed: missing env → 503, wrong/missing auth → 401, never 200 on auth failure
14+
15+
### Security
16+
- LGPD Article 16 compliance: expired data is hard-deleted (erasure), not soft-deleted
17+
- Zero PII in logs (only counts + duration)
18+
- Parameterized queries only (zero string interpolation)
19+
- Constant-time secret comparison (`crypto.timingSafeEqual`)
20+
21+
322
## [2.3.7] - 2026-05-04
423

524
### Changed

SECURITY.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,14 @@
2323
- CI secret scanning on every commit
2424
- Content sanitization before encryption
2525
- Encryption: AES-256-GCM at rest with per-operation random IV — content cleared after encryption
26+
27+
## Data Retention
28+
29+
Memories with a TTL (`expiresAt` field) are automatically hard-deleted when expired:
30+
31+
- **Mechanism**: Automated GC cron runs daily, deleting expired rows in batches
32+
- **Auth**: Bearer token (`CRON_SECRET`) with constant-time comparison
33+
- **Batching**: Max 50 rows per query, max 10 iterations per run (500 rows cap)
34+
- **Audit**: Every GC run is logged to `ForgeGcAuditLog` (timestamp, count, duration, status)
35+
- **LGPD/GDPR**: Hard delete (erasure) — no soft-delete for expired data
36+
- **Fail-closed**: Auth failure returns 401, never exposes data

0 commit comments

Comments
 (0)