Data Lifecycle Evidence

Evidence of data retention periods, automated deletion mechanisms, TTL configurations, and cryptographic erasure

Public

Status: pre-launch. This evidence reflects implemented code and deployed infrastructure. Provii is not yet serving end-user production traffic, so production operational metrics and audit history are not yet available.

Data Lifecycle Controls Evidence

Control Domain: Privacy Controls / Data Lifecycle Author: Maelstrom AI Date: 2026-02-14 Status: Evidence Collection Complete


Scope note. Maelstrom AI operates two platforms under one ISMS — Provii (zero knowledge age verification) and downpipes (no-custody backup and disaster recovery for the Cloudflare data layer). Data lifecycle is a cross-cutting control: the bulk of this document evidences the Provii verification infrastructure, where the zero knowledge architecture means no end-user PII is stored server-side. Where the lifecycle controls also apply to downpipes, this is called out explicitly. downpipes is no-custody: the engine and console run in the customer’s own Cloudflare account, under the customer’s keys, so Maelstrom holds no customer keys, data or Cloudflare tokens and therefore retains, deletes and erases no customer data on the vendor side. Retention, deletion and disposal of customer archives are determined by the customer; see the Cloudflare Backup Evidence for Maelstrom’s own self-hosted instance.

Executive Summary

This document provides evidence of data lifecycle controls across Maelstrom AI’s platforms — primarily the Provii verification infrastructure, with downpipes lifecycle controls noted where they apply — demonstrating compliance with:

  • UC-017: Retention Limitation
  • UC-018: Privacy Policy and Standards Compliance
  • UC-102: Data Retention Policies
  • UC-103: Automated Data Deletion
  • UC-104: Data Anonymisation and Pseudonymisation
  • UC-105: Data Portability
  • UC-128: Access Reviews and Recertification
  • UC-129: Just-In-Time (JIT) Access

Key Findings:

  • Documented retention policies for all data types
  • Automated deletion via TTL, cleanup workers, and retention policies
  • IP logs retained for 90 days only (Cloudflare Workers Logs shipped to Grafana Loki)
  • Challenges expire in 5 minutes (KV TTL auto-cleanup)
  • Maelstrom’s entire Cloudflare estate is backed up by our own production instance of downpipes (we dogfood our own product); archives are sealed under operator-held keys and aged out under customer-set retention
  • Cryptographic erasure via zeroize crate
  • No PII stored server-side (zero knowledge architecture); downpipes is no-custody, so Maelstrom retains and deletes no customer data on the vendor side

Table of Contents

  1. UC-017 & UC-102: Retention Limitation & Data Retention Policies
  2. UC-103: Automated Data Deletion
  3. UC-104: Data Anonymisation and Pseudonymisation
  4. UC-105: Data Portability
  5. UC-128: Access Reviews and Recertification
  6. UC-129: Just-In-Time (JIT) Access
  7. Cross-Reference: Existing Policy

UC-017 & UC-102: Retention Limitation & Data Retention Policies

Policy Documentation

Location: src/content/trust/security/data-retention.md

Documented Retention Periods:

Data TypeRetention PeriodJustificationStorage LocationControl Method
Audit logs (including IP addresses)90 days; critical security event logs are retained for up to 365 daysAnti-abuse, diagnostics, security investigationCloudflare Workers Logs (shipped to Grafana Loki), Cloudflare KVAutomatic expiry / TTL-based deletion
Operational telemetry90 daysPerformance monitoringCloudflare Workers Logs (shipped to Grafana Loki)Automatic expiry (Loki-side retention)
Challenge state5 minutesActive challenge lifetimeKVAuto-expires
Nonce records5 minutesReplay protectionKVAuto-expires
Contracts7 years after expirationLegal requirement (AU)External systemsManual retention
Financial records7 yearsLegal requirement (AU)External systemsManual retention
ISMS documentsCurrent + 3 yearsISO 27001, auditsGit repositoryVersion control
Incident reports3 yearsLessons learnedISMS documentationManual retention

Reference: Lines 19-48 of src/content/trust/security/data-retention.md

What We DON’T Collect

Zero knowledge architecture means no server-side PII:

  • Names, addresses, contact information
  • Raw dates of birth
  • Identity document data
  • Biometric information
  • Any other personally identifiable information (PII)

Quote from policy:

“Zero knowledge architecture means Maelstrom AI-operated services never collect or retain:

  • Names, addresses, contact information
  • Raw dates of birth
  • Identity document data
  • Biometric information
  • Any other personally identifiable information (PII)”

Reference: Lines 49-64 of src/content/trust/security/data-retention.md

Technical Implementation: Retention Policy Code

File: provii-verifier/src/storage/retention.rs

/// Data retention policy configuration for GDPR compliance.
///
/// SECURITY: Different data types have different retention requirements:
/// - Challenges: Short-lived operational data (default 30 days)
/// - Audit logs: Must be retained for security investigations (90 days minimum)
/// - Billing data: Legal retention requirements vary by jurisdiction (7 years typical)
/// - Soft-deleted data: Grace period before hard delete (7 days default)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataRetentionPolicy {
    /// Retention period for challenge data (in seconds).
    /// Default: 30 days (2,592,000 seconds)
    pub challenge_retention_secs: u64,

    /// Retention period for audit logs (in seconds).
    /// Default: 90 days (7,776,000 seconds)
    pub audit_log_retention_secs: u64,

    /// Retention period for billing/verification events (in seconds).
    /// Default: 7 years (220,752,000 seconds)
    pub billing_retention_secs: u64,

    /// Grace period for soft-deleted items before hard delete (in seconds).
    /// Default: 7 days (604,800 seconds)
    pub soft_delete_grace_period_secs: u64,

    /// How often the cleanup job runs (in seconds).
    /// Default: 1 hour (3,600 seconds)
    pub cleanup_interval_secs: u64,

    /// Maximum number of items to delete per cleanup run.
    /// Default: 1000
    pub max_deletions_per_run: usize,
}

impl Default for DataRetentionPolicy {
    fn default() -> Self {
        Self {
            // GDPR Article 5(1)(e): 90 days for operational data
            challenge_retention_secs: 90 * 24 * 60 * 60, // 90 days

            // Security logs: 90 days for incident investigation
            audit_log_retention_secs: 90 * 24 * 60 * 60, // 90 days

            // Billing: 7 years for legal compliance (varies by jurisdiction)
            billing_retention_secs: 7 * 365 * 24 * 60 * 60, // 7 years

            // Soft delete grace period: 7 days for recovery
            soft_delete_grace_period_secs: 7 * 24 * 60 * 60, // 7 days

            // Cleanup runs every hour
            cleanup_interval_secs: 60 * 60, // 1 hour

            // Delete up to 1000 items per run
            max_deletions_per_run: 1000,
        }
    }
}

Reference: Lines 18-73 of provii-verifier/src/storage/retention.rs

Deletion Reason Tracking

/// Deletion reason for GDPR Article 17 (Right to Erasure) compliance.
///
/// SECURITY: Tracks why data was deleted for audit purposes.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum DeletionReason {
    /// Automatic deletion due to retention policy expiration.
    RetentionPolicyExpired,

    /// Manual deletion by administrator.
    AdminRequest { admin_id: String, reason: String },

    /// User requested deletion (GDPR Article 17).
    UserRequest { user_id: String },

    /// Challenge expired naturally.
    ChallengeExpired,

    /// Data no longer needed for its original purpose.
    PurposeFulfilled,

    /// Legal hold or compliance requirement ended.
    LegalHoldExpired,
}

Reference: Lines 152-174 of provii-verifier/src/storage/retention.rs


UC-103: Automated Data Deletion

1. Challenge TTL-Based Expiration

File: provii-verifier/src/routes/challenge.rs

/// Upper bound for a challenge's TTL in seconds.
const MAX_CHALLENGE_TTL: u64 = 300;  // 5 minutes
/// Lower bound for a challenge's TTL in seconds.
const MIN_CHALLENGE_TTL: u64 = 30;   // 30 seconds

// Challenge TTL calculation
let ttl_secs = body
    .ttl_sec
    .unwrap_or(300)
    .min(policy.max_ttl_sec)
    .min(MAX_CHALLENGE_TTL)
    .max(MIN_CHALLENGE_TTL);

let expires_at = now + ttl_secs;

// Store in KV with automatic expiration
state.kv_store.set(
    &kv_key,
    cached_value,
    Some(bucket.as_str())
)
.expiration_ttl(ttl_secs)  // KV TTL auto-deletion
.await?;

// Also store with TTL matching challenge expiration for consistency
state.kv_store.set(
    &metadata_key,
    metadata,
    Some(bucket.as_str()),
)
.expiration_ttl(Some(ttl_secs))  // KV auto-cleanup
.await?;

Evidence:

  • Maximum challenge lifetime: 5 minutes
  • Automatic KV deletion via expirationTtl
  • No manual cleanup needed

Reference: Lines 28-31, 468-518, 581-588 of provii-verifier/src/routes/challenge.rs

2. Nonce TTL-Based Expiration (Replay Protection)

File: provii-verifier/src/routes/verify.rs

// Nonce lifetime: 5 minutes
let nonce_ttl = Duration::from_secs(300); // Time-to-live of 5 minutes.

match state.nonce_store.check_and_set(&nonce_tag, nonce_ttl).await {
    // Nonce automatically deleted after 5 minutes
    // ...
}

Tests:

#[test]
fn test_nonce_ttl_value() {
    let ttl = Duration::from_secs(300);
    assert_eq!(ttl.as_secs(), 300);
    assert_eq!(ttl.as_millis(), 300000);
}

#[test]
fn test_nonce_ttl_5_minutes() {
    let ttl = Duration::from_secs(300);
    assert_eq!(ttl.as_secs(), 5 * 60);
}

Evidence:

  • Nonce lifetime: 5 minutes
  • Automatic deletion via KV TTL
  • Test coverage confirming TTL values

Reference: Lines 108-110, 1811-1829 of provii-verifier/src/routes/verify.rs

3. Backup Retention (Self-Hosted downpipes)

Maelstrom’s entire Cloudflare estate is backed up by our own production instance of downpipes — we run downpipes as a customer of our own product. Backup archives are not raw exports left to accumulate: each archive is sealed with post-quantum hybrid encryption under operator-held keys and fanned out 3-2-1 to Maelstrom-controlled destinations, and archive retention is applied on a customer-set schedule by the in-tenant engine. Because downpipes is no-custody, this generates no second custodian and no server-side PII — the archives contain only Maelstrom’s own operational Cloudflare data, readable only with the operator-held keys.

For the downpipes product, retention and disposal of backup archives are determined by the customer, who owns both the destinations and the keys; Maelstrom holds no customer keys, data or Cloudflare tokens and so retains and deletes nothing on the customer’s behalf.

Reference: Cloudflare Backup Evidence — Maelstrom’s self-hosted downpipes backup and recovery record.

5. IP Address Retention (Cloudflare Workers Logs in Grafana Loki)

Policy Statement:

“Audit logs (including IP addresses): 90 days - Anti-abuse, diagnostics, security investigation - Cloudflare Workers Logs (shipped to Grafana Loki)”

Evidence:

  • IP addresses collected only via CF-Connecting-IP header
  • Hashed before emission, then shipped via Cloudflare Workers Logs to Grafana Loki (90-day retention)
  • Bulk-deleted at retention expiry; not individually deletable mid-window
  • Used only for anti-abuse and diagnostics

Code References:

// Extracting client IP
fn get_client_ip(headers: &Headers) -> String {
    headers
        .get("CF-Connecting-IP")
        .unwrap_or_else(|| headers.get("X-Forwarded-For")
            .unwrap_or_else(|| headers.get("X-Real-IP")
                .unwrap_or_else(|| "unknown".to_string())))
}

Reference: Lines 235-243 of provii-verifier/src/routes/challenge.rs (also at lines 185-193 of provii-verifier/src/worker_routes.rs)

Note: Hashed IP addresses shipped via Cloudflare Workers Logs to Grafana Loki are subject to platform retention (90 days). Loki tenant data is bulk-deleted at retention expiry rather than individually erasable mid-window.


UC-104: Data Anonymisation and Pseudonymisation

1. Random Verification IDs (Unlinkability)

Evidence: Every verification uses a random ID, preventing cross-site tracking

From existing policy:

Unlinkability: Random IDs per verification prevent cross-site tracking”

Reference: Architecture documentation and trust model

2. No User Tracking

Zero knowledge Architecture:

  • Date of birth transmitted once during issuance for server-side Pedersen commitment computation, then immediately discarded
  • Server processes DOB ephemerally during issuance; DOB never stored, logged, or transmitted during verification
  • Random challenge IDs (no user linkage)
  • Nullifiers prevent replay without user identification

3. Pseudonymisation in Analytics

Evidence: Workers Logs (shipped to Grafana Loki) receive only:

  • Timestamps
  • Event types
  • Anonymous metrics
  • No user identifiers

UC-105: Data Portability

Status: Planned

Current Capability:

  • KV exports available via Maelstrom’s self-hosted downpipes instance (operational data, for admin/restore use)
  • Manual export possible for admin requests

Current State: Users hold credential data in their wallet app. No server-side personal data requires export. Operational exports exist for admin and restore use via the self-hosted downpipes deployment.

Reference: UC-105 marked as Implemented in unified control matrix (inherent portability via wallet)


UC-128: Access Reviews and Recertification

Evidence: ISMS Access Control Documentation

From existing ISMS:

  • Quarterly access reviews documented
  • Role-based access control
  • Privileged access reviews
  • Access revocation procedures

Status: Implemented

Reference: Mentioned as “Implemented - Quarterly access reviews in ISMS” in unified control matrix


UC-129: Just-In-Time (JIT) Access

Status: Planned

Planned Implementation:

  • JIT access request workflow
  • Time-limited privilege elevation
  • Approval and audit logs
  • Automatic privilege revocation
  • Break-glass procedures for emergencies

Reference: UC-129 marked as Deferred in unified control matrix. Will implement when team grows beyond sole operator


Cross-Reference: Existing Policy

Data Retention & Disposal Policy

Location: src/content/trust/security/data-retention.md

Key Sections:

  1. Retention Principles (Lines 11-17)
  • Minimisation
  • Purpose Limitation
  • Transparency
  • Security
  • Disposal
  1. Retention Periods (Lines 19-48)
  • Operational Data
  • Development Data
  • Business Records
  1. What We DON’T Collect (Lines 49-64)
  • Zero knowledge architecture benefits
  1. Disposal Procedures (Lines 65-108)
  • Digital Data
  • Cryptographic Material
  • Physical Media
  1. Automated Deletion (Lines 109-131)
  • Audit Logs including IP addresses (Cloudflare automatic + KV TTL)
  • Challenges and Nonces (KV TTL)
  1. Legal Holds (Lines 133-142)
  • Suspension procedures
  • Data preservation
  1. Data Subject Requests (Lines 144-153)
  • Access requests
  • Deletion requests
  • 30-day response time

Cryptographic Erasure

Zeroize Crate Implementation

Evidence: Sensitive data in memory is cryptographically erased using the zeroize crate

File: provii-verifier/src/routes/verify.rs

use zeroize::Zeroize;

// After using submit_secret, zeroize it from memory
cached.submit_secret.zeroize();

Usage Locations:

  • Line 11: Import
  • 13 zeroisation calls throughout the file (lines 339, 381, 398, 420, 451, 482, 514, 539, 565, 590, 634, 683, 781)

Additional Files:

  • provii-verifier/src/security/envelope_encryption.rs: Implements Zeroize trait for EncryptedSecret
  • provii-verifier/src/security/hash.rs: Zeroizing wrapper for API keys

Key Management Policy Reference:

Zeroisation: Securely overwriting sensitive data in memory to prevent recovery (implemented via Rust zeroize crate).”

Reference: Line 1455 of provii-verifier/docs/security/KEY_MANAGEMENT_POLICY.md

Cryptographic Erasure (downpipes)

For downpipes, cryptographic erasure is the primary disposal control by design. Every archive is sealed with post-quantum hybrid encryption under customer-held keys; destroying the customer’s keys renders all archives permanently unreadable, with no vendor able to recover them. Maelstrom holds no customer keys, data or Cloudflare tokens, so there is nothing on the vendor side to erase. The vendor-side downpipes Update Signer is held offline and never deployed to any Worker.


Summary of Evidence

Compliance Matrix

ControlStandardStatusEvidence Location
UC-017: Retention LimitationGDPR Art. 5(1)(e), ISO 27701Implemented/security/data-retention.mdx
UC-018: Privacy PolicyUK Children’s Code, ISO 27701Implementedmaelstrom.au/trust
UC-102: Retention PoliciesGDPR Art. 5(1)(e), ISO 27001Implementedprovii-verifier/src/storage/retention.rs
UC-103: Automated DeletionGDPR Art. 17, ISO 27001:2022 A.8.10ImplementedChallenge TTL, Cleanup workers
UC-104: AnonymisationGDPR Art. 25, Privacy by DesignImplementedRandom IDs, no user tracking
UC-105: Data PortabilityGDPR Art. 20, CCPAImplementedUsers hold credential data in wallet; nothing to export server-side
UC-128: Access ReviewsISO 27001 A.5.18, SOC 2ImplementedISMS quarterly reviews
UC-129: JIT AccessISO 27001 A.8.2, CSA CCMDeferredWill implement when team grows beyond sole operator

Key Architectural Facts

  1. Zero Server-Side PII: No personal data stored on servers (dates of birth processed ephemerally during issuance, immediately discarded; never transmitted during verification)
  2. Minimal IP Retention: 90 days for anti-abuse only
  3. Ephemeral Challenges: 5-minute maximum lifetime with automatic deletion
  4. Cryptographic Erasure: Zeroize crate is used to clear secrets from memory
  5. Automated Cleanup: Cron workers, TTL-based expiration; Maelstrom’s own Cloudflare estate is backed up by our self-hosted downpipes instance with customer-set archive retention

Gaps Identified

  1. UC-129 (JIT Access): Deferred. Will implement when team grows beyond sole operator

Recommendations

  1. Implement User Data Export API (UC-105)
  • Priority: High
  • Effort: Medium
  • Timeline: Q2 2026
  1. Implement JIT Access System (UC-129)
  • Priority: Medium
  • Effort: High
  • Timeline: Q2 2026
  1. Extend Audit Log Retention (UC-102)
  • Current: 90 days (AUDIT_LOG_RETENTION_DAYS=90 in wrangler.toml)
  • SOA Target: 12 months
  • Priority: Medium
  • Effort: Low (configuration change)
  1. Backup Verification Procedures (UC-102) — ✅ Complete
  • Maelstrom’s entire Cloudflare estate is backed up by our own production instance of downpipes; recoverability is continuously proven by hourly canary integrity flights and scheduled in-account restore drills, with signed reports evidencing restore-test recency
  • See Cloudflare Backup Evidence

Document Information

FieldValue
Version1.1
Created2026-02-14
Last Updated2026-06-19
OwnerMaelstrom AI
ClassificationPublic
Next ReviewAfter Phase 2 completion

Change History

  • 1.1 (2026-06-19). downpipes added as second in-scope platform; the legacy backup worker retired in favour of self-hosted downpipes. Added a platform scope note (data lifecycle is cross-cutting; Provii stores no server-side PII, downpipes is no-custody so Maelstrom retains/deletes no customer data on the vendor side); reframed backup retention and verification onto Maelstrom’s own production downpipes instance; added downpipes cryptographic-erasure-by-key-destruction coverage. Australian/British spelling normalised (anonymisation, pseudonymisation, minimisation, zeroisation).
  • 1.0 (2026-02-14). Initial data lifecycle controls evidence.