Skip to content

Simulation Mode — UX Contract and Guarantees


Purpose

Simulation mode allows users to test their triggers and configurations without actually executing anything. This builds confidence and catches configuration errors before they matter.

Core Guarantee: Simulation shows exactly what WOULD happen without making it happen.


The Contract

What Simulation DOES

  1. Shows trigger evaluation — Would this trigger fire right now? Why or why not?
  2. Shows action sequence — What actions would execute, in what order?
  3. Shows recipients — Who would be notified? Who would receive access?
  4. Shows timing — When would each step happen?
  5. Shows documents affected — Which documents would be released?
  6. Produces a report — Downloadable summary of simulation results

What Simulation NEVER Does

  1. Never sends notifications — No emails, SMS, or push notifications
  2. Never grants access — No permissions changed
  3. Never decrypts documents — Content remains encrypted
  4. Never modifies state — Trigger state unchanged
  5. Never records to audit log — Simulation is not an auditable event
  6. Never contacts external systems — No API calls to recipients

User Interface Requirements

Always-Visible Simulation Banner

When in simulation mode, display prominently:

┌─────────────────────────────────────────────────────────────────────┐
│  🔬 SIMULATION MODE                                                 │
│                                                                     │
│  This is a preview only. No actions will be taken.                 │
│  No notifications sent. No access changed. No data released.       │
│                                                                     │
│  [Exit Simulation]                                                  │
└─────────────────────────────────────────────────────────────────────┘

Simulation Entry Point

┌─────────────────────────────────────────────────────────────────────┐
│  My Vault — Triggers                                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │  Estate Release                                    [Armed] │    │
│  │  Dead man's switch • 7-day check-in • 3-day grace         │    │
│  │                                                            │    │
│  │  [Edit]  [Simulate]  [Disable]                            │    │
│  └────────────────────────────────────────────────────────────┘    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Simulation Report

┌─────────────────────────────────────────────────────────────────────┐
│  🔬 Simulation Report: Estate Release                               │
│  Generated: 2025-12-16 14:32:00 UTC                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  TRIGGER STATUS                                                     │
│  ─────────────────────────────────────────────                     │
│  Would fire now? NO                                                 │
│  Reason: Last check-in was 2 days ago. Next check-in due in 5 days.│
│                                                                     │
│  IF TRIGGER FIRED                                                   │
│  ─────────────────────────────────────────────                     │
│                                                                     │
│  Step 1: Challenge Window (7 days)                                  │
│    • You would receive: Email, SMS, Push notification              │
│    • Secondary contacts notified: John Smith, Jane Doe             │
│    • You could abort during this window                            │
│                                                                     │
│  Step 2: Pending Execution (24 hours)                               │
│    • Final warning sent to all channels                            │
│    • Abort still available with typed confirmation                 │
│                                                                     │
│  Step 3: Execution                                                  │
│    • Notifications sent to:                                        │
│      - john.executor@email.com (Executor)                          │
│      - jane.family@email.com (Family)                              │
│                                                                     │
│    • Access granted to:                                            │
│      - John Smith: Full executor access                            │
│      - Jane Doe: View-only family access                           │
│                                                                     │
│    • Documents released:                                           │
│      - Will_2024.pdf (Class B)                                     │
│      - Account_List.pdf (Class B)                                  │
│      - Letter_to_Children.pdf (Class B)                            │
│                                                                     │
│  Step 4: Release Complete                                           │
│    • Reversal window: 7 days                                       │
│    • After reversal window: Finalized (irreversible)               │
│                                                                     │
│  ─────────────────────────────────────────────                     │
│  NOTHING HAS HAPPENED. This is only a simulation.                  │
│  ─────────────────────────────────────────────                     │
│                                                                     │
│  [Download Report]  [Run Another Simulation]  [Exit]               │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Simulation Scenarios

Users can simulate different scenarios:

1. "What if I died today?"

Simulates death verification trigger with current date.

2. "What if I miss my next check-in?"

Simulates dead man's switch firing.

3. "What if [date] arrives?"

Simulates scheduled trigger at specific date.

4. "What if [event] happens?"

Simulates event-based trigger.


Privacy Considerations

Simulation Visibility

Simulation results may contain sensitive information: - Who receives what documents - Who has what access level - What documents exist

Rules: 1. Simulation requires full authentication 2. Simulation results are not cached server-side 3. Simulation results are not logged to audit trail 4. Downloaded reports are marked "CONFIDENTIAL - SIMULATION ONLY"

Sensitive Content Warning

Before showing simulation results:

┌─────────────────────────────────────────────────────────────────────┐
│  Privacy Notice                                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  This simulation will show:                                        │
│  • Who would receive your documents                                │
│  • What access levels would be granted                             │
│  • Which documents would be released                               │
│                                                                     │
│  Make sure you're in a private location before viewing.            │
│                                                                     │
│  [Show Simulation Results]  [Cancel]                               │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Implementation

Simulation Engine

interface SimulationResult {
  triggerId: string;
  simulatedAt: Date;
  wouldFireNow: boolean;
  fireReason: string | null;
  noFireReason: string | null;

  timeline: SimulationTimelineStep[];
  notifications: SimulationNotification[];
  accessGrants: SimulationAccessGrant[];
  documentsReleased: SimulationDocument[];

  warnings: string[];  // Configuration issues detected
}

interface SimulationTimelineStep {
  step: number;
  name: string;
  description: string;
  duration: string;
  abortAvailable: boolean;
}

interface SimulationNotification {
  recipient: string;
  recipientEmail: string;  // Partially masked: j***@email.com
  channel: 'email' | 'sms' | 'push';
  type: 'warning' | 'release' | 'access_granted';
  timing: string;
}

interface SimulationAccessGrant {
  recipientName: string;
  role: string;
  permissions: string[];
  timing: string;
}

interface SimulationDocument {
  name: string;
  encryptionClass: 'A' | 'B' | 'C';
  size: string;
  willBeDecrypted: boolean;
}

Simulation Execution

async function simulateTrigger(
  triggerId: string,
  scenario: SimulationScenario
): Promise<SimulationResult> {
  const trigger = await getTrigger(triggerId);

  // Create a virtual copy of state for simulation
  const virtualState = createVirtualState(trigger, scenario);

  // Evaluate trigger conditions
  const wouldFire = evaluateTriggerConditions(virtualState);

  // If would fire, walk through execution steps
  let timeline: SimulationTimelineStep[] = [];
  let notifications: SimulationNotification[] = [];
  let accessGrants: SimulationAccessGrant[] = [];
  let documentsReleased: SimulationDocument[] = [];

  if (wouldFire) {
    // Simulate each phase
    timeline = simulateTimeline(trigger);
    notifications = simulateNotifications(trigger);
    accessGrants = simulateAccessGrants(trigger);
    documentsReleased = simulateDocumentRelease(trigger);
  }

  // Check for configuration warnings
  const warnings = validateTriggerConfig(trigger);

  return {
    triggerId,
    simulatedAt: new Date(),
    wouldFireNow: wouldFire,
    fireReason: wouldFire ? getFireReason(virtualState) : null,
    noFireReason: wouldFire ? null : getNoFireReason(virtualState),
    timeline,
    notifications,
    accessGrants,
    documentsReleased,
    warnings
  };
}

Configuration Warnings

Simulation should catch configuration issues:

function validateTriggerConfig(trigger: Trigger): string[] {
  const warnings: string[] = [];

  // No recipients configured
  if (trigger.actions.filter(a => a.type === 'notify').length === 0) {
    warnings.push('No notification recipients configured. No one will be notified when this trigger fires.');
  }

  // No documents to release
  if (trigger.actions.filter(a => a.type === 'release_documents').length === 0) {
    warnings.push('No documents configured for release. This trigger will notify but not share any files.');
  }

  // Executor email not verified
  const executor = getExecutor(trigger);
  if (executor && !executor.emailVerified) {
    warnings.push(`Executor email (${maskEmail(executor.email)}) has not been verified. They may not receive notifications.`);
  }

  // No secondary contacts for dead man's switch
  if (trigger.type === 'dead_man_switch' && !trigger.config.secondary_contacts?.length) {
    warnings.push('No secondary contacts configured. Consider adding someone who can confirm your status before the trigger fires.');
  }

  // Check-in interval very short
  if (trigger.type === 'dead_man_switch' && trigger.config.check_interval_days < 3) {
    warnings.push(`Check-in interval is only ${trigger.config.check_interval_days} days. This may cause accidental triggers during travel or illness.`);
  }

  // Class C documents can't be previewed
  const classC = trigger.actions
    .filter(a => a.type === 'release_documents')
    .flatMap(a => a.documents)
    .filter(d => d.encryptionClass === 'C');

  if (classC.length > 0) {
    warnings.push(`${classC.length} document(s) are Class C (zero-knowledge). Even you cannot preview their contents after upload.`);
  }

  return warnings;
}

Guarantees (Enforceable in Code)

G1: No Side Effects

// Simulation functions must not call any side-effect functions
// Enforced via code review and linting rules

// PROHIBITED in simulation code:
// - await sendEmail(...)
// - await sendSMS(...)
// - await grantAccess(...)
// - await updateTriggerState(...)
// - await auditLog.record(...)
// - await decrypt(...)  // for Class B/C

G2: No Persistent State

// Simulation results are computed, not stored
// No database writes during simulation
// Virtual state is discarded after simulation

function simulateTrigger(triggerId: string, scenario: SimulationScenario) {
  // All state is local to this function
  const virtualState = { ...currentState };  // Copy, not reference

  // Compute results
  const result = computeSimulation(virtualState);

  // virtualState is garbage collected
  // No writes to database occurred
  return result;
}

G3: Authentication Required

// Simulation requires authenticated user who owns the trigger
async function simulateTrigger(triggerId: string, userId: string) {
  const trigger = await getTrigger(triggerId);

  if (trigger.owner_id !== userId) {
    throw new AuthorizationError('Cannot simulate trigger you do not own');
  }

  // Proceed with simulation
}

G4: Clear Visual Distinction

// UI must clearly distinguish simulation from reality
// Enforced via design system and component library

// SimulationBanner component is REQUIRED on all simulation screens
// SimulationBanner cannot be dismissed while in simulation mode
// All simulation output is styled differently (e.g., dashed borders, different background)

Testing Requirements

Unit Tests

  • [ ] Simulation produces correct results for each trigger type
  • [ ] Simulation never calls side-effect functions
  • [ ] Simulation warns on configuration issues
  • [ ] Simulation masks sensitive data appropriately

Integration Tests

  • [ ] Full simulation flow for each trigger type
  • [ ] Simulation results match what would actually happen
  • [ ] Simulation does not modify database state
  • [ ] Simulation report can be downloaded

Manual QA

  • [ ] Simulation banner is always visible
  • [ ] Privacy warning appears before results
  • [ ] Results are clearly labeled as simulation
  • [ ] "Nothing has happened" message is prominent

Edge Cases

Empty Configuration

If trigger has no actions configured, simulation should show:

This trigger has no actions configured.
If it fires, nothing will happen.
Consider adding notification recipients and documents to release.

All Class C Documents

If all documents are Class C (zero-knowledge), simulation should show:

All documents in this trigger are Class C (zero-knowledge).
Even we cannot preview their contents.
Your recipients will receive encrypted files that only they can decrypt
with keys you've shared with them separately.

Executor Not Configured

If no executor is configured:

⚠️ WARNING: No executor configured.
When this trigger fires, no one will receive access to your vault.
Add an executor to ensure your documents are delivered.


This document defines the simulation mode contract. Implementation must guarantee these behaviors. Simulation must never have side effects.