From 6714e332931b8b8ba00bad05aecc6aeb48a47664 Mon Sep 17 00:00:00 2001
From: 2witstudios <2witstudios@gmail.com>
Date: Thu, 9 Apr 2026 14:39:03 -0500
Subject: [PATCH 1/4] docs: update compliance doc and prototype panes to
reflect implemented fixes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
DSAR export, message hard-delete, AI log purge on account deletion, and
audit chain verification are now implemented — update stale gap claims.
Note in-progress work on pu/hash-chain-pii and pu/export-rate-limit.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.../compliance-sovereignty-analysis.md | 102 +++++++++++-------
.../src/components/panes/CompliancePane.tsx | 14 ++-
.../src/components/panes/GdprPane.tsx | 67 +++++++-----
3 files changed, 110 insertions(+), 73 deletions(-)
diff --git a/docs/security/compliance-sovereignty-analysis.md b/docs/security/compliance-sovereignty-analysis.md
index 121080e9cd..1f2b494964 100644
--- a/docs/security/compliance-sovereignty-analysis.md
+++ b/docs/security/compliance-sovereignty-analysis.md
@@ -139,18 +139,24 @@ qualifying "local models only" is dangerous.
### 1.2. AI Usage Logging → Right to Erasure / GDPR Article 17
**What happens**: Every AI interaction logs the first 1000 characters of the
-user's prompt and the AI's response to the `ai_usage_logs` table. This data is
-stored indefinitely with no automatic retention policy or deletion mechanism.
+user's prompt and the AI's response to the `ai_usage_logs` table.
-**Why it's incompatible**:
-- No TTL or automatic purge on `ai_usage_logs`
-- No API endpoint to delete a user's AI usage history
+**Status — partially resolved**:
+- ~~Account deletion does NOT purge these logs~~ → **Fixed**: `deleteAiUsageLogsForUser()` purges on account deletion
+- ~~No API endpoint to delete a user's AI usage history~~ → **Fixed**: account deletion handles this via FK cascade
+- ~~Conversation messages retained indefinitely~~ → **Fixed**: 30-day purge cron for soft-deleted messages + FK cascade on account deletion
+
+**Remaining gaps**:
+- No TTL or automatic purge on `ai_usage_logs` for non-deleted accounts (logs accumulate indefinitely while account is active)
- No mechanism to selectively redact prompt/completion content
-- Account deletion does NOT purge these logs
-- Conversation `messages` table retains full message content indefinitely
-**Affected compliance regimes**: GDPR Article 17 (right to erasure), CCPA
-(right to delete), any "right to be forgotten" regulation.
+**Severity**: Downgraded from hard incompatibility to modification-required.
+Account deletion now satisfies Art. 17 for erasure requests. The remaining gap
+is retention minimization for active accounts.
+
+**Affected compliance regimes**: GDPR Article 17 (right to erasure — now
+partially satisfied), CCPA (right to delete — satisfied on account deletion),
+data minimization (active account retention still unbounded).
**Key files**:
- `packages/db/src/schema/monitoring.ts` (lines 144-203: `ai_usage_logs` schema)
@@ -222,34 +228,42 @@ residency, any regime requiring all PII processing to be on-premise.
These features work today but would need changes to pass audits in regulated
environments.
-### 2.1. No Data Retention Policy Engine
+### 2.1. No Data Retention Policy Engine (Partial)
-**Current state**: No automatic data expiration exists for:
-- AI usage logs (indefinite)
-- Conversation messages (indefinite, soft-delete only via `isActive` flag)
-- Activity/audit logs (indefinite)
-- Client analytics events (indefinite)
-- Security audit logs (indefinite)
+**Current state**: Retention exists for some data categories but not all:
+- ~~AI usage logs (indefinite)~~ → **Partial**: purged on account deletion; no TTL for active accounts
+- ~~Conversation messages (indefinite)~~ → **Fixed**: 30-day purge cron for soft-deleted messages + FK cascade on account deletion
+- Sessions and tokens → **Fixed**: TTL-based cleanup on cron schedule
+- Page versions → **Fixed**: 30-day auto-retention with pinnable exemptions
+- AI logs → **Fixed**: anonymize at 30 days, purge at 90 days (configurable via `AI_LOG_RETENTION_DAYS`)
+- Activity/audit logs (indefinite — no retention policy)
+- Client analytics events (indefinite — no retention policy)
+- Security audit logs (indefinite — no retention policy)
-**What's needed**: Configurable retention windows with automatic purge jobs.
-Most compliance frameworks require documented retention schedules.
+**What's needed**: Configurable retention windows for the remaining categories
+(activity logs, analytics, security audit logs) and admin configuration UI.
-**Effort**: Medium. Requires cron jobs and admin configuration UI.
+**Effort**: Medium. Core retention engine and cron infrastructure exists; needs
+extension to remaining data categories and admin-facing configuration.
---
-### 2.2. Incomplete Data Subject Access / Export
+### 2.2. ~~Incomplete Data Subject Access / Export~~ → RESOLVED
-**Current state**: No mechanism exists for a user to:
-- Export all their personal data (GDPR Article 15 / CCPA right to know)
-- See which third parties received their data
-- Download their conversation history, uploaded files, or activity logs
+**Current state**: Data subject access and export is implemented:
+- **User DSAR export**: `GET /api/account/export` — ZIP archive with JSON files
+ covering profile, drives, pages, messages, files metadata, activity logs,
+ AI usage logs, and tasks. Rate-limited to 1 export per 24 hours.
+- **Admin DSAR endpoint**: `GET /api/admin/users/[userId]/export` — admin-only
+ endpoint for processing data subject access requests. Logs which admin
+ accessed which user's data for audit trail.
-**What's needed**: Data export endpoint that packages all user data in a
-portable format (JSON/ZIP).
+**Remaining gap**: No "which third parties received data" view — users cannot
+see a summary of which AI providers, integrations, or external services
+processed their data.
-**Effort**: Medium-high. Requires aggregating data across multiple tables and
-file storage.
+**Effort**: Low for the remaining gap (query integration credentials +
+AI usage logs by provider).
---
@@ -365,14 +379,21 @@ accessed, retention limits for cached calendar data, and token revocation UI.
---
-### 2.9. Security Audit Log Integrity Verification
+### 2.9. Security Audit Log Integrity Verification (Partially Resolved)
-**Current state**: Tamper-evident hash chain exists in `security_audit_log` but
-no automated verification process runs to detect tampering.
+**Current state**: Security audit chain integrity is now verified automatically:
+- **Fixed (#541)**: Hash chain GDPR-safe — PII fields excluded from hash
+ computation so anonymization doesn't break the chain
+- **Fixed**: Daily verification cron (`/api/cron/verify-audit-chain`) recomputes
+ hashes and verifies chain links. HMAC-signed cron requests. Pluggable
+ `ChainAlertHandler` for Slack/email/PagerDuty alerting.
-**What's needed**: Periodic chain integrity verification job with alerting.
+**Remaining gap**: Activity log hash chain still broken by anonymization — PII
+fields are included in `serializeLogDataForHash()`. Fix in progress on branch
+`pu/hash-chain-pii`.
-**Effort**: Low. The infrastructure exists; needs a cron job.
+**Effort**: Low. Security audit chain is done; activity log chain fix follows
+the same pattern.
---
@@ -459,11 +480,12 @@ Every path where data leaves the PageSpace deployment boundary.
### P1 — Required for GDPR/CCPA readiness
-4. **Build data subject export endpoint** — Users cannot export their data today.
+4. ~~**Build data subject export endpoint**~~ — **DONE**. `GET /api/account/export`
+ (user) and `GET /api/admin/users/[userId]/export` (admin DSAR). ZIP with JSON.
5. **Add consent management for AI providers** — No disclosure or consent record
when selecting external AI providers.
-6. **Implement conversation message hard-delete** — Only soft-delete (`isActive`)
- exists today.
+6. ~~**Implement conversation message hard-delete**~~ — **DONE**. 30-day purge cron
+ hard-deletes soft-deleted messages. FK cascade on account deletion.
7. **Document data retention schedules** — No formal retention policy exists for
any data category.
@@ -540,10 +562,10 @@ When entering compliance or white-label conversations, use this framing:
- "Payment processing" — only relevant for SaaS mode, not on-prem
**Do not promise without engineering work**:
-- GDPR right-to-erasure compliance (file deletion gap)
-- Data subject export / portability
-- Configurable data retention policies
-- Formal audit log integrity verification
+- GDPR right-to-erasure compliance (file deletion gap remains — DB erasure is done)
+- ~~Data subject export / portability~~ → **Resolved**: DSAR export endpoints exist
+- Configurable data retention policies (partial — AI logs and messages have retention; activity/analytics/audit logs do not)
+- ~~Formal audit log integrity verification~~ → **Resolved**: daily verification cron with alerting (activity log chain fix in progress)
- Database encryption at rest (TDE needed for regulated industries)
**Never promise** (without fundamental changes):
diff --git a/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx b/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
index 4171550fd8..07ca5d74cf 100644
--- a/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
+++ b/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
@@ -341,7 +341,10 @@ export function CompliancePane() {
stored logHash. Security audit chain is{" "}
fixed (#541)
{" "}— PII fields excluded from hash computation so the
- chain remains valid after GDPR anonymization.
+ chain remains valid after GDPR anonymization.{" "}
+
+ Activity log fix in progress (pu/hash-chain-pii).
+
@@ -351,7 +354,10 @@ export function CompliancePane() {
The 1-export-per-24-hours DSAR rate limit uses an in-process{" "}
Map(). Resets on deploy/restart. Not shared across
- instances. Works for single instance but won't scale.
+ instances. Works for single instance but won't scale.{" "}
+
+ Fix in progress (pu/export-rate-limit).
+
@@ -462,7 +468,7 @@ export function CompliancePane() {
@@ -482,7 +488,7 @@ export function CompliancePane() {
diff --git a/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx b/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
index fffb765bbc..9e36fc7754 100644
--- a/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
+++ b/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
@@ -66,6 +66,17 @@ export function GdprPane() {
+
+ Message deletion: two-stage hard-delete
+
+ Messages use a two-stage deletion path: soft-delete via{" "}
+ isActive flag, then a 30-day purge cron hard-deletes
+ the rows. On account deletion, FK cascade removes all messages
+ immediately. Satisfies GDPR Art. 17 right to erasure for message
+ content.
+
+
+
Data Retention & Minimization
-
-
- Message deletion is soft-delete only
-
- Conversation messages use an isActive flag for
- deletion. GDPR Art. 17 may require true hard-delete. Current
- soft-delete means message content persists in the database
- even after “deletion.”
-
-
-
- Activity log hash chain broken by anonymization
-
- Account deletion anonymizes activity log entries, but this
- changes the data that was hashed — invalidating the
- hash chain. The tamper-evident property is compromised for
- entries involving deleted users. Security audit chain is{" "}
- fixed (#541)
- {" "}— PII fields excluded from hash computation.
-
-
-
+
+ Activity log hash chain broken by anonymization
+
+ Account deletion anonymizes activity log entries, but this
+ changes the data that was hashed — invalidating the
+ hash chain. The tamper-evident property is compromised for
+ entries involving deleted users. Security audit chain is{" "}
+ fixed (#541)
+ {" "}— PII fields excluded from hash computation.{" "}
+
+ Activity log fix in progress (pu/hash-chain-pii).
+
+
+
Export rate limit is in-memory
The 1-export-per-24-hours rate limit uses an in-process Map().
Resets on deploy/restart. Not shared across instances. Works for single
- instance but won't scale.
+ instance but won't scale.{" "}
+
+ Fix in progress (pu/export-rate-limit).
+
@@ -209,12 +215,12 @@ export function GdprPane() {
-
- Hard-delete for messages
+
+ Hard-delete for messages — Done
- True deletion path for conversation messages with audit tombstones
- (record that deletion occurred without preserving content). Supports
- GDPR Art. 17 right to erasure for all data types.
+ Implemented: 30-day purge cron hard-deletes soft-deleted messages.
+ FK cascade on account deletion removes all messages immediately.
+ GDPR Art. 17 right to erasure satisfied for message content.
@@ -222,7 +228,10 @@ export function GdprPane() {
Anonymization that preserves chain link integrity. Hash computed
over anonymization-stable fields only, or chain links explicitly
- maintained through the anonymization process.
+ maintained through the anonymization process.{" "}
+
+ In progress (pu/hash-chain-pii).
+
From 172f7444c3075b93951e12cb18da36d0bc65f943 Mon Sep 17 00:00:00 2001
From: 2witstudios <2witstudios@gmail.com>
Date: Fri, 10 Apr 2026 09:14:02 -0500
Subject: [PATCH 2/4] docs: reflect merged PRs and fix stale review comments
Update for #865 (Redis export rate limit), #866 (activity log PII
exclusion), #867 (activity chain serialization), #868-870 (audit
service wiring), #863 (GDPR cron jobs), #861 (password auth removed).
Fix code review comments: AI usage log deletion is explicit call not
FK cascade; note shared-page assistant messages survive account
deletion; resolve P2 #8 and #9 contradictions.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.../compliance-sovereignty-analysis.md | 33 ++++----
.../src/components/panes/CompliancePane.tsx | 82 ++++++++-----------
.../src/components/panes/GdprPane.tsx | 48 +++++------
3 files changed, 71 insertions(+), 92 deletions(-)
diff --git a/docs/security/compliance-sovereignty-analysis.md b/docs/security/compliance-sovereignty-analysis.md
index 1f2b494964..11c11247ae 100644
--- a/docs/security/compliance-sovereignty-analysis.md
+++ b/docs/security/compliance-sovereignty-analysis.md
@@ -142,9 +142,9 @@ qualifying "local models only" is dangerous.
user's prompt and the AI's response to the `ai_usage_logs` table.
**Status — partially resolved**:
-- ~~Account deletion does NOT purge these logs~~ → **Fixed**: `deleteAiUsageLogsForUser()` purges on account deletion
-- ~~No API endpoint to delete a user's AI usage history~~ → **Fixed**: account deletion handles this via FK cascade
-- ~~Conversation messages retained indefinitely~~ → **Fixed**: 30-day purge cron for soft-deleted messages + FK cascade on account deletion
+- ~~Account deletion does NOT purge these logs~~ → **Fixed**: `deleteAiUsageLogsForUser()` explicitly purges on account deletion (no FK cascade — manual deletion is the only path)
+- ~~No API endpoint to delete a user's AI usage history~~ → **Fixed**: account deletion handles this via explicit purge call
+- ~~Conversation messages retained indefinitely~~ → **Fixed**: 30-day purge cron hard-deletes soft-deleted messages; account deletion cascade removes user-owned messages. **Note**: shared-page assistant messages saved with `userId: null` survive account deletion and may contain user-derived content.
**Remaining gaps**:
- No TTL or automatic purge on `ai_usage_logs` for non-deleted accounts (logs accumulate indefinitely while account is active)
@@ -379,22 +379,19 @@ accessed, retention limits for cached calendar data, and token revocation UI.
---
-### 2.9. Security Audit Log Integrity Verification (Partially Resolved)
+### 2.9. ~~Security Audit Log Integrity Verification~~ → RESOLVED
-**Current state**: Security audit chain integrity is now verified automatically:
-- **Fixed (#541)**: Hash chain GDPR-safe — PII fields excluded from hash
+**Current state**: Both hash chains are now integrity-verified and GDPR-safe:
+- **Fixed (#541)**: Security audit hash chain — PII fields excluded from hash
computation so anonymization doesn't break the chain
+- **Fixed (#866)**: Activity log hash chain — PII fields excluded from hash
+ computation (matching security audit pattern)
+- **Fixed (#867)**: Activity log writes now serialized with
+ `pg_advisory_xact_lock`, preventing chain forking on concurrent writes
- **Fixed**: Daily verification cron (`/api/cron/verify-audit-chain`) recomputes
hashes and verifies chain links. HMAC-signed cron requests. Pluggable
`ChainAlertHandler` for Slack/email/PagerDuty alerting.
-**Remaining gap**: Activity log hash chain still broken by anonymization — PII
-fields are included in `serializeLogDataForHash()`. Fix in progress on branch
-`pu/hash-chain-pii`.
-
-**Effort**: Low. Security audit chain is done; activity log chain fix follows
-the same pattern.
-
---
### 2.10. Session Token in Single Cookie Without Binding
@@ -491,9 +488,11 @@ Every path where data leaves the PageSpace deployment boundary.
### P2 — Required for SOC 2 / enterprise sales
-8. **Add audit log integrity verification cron** — Hash chain exists but is never
- verified.
-9. **Standardize bcrypt cost factor** — Minor inconsistency (cost 10 vs 12).
+8. ~~**Add audit log integrity verification cron**~~ — **RESOLVED**. Daily
+ verification cron exists with alerting. Both security audit and activity log
+ chains are now GDPR-safe and serialized (#541, #866, #867).
+9. ~~**Standardize bcrypt cost factor**~~ — **N/A**. Password-based authentication
+ removed entirely (#861).
10. **Add AI provider DPA references** — Link to each provider's data processing
terms.
@@ -565,7 +564,7 @@ When entering compliance or white-label conversations, use this framing:
- GDPR right-to-erasure compliance (file deletion gap remains — DB erasure is done)
- ~~Data subject export / portability~~ → **Resolved**: DSAR export endpoints exist
- Configurable data retention policies (partial — AI logs and messages have retention; activity/analytics/audit logs do not)
-- ~~Formal audit log integrity verification~~ → **Resolved**: daily verification cron with alerting (activity log chain fix in progress)
+- ~~Formal audit log integrity verification~~ → **Resolved**: daily verification cron with alerting; both chains GDPR-safe and serialized (#541, #866, #867)
- Database encryption at rest (TDE needed for regulated industries)
**Never promise** (without fundamental changes):
diff --git a/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx b/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
index 07ca5d74cf..c3c6f5b67b 100644
--- a/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
+++ b/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
@@ -241,19 +241,21 @@ export function CompliancePane() {
The security infrastructure is solid but has coverage gaps.
- The SIEM adapter is built but not wired. The security audit
- service covers ~1.6% of routes. The activity log hash chain has
- a known fork vulnerability. Agent-level security is missing entirely.
+ The SIEM adapter is built but not wired. Audit service coverage
+ expanded to pages, drives, permissions, settings, account, files,
+ and export routes (#868-870) but not yet 100%. Agent-level security
+ is missing entirely.
-
- SecurityAuditService: ~5 of 252 routes (~2%)
+
+ SecurityAuditService: expanded but not 100%
- Hash chain audit service exists with advisory lock serialization
- and 35 event types. But only wired to login, logout, and mobile
- login routes. Core operations (pages, drives, files, permissions,
- settings, admin actions) are not security-audited.
+ Hash chain audit service with advisory lock serialization and
+ 35 event types. Now wired to auth, pages, drives, permissions,
+ settings, account, files, and export routes (#868-870).
+ Remaining: admin actions, trash, invitations, and other
+ secondary routes.
@@ -268,15 +270,14 @@ export function CompliancePane() {
-
- Activity log hash chain can fork
+
+ Activity log hash chain — Fixed
- Activity log writes not serialized with row locking. Concurrent
- writes can create chain forks. Security audit chain uses
- advisory locks and is safe. Activity logs have hash chain fields
- (previousLogHash, logHash,{" "}
- chainSeed) but no locking to prevent concurrent
- forking.
+ Activity log writes now serialized with{" "}
+ pg_advisory_xact_lock (#867), matching the
+ security audit chain pattern. Chain forking on concurrent
+ writes is prevented. PII excluded from hash computation
+ (#866) so GDPR anonymization preserves chain integrity.
@@ -332,32 +333,21 @@ export function CompliancePane() {
-
- Activity log hash chain broken by anonymization
+
+ Activity log hash chain — Fixed
- Account deletion anonymizes actorEmail in activity
- logs, but this field is included in{" "}
- serializeLogDataForHash() — invalidating the
- stored logHash. Security audit chain is{" "}
- fixed (#541)
- {" "}— PII fields excluded from hash computation so the
- chain remains valid after GDPR anonymization.{" "}
-
- Activity log fix in progress (pu/hash-chain-pii).
-
+ Both security audit (#541) and activity log (#866) chains now
+ exclude PII from hash computation — GDPR anonymization
+ preserves chain integrity. Activity log writes serialized with{" "}
+ pg_advisory_xact_lock (#867).
-
-
-
- Export rate limit is in-memory
+
+ Export rate limit — Fixed
- The 1-export-per-24-hours DSAR rate limit uses an in-process{" "}
- Map(). Resets on deploy/restart. Not shared across
- instances. Works for single instance but won't scale.{" "}
-
- Fix in progress (pu/export-rate-limit).
-
+ DSAR export rate limit migrated to distributed Redis (#865).
+ Shared across instances, survives deploys/restarts. Matches
+ existing auth rate limiter pattern.
@@ -384,7 +374,7 @@ export function CompliancePane() {
@@ -467,8 +457,8 @@ export function CompliancePane() {
/>
@@ -487,8 +477,8 @@ export function CompliancePane() {
/>
diff --git a/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx b/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
index 9e36fc7754..d51560123a 100644
--- a/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
+++ b/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
@@ -138,8 +138,7 @@ export function GdprPane() {
Data subject rights (access, erasure, portability) are solid. AI
provider consent is handled via TOS — PageSpace is an AI product.
- The remaining gaps are cookie consent, data residency, and edge cases
- around message deletion and hash chain integrity.
+ The remaining gaps are cookie consent and data residency.
@@ -157,30 +156,23 @@ export function GdprPane() {
/>
-
- Activity log hash chain broken by anonymization
+
+ Activity log hash chain — Fixed
- Account deletion anonymizes activity log entries, but this
- changes the data that was hashed — invalidating the
- hash chain. The tamper-evident property is compromised for
- entries involving deleted users. Security audit chain is{" "}
- fixed (#541)
- {" "}— PII fields excluded from hash computation.{" "}
-
- Activity log fix in progress (pu/hash-chain-pii).
-
+ Both security audit and activity log hash chains are now GDPR-safe.
+ PII fields excluded from hash computation (#541, #866) so
+ anonymization preserves chain integrity. Activity log writes
+ serialized with pg_advisory_xact_lock (#867),
+ preventing chain forking on concurrent writes.
-
- Export rate limit is in-memory
+
+ Export rate limit — Fixed
- The 1-export-per-24-hours rate limit uses an in-process Map().
- Resets on deploy/restart. Not shared across instances. Works for single
- instance but won't scale.{" "}
-
- Fix in progress (pu/export-rate-limit).
-
+ DSAR export rate limit migrated to distributed Redis (#865).
+ Shared across instances, survives deploys/restarts. Matches
+ existing auth rate limiter pattern.
@@ -223,15 +215,13 @@ export function GdprPane() {
GDPR Art. 17 right to erasure satisfied for message content.
-
- Hash-chain-safe anonymization
+
+ Hash-chain-safe anonymization — Done
- Anonymization that preserves chain link integrity. Hash computed
- over anonymization-stable fields only, or chain links explicitly
- maintained through the anonymization process.{" "}
-
- In progress (pu/hash-chain-pii).
-
+ Implemented: PII fields excluded from hash computation in both
+ security audit (#541) and activity log (#866) chains. Activity
+ log writes serialized with advisory locks (#867). Anonymization
+ preserves chain integrity.
From a02b0fb8af9d31e8b65747f6bb608c6965f8cfa4 Mon Sep 17 00:00:00 2001
From: 2witstudios <2witstudios@gmail.com>
Date: Fri, 10 Apr 2026 09:42:02 -0500
Subject: [PATCH 3/4] docs: fix stale password auth reference, add userId:null
caveat
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Section 7.1 referenced "local email+password auth" — password auth was
removed in #861; on-prem now uses magic links + passkeys. GdprPane
message deletion cards now note shared-page assistant messages with
userId: null may survive account deletion.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
docs/security/compliance-sovereignty-analysis.md | 2 +-
.../src/components/panes/GdprPane.tsx | 11 ++++++-----
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/docs/security/compliance-sovereignty-analysis.md b/docs/security/compliance-sovereignty-analysis.md
index 11c11247ae..92a44291cb 100644
--- a/docs/security/compliance-sovereignty-analysis.md
+++ b/docs/security/compliance-sovereignty-analysis.md
@@ -522,7 +522,7 @@ to be stripped or replaced for a compliance-clean on-premise deployment:
|---------------|----------------------|--------|-------|
| Stripe billing | Remove entirely, or license-key model | Low-medium | No billing needed for single-tenant on-prem |
| Resend email | Self-hosted SMTP (Postfix, etc.) | Medium | Only needed if email verification is required |
-| Google OAuth | Remove; use local email+password auth only | Low | Original auth system already exists |
+| Google OAuth | Remove; use magic links + passkeys | Low | Password auth removed (#861); on-prem uses magic links |
| Google One Tap | Remove | Trivial | UI-only removal |
| Google Calendar | Remove | Low | Optional integration |
| Cloud AI providers | Remove from config; Ollama/LM Studio only | Low | Provider factory already supports this |
diff --git a/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx b/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
index d51560123a..817aa38e45 100644
--- a/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
+++ b/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
@@ -71,9 +71,9 @@ export function GdprPane() {
Messages use a two-stage deletion path: soft-delete via{" "}
isActive flag, then a 30-day purge cron hard-deletes
- the rows. On account deletion, FK cascade removes all messages
- immediately. Satisfies GDPR Art. 17 right to erasure for message
- content.
+ the rows. On account deletion, FK cascade removes user-owned
+ messages immediately. Note: shared-page assistant messages stored
+ with userId: null may survive account deletion.
@@ -211,8 +211,9 @@ export function GdprPane() {
Hard-delete for messages — Done
Implemented: 30-day purge cron hard-deletes soft-deleted messages.
- FK cascade on account deletion removes all messages immediately.
- GDPR Art. 17 right to erasure satisfied for message content.
+ FK cascade on account deletion removes user-owned messages
+ immediately. Remaining gap: shared-page assistant messages with{" "}
+ userId: null may persist.
From 1284e93167e06652d5bc709eebe5cecbc7505297 Mon Sep 17 00:00:00 2001
From: 2witstudios <2witstudios@gmail.com>
Date: Fri, 10 Apr 2026 09:55:20 -0500
Subject: [PATCH 4/4] fix(prototype): restructure panes so resolved items live
in Current
Resolved items were sitting in Gaps/End Game with "Fixed"/"Done" labels,
breaking the narrative flow. Now:
- Current: hash chain integrity, distributed rate limit, message
hard-delete all live in their natural subsections
- Gaps: only genuine gaps remain (cookie consent, data residency,
SIEM, audit coverage, agent trails)
- End Game: only future work (no "Done" items cluttering the roadmap)
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.../src/components/panes/CompliancePane.tsx | 89 ++++++-------------
.../src/components/panes/GdprPane.tsx | 77 +++++-----------
2 files changed, 52 insertions(+), 114 deletions(-)
diff --git a/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx b/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
index c3c6f5b67b..58cd3db62f 100644
--- a/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
+++ b/prototypes/pagespace-endgame/src/components/panes/CompliancePane.tsx
@@ -45,6 +45,17 @@ export function CompliancePane() {
+
+ Activity log hash chain integrity
+
+ Activity log writes serialized with{" "}
+ pg_advisory_xact_lock (#867), matching the security
+ audit chain pattern. PII fields excluded from hash computation
+ (#866) so GDPR anonymization preserves chain integrity. Both
+ chains are now tamper-evident and GDPR-safe.
+
+
+
Activity logs
@@ -129,8 +140,9 @@ export function CompliancePane() {
User DSAR export (GET /api/account/export): ZIP
archive with JSON files for profile, drives, pages, messages,
- files metadata, activity logs, AI usage logs, tasks. Rate-limited
- to 1 per 24 hours. Admin DSAR endpoint
+ files metadata, activity logs, AI usage logs, tasks. Distributed
+ Redis-backed rate limit (1 per 24 hours, survives deploys).
+ Admin DSAR endpoint
(GET /api/admin/users/[userId]/export) with
security audit logging of which admin accessed which user's data.
@@ -269,28 +281,16 @@ export function CompliancePane() {
-
-
- Activity log hash chain — Fixed
-
- Activity log writes now serialized with{" "}
- pg_advisory_xact_lock (#867), matching the
- security audit chain pattern. Chain forking on concurrent
- writes is prevented. PII excluded from hash computation
- (#866) so GDPR anonymization preserves chain integrity.
-
-
-
- No agent-specific audit trails
-
- Security audit is user-centric. When an agent makes a change,
- it's attributed to the user who triggered it, not the agent.
- Activity logs track isAiGenerated and AI model,
- but can't answer: “what did agent X do across all
- conversations?”
-
-
-
+
+ No agent-specific audit trails
+
+ Security audit is user-centric. When an agent makes a change,
+ it's attributed to the user who triggered it, not the agent.
+ Activity logs track isAiGenerated and AI model,
+ but can't answer: “what did agent X do across all
+ conversations?”
+
+
Capability gates: tools + pages, no budgets
@@ -332,25 +332,6 @@ export function CompliancePane() {
-
-
- Activity log hash chain — Fixed
-
- Both security audit (#541) and activity log (#866) chains now
- exclude PII from hash computation — GDPR anonymization
- preserves chain integrity. Activity log writes serialized with{" "}
- pg_advisory_xact_lock (#867).
-
-
-
- Export rate limit — Fixed
-
- DSAR export rate limit migrated to distributed Redis (#865).
- Shared across instances, survives deploys/restarts. Matches
- existing auth rate limiter pattern.
-
-
-
@@ -385,8 +366,8 @@ export function CompliancePane() {
/>
@@ -442,7 +423,7 @@ export function CompliancePane() {
Full GDPR compliance
-
+
-
-
-
-
);
diff --git a/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx b/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
index 817aa38e45..cd4d88e548 100644
--- a/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
+++ b/prototypes/pagespace-endgame/src/components/panes/GdprPane.tsx
@@ -26,8 +26,9 @@ export function GdprPane() {
GET /api/account/export — returns a ZIP archive
with JSON files covering all user data: profile, drives, pages,
messages (AI chat, channels, DMs), files metadata, activity logs,
- AI usage logs, and tasks. Rate-limited to 1 export per 24 hours.
- Machine-readable JSON format for portability.
+ AI usage logs, and tasks. Distributed Redis-backed rate limit
+ (1 per 24 hours, survives deploys). Machine-readable JSON format
+ for portability.
@@ -66,16 +67,27 @@ export function GdprPane() {
-
- Message deletion: two-stage hard-delete
-
- Messages use a two-stage deletion path: soft-delete via{" "}
- isActive flag, then a 30-day purge cron hard-deletes
- the rows. On account deletion, FK cascade removes user-owned
- messages immediately. Note: shared-page assistant messages stored
- with userId: null may survive account deletion.
-
-
+
+
+ Message hard-delete
+
+ Two-stage deletion: soft-delete via isActive flag,
+ then 30-day purge cron hard-deletes the rows. On account deletion,
+ FK cascade removes user-owned messages immediately. Note:
+ shared-page assistant messages with userId: null may
+ survive account deletion.
+
+
+
+ Hash chain GDPR safety
+
+ Both security audit and activity log hash chains exclude PII
+ from hash computation (#541, #866). Anonymization on account
+ deletion preserves chain integrity. Activity log writes
+ serialized with pg_advisory_xact_lock (#867).
+
+
+
Data Retention & Minimization
@@ -156,26 +168,6 @@ export function GdprPane() {
/>
-
- Activity log hash chain — Fixed
-
- Both security audit and activity log hash chains are now GDPR-safe.
- PII fields excluded from hash computation (#541, #866) so
- anonymization preserves chain integrity. Activity log writes
- serialized with pg_advisory_xact_lock (#867),
- preventing chain forking on concurrent writes.
-
-
-
-
- Export rate limit — Fixed
-
- DSAR export rate limit migrated to distributed Redis (#865).
- Shared across instances, survives deploys/restarts. Matches
- existing auth rate limiter pattern.
-
-
-
{/* ── End Game ── */}
@@ -206,27 +198,6 @@ export function GdprPane() {
/>
-
-
- Hard-delete for messages — Done
-
- Implemented: 30-day purge cron hard-deletes soft-deleted messages.
- FK cascade on account deletion removes user-owned messages
- immediately. Remaining gap: shared-page assistant messages with{" "}
- userId: null may persist.
-
-
-
- Hash-chain-safe anonymization — Done
-
- Implemented: PII fields excluded from hash computation in both
- security audit (#541) and activity log (#866) chains. Activity
- log writes serialized with advisory locks (#867). Anonymization
- preserves chain integrity.
-
-
-
-
AI consent is already handled