Legal

Demo Data Handling

Last updated May 24, 2026

ShieldScore offers a public demo experience that creates a short-lived organization scoped to a single browser session. This page documents precisely how those demo organizations and their cascade data are retained, scrubbed, and deleted — and how a prospect, auditor, or customer can verify our claims independently.

This is part of our build-complete posture: the public claim (“we don't keep demo data forever”) is bound to an executable mechanism, signed evidence, and a verbatim audit query.

Tombstone Two-Timer Mechanism

Ephemeral demo organizations move through two retention boundaries:

  • T1 — Soft-tombstone at createdAt + 1 hour + grace. The organization row and all child rows (audit logs, evidence metadata, control assignments) remain in the database; sessions are invalidated; authentication is blocked. The 1-hour boundary matches the demo session token TTL. Bridge-window note: the demo-org sweep cron that creates the T1 tombstone shares the same DEMO_ORG_SWEEP_DRY_RUN flag as the T2 hard-purge pass and is currently in DRY_RUN observation through the 2026-06-13 LIVE flip target — no new tombstones are being written during the bridge window. T1 access-block is enforced live at the auth layer for any organization with tombstonedAt already set (including the R13.AC L2 backfill cohort), and after the LIVE flip the sweep cron resumes creating new tombstones automatically.
  • T2 — Hard-purge (shipped 2026-05-24 / PR #154, currently in DRY_RUN observation; LIVE flip target 2026-06-13) at tombstonedAt + 90 days (environment-tunable via TRUST_CENTER_DEMO_RETENTION_DAYS). When a demo organization reaches that T2 boundary after the 2026-06-13 LIVE flip, the organization is soft-deleted, child rows are FK-rewritten to a per-organization system sentinel actor (preserving audit-log Merkle-chain integrity), and PII-bearing telemetry is purged. Current substrate state and substrate-gap disclosure are at /incidents/r13-ac.

Real customer organizations are never subjected to the Tombstone Two-Timer policy. Customer-initiated offboarding uses a separate GDPR Article 17 erasure path with explicit operator-initiated request and an extended retention window scoped to the customer's contractual obligations.

PII Scrub at T2 Hard-Purge (not at T1 tombstone)

At T1 (soft-tombstone) the demo organization is marked tombstoned and authentication is blocked, but no cascade User PII is scrubbed — the signed deletion receipt produced at T1 carries scrubMechanism: "t1-tombstone-only" and affectedCounts.usersScrubbed: 0 reflecting that semantic. T1 is a tombstone-and-access-block event, not a PII-scrub event.

At T2 (hard-purge, once the stored retentionUntil elapses — default 90 days post-tombstone), the cascade users on the tombstoned organization have their personally identifiable fields scrubbed. T2 runs in two phases: Phase 1 (pseudonymization pre-pass, outside any per-org transaction, idempotent) performs the User field scrubs below; Phase 2 (per-organization Serializable transaction) writes the FK rewrites to a per-org system sentinel actor, the organization soft-delete, and the ORG_HARD_PURGED audit-log row together.

  • User email is replaced with a cryptographically random placeholder of the form scrubbed-<random-uuid>@redacted.invalid. The replacement is irreversible by construction — no salt is preserved, no recovery table is maintained. The domain redacted.invalid is reserved by RFC 2606 and is non-routable globally.
  • User display name is set to NULL.
  • MFA recovery codes for cascade users are deleted (expected count zero — the demo flow does not enroll MFA; the delete is a defense-in-depth no-op that produces an auditable count).
  • Rate-limit event telemetry rows scoped to the tombstoned organization have ip, userAgent, cfRay, and cfCountry fields set to NULL.

T2 Phase 2 revalidates the organization's eligibility predicates (orgType, tombstonedAt, retentionUntil, deletedAt) inside the per-organization transaction immediately before writing the audit-log evidence. A stale candidate (already soft-deleted by another runner, or with shifted retentionUntil) is silently skipped without producing audit evidence.

The historical one-shot L2 backfill script at scripts/backfill-r13-ac-stale-demo-orgs.ts retains stricter pre-tombstone C3 invariant assertions on image, passwordHash, mfaSecret, and stripeCustomerId, halting the run on non-conforming rows with a separate upstream-finding audit entry — that is the operator-driven backfill behavior for one-shot remediation runs, not the recurring T1/T2 substrate behavior described above.

Signed Deletion Receipt

Every tombstone event emits a signed deletion receipt — a JSON-LD artifact signed (RS256 via AWS KMS) using the same Trust Center signing key that backs our posture API and JWKS endpoint. The current key set (active kid + JWK parameters) is exposed at https://api.shieldscore.ai/.well-known/jwks.json.

The receipt payload contains:

  • The redacted organization identifier
  • tombstoneReason (e.g. EPHEMERAL_DEMO_STALE)
  • tombstonedAt and retentionUntil timestamps
  • Affected cascade counts (users scrubbed, MFA recovery codes deleted, rate-limit events scrubbed)
  • The decision provenance — links to the Tier-1 verdict that authorized the mechanism
  • The scrub mechanism (cuid_replacement_random_email_local_part) and PII-invariant verification per user per field

The receipt template is documented at docs/compliance/deletion-receipt-template.md in our public repository, including JWS canonicalization (RFC 8785 JCS) and a worked verification example.

Self-Verification

Customer compliance leads, prospects, and auditors can verify ShieldScore's demo data retention discipline at two layers: the historical L2 one-shot backfill (a fixed 2026-05-17 event), and the PR-γ recurring T1/T2 substrate that runs daily as of 2026-05-24.

Layer 1 — Historical L2 backfill (2026-05-17 one-shot)

The L2 one-shot backfill tombstoned 14 historical EPHEMERAL_DEMO_STALE demo organizations on 2026-05-17. The per-execution audit artifact is published at:

docs/operational/audits/r13-ac-backfill-2026-05-17T00-29-36-718Z.json

The artifact contains the redacted target list, per-organization outcomes, aggregate counts (including aggregateOutcomes.orgsTombstoned: 14), the post-execute Stripe sub-processor sweep result, and the verbatim audit-log query a reviewer can run against the API or via a customer-shared evidence-export:

SELECT COUNT(*)
FROM "AuditLog"
WHERE action = 'EPHEMERAL_DEMO_TOMBSTONED'
  AND "createdAt" >= '2026-05-17'::date
  AND "createdAt" <  '2026-05-18'::date;

The count returned should match aggregateOutcomes.orgsTombstoned (= 14) in the L2 run artifact. Each row is a per-tombstone audit-log entry with a Merkle chain hash chained to the prior live entry for that organization — chain integrity is verifiable via the existing audit-log chain verification path. This artifact is fixed evidence of the L2 one-shot backfill; it does not describe the current recurring T1/T2 substrate.

Layer 2 — Current PR-γ recurring T1/T2 substrate (post-2026-05-24)

The recurring substrate that runs daily as the demo-org-sweep scheduler (registered at boot with a 13-minute stagger, 1-day fire interval) currently operates in DRY_RUN through the 2026-06-13 LIVE flip target. In DRY_RUN, per-tick evidence is operator-visible scheduler logs and the daily sweep summary; no AuditLog rows are produced (per-tick summaries record auditLogEmitted: false).

After the 2026-06-13 LIVE flip, LIVE candidate-mutating ticks produce customer-verifiable AuditLog rows: T1 ticks write EPHEMERAL_DEMO_TOMBSTONED rows with the full signed deletion-receipt envelope embedded in metadata; T2 Phase 2 ticks write ORG_HARD_PURGED rows inside per-organization Serializable transactions that also write the FK rewrites and organization soft-delete. Each row is Merkle-chained to the prior live entry for that organization, so chain integrity is verifiable via the existing audit-log chain verification path. The verbatim audit-log query a reviewer can run for recurring-substrate evidence (post-LIVE-flip) is:

SELECT action, COUNT(*) AS row_count,
       MIN("createdAt") AS first_row,
       MAX("createdAt") AS last_row
FROM "AuditLog"
WHERE action IN ('EPHEMERAL_DEMO_TOMBSTONED', 'ORG_HARD_PURGED')
  AND "createdAt" >= '2026-06-13'::date
GROUP BY action
ORDER BY action;

A reader who runs that query before any LIVE candidate-mutating tick has fired (e.g., between the 2026-06-13 LIVE flip and the first tick that finds a T2-eligible candidate, which depends on stored retentionUntil thresholds) will see zero rows — that is the correct, honest state. Recurring substrate AuditLog evidence appears only on actual candidate-mutating events; the PR-δ-final closure will cite the first such row, or explicitly disclose if no candidate was eligible within the closure window.

Sub-Processor Accountability (Stripe)

ShieldScore's demo flow does not create Stripe customer objects: the demo path is unauthenticated and writes only local ephemeral state, so there is no Stripe-side data for the recurring T1/T2 substrate to delete. There is no per-tick Stripe interaction in the PR-γ recurring sweep because no new sub-processor exposure is introduced at T1 or T2 that would warrant per-tick re-verification.

To verify the absence empirically at the historical L2 backfill event (2026-05-17), the L2 backfill ran a post-execute read-only sweep against the Stripe API for each tombstoned organization and recorded the result in the L2 audit artifact as stripeCustomersVerifiedAbsent: 14 (14 of 14 tombstoned organizations verified absent from the Stripe Customers index). That sweep addresses GDPR Article 28 sub-processor accountability for the L2 one-shot event.

The Stripe wrapper used by the L2 backfill is structurally read-only — the class exposes only stripe.customers.search via a typed wrapper that forbids any .create, .update, or .delete method at compile time. Should a future PR add per-tick Stripe verification to the recurring T1/T2 substrate, the same read-only wrapper will be reused.

Standards Conformance

The Tombstone Two-Timer discipline satisfies the following standards. Where the satisfying mechanism differs between the historical L2 one-shot backfill and the PR-γ recurring T1/T2 substrate, both are cited explicitly:

  • GDPR Article 5(1)(c) — data minimization: cascade fields scrubbed at T2 hard-purge (after the stored retentionUntil elapses) to the maximum extent compatible with audit-log chain integrity. T1 tombstone is access-block + signed receipt only; no PII is retained beyond the T2 hard-purge window.
  • GDPR Article 5(2) — accountability:
    • L2 one-shot backfill (2026-05-17): per-tombstone audit-log evidence with paired pre_assert: null_verified + post_scrub: null_confirmed fields proving invariant ratification, not performative scrub claims.
    • PR-γ recurring substrate (post-2026-05-24): T1 ticks produce EPHEMERAL_DEMO_TOMBSTONED rows with the full signed deletion-receipt envelope embedded in metadata; T2 Phase 2 ticks produce ORG_HARD_PURGED rows inside per-organization Serializable transactions that also write FK rewrites and organization soft-delete. T1 receipts honestly use scrubMechanism: "t1-tombstone-only" and zero scrub counts to reflect T1 semantics. All rows are Merkle-chained to prior live entries for the same organization.
  • GDPR Article 28 — sub-processor disclosure: ShieldScore's demo flow is unauthenticated and never creates Stripe customer objects; the historical L2 backfill ran an independent post-execute read-only Stripe Customer search per tombstoned organization to verify absence (recorded as stripeCustomersVerifiedAbsent: 14 in the L2 audit artifact). The PR-γ recurring substrate does not repeat the Stripe sweep at each tick because the demo flow introduces no new Stripe relationships at T1 or T2.
  • SOC 2 CC9.2 — monitoring: the recurring scheduler emits per-tick result summaries; per-organization failures route through Sentry with kind tags (t1, t2-phase-1, t2-phase-2) and extra.orgId; three consecutive per-org failures escalate to systemic fatal level. AuditLog Merkle chain integrity is verifiable via the existing audit-log chain verification path. The historical L2 backfill artifact remains published and version-controlled as the L2 monitoring evidence.
  • HIPAA §164.514(b) — safe-harbor de-identification of demo-cascade users at T2 hard-purge (not at T1 tombstone). No individually identifiable direct or quasi-identifying field is retained on the scrubbed row after T2.
  • HIPAA §164.530(j) — 6-year documentation retention: the historical L2 per-execution audit artifact is published and version-controlled; the recurring substrate produces queryable AuditLog rows (post-LIVE-flip) that themselves satisfy the documentation retention requirement.

Questions or Verification Requests

For verification questions, demo-data retention disputes, or signed receipt requests for a specific tombstoned organization, contact privacy@shieldscore.ai. Operational implementation details and the canonical decision record are maintained at docs/operational/sessions/2026-05-16-session-9.md in our public source repository.