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