Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Operation Categories

This document defines Aura's three-tier classification system for distributed operations. The core insight is that not all operations require consensus - many can proceed optimistically with background reconciliation.

Overview

Operations in Aura fall into three categories based on their effect timing and security requirements:

CategoryNameEffect TimingWhen Used
AOptimisticImmediate local effectLow-risk operations within established contexts
BDeferredPending until confirmedMedium-risk policy/membership changes
CConsensus-GatedBlocked until ceremony completesCryptographic context establishment

The Key Architectural Insight

Ceremonies establish shared cryptographic context. Operations within that context are cheap.

┌─────────────────────────────────────────────────────────────────────┐
│                     CEREMONY (Category C)                           │
│  Invitation acceptance between Alice and Bob                        │
│  - Runs once per relationship                                       │
│  - Establishes ContextId + shared tree roots                        │
│  - Creates relational context journal                               │
│  - All future encryption derives from this                          │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│              OPTIMISTIC OPERATIONS (Category A)                     │
│  Within the established relational context:                         │
│                                                                     │
│  • Create channel     → Just emit ChannelCheckpoint fact            │
│  • Send message       → Derive key from context, encrypt, send      │
│  • Add channel member → Just emit ChannelMemberAdded fact           │
│    (if already in context)                                          │
│                                                                     │
│  Keys derive deterministically: KDF(ContextRoot, ChannelId, epoch)  │
│  No new agreement needed - shared state already exists              │
└─────────────────────────────────────────────────────────────────────┘

Category A: Optimistic Operations

Characteristics:

  • Immediate local effect via CRDT fact emission
  • Background sync via anti-entropy
  • Failure shows status indicator, doesn't block functionality
  • Partial success is acceptable

Examples

OperationImmediate ActionBackground SyncOn Failure
Create channelShow channel, enable messagingFact syncs to membersShow "unsynced" badge
Send messageDisplay in chat immediatelyDelivery receiptsShow "undelivered" indicator
Add contact (within context)Show in listMutual acknowledgmentShow "pending" status
Block contactHide from view immediatelyPropagate to contextAlready effective locally
Update profileShow changes immediatelyPropagate to contactsShow sync indicator
React to messageShow reactionFact syncsShow "pending"

Implementation Pattern

#![allow(unused)]
fn main() {
// Category A operations emit CRDT facts immediately
async fn create_channel_optimistic(&mut self, config: ChannelConfig) -> ChannelId {
    // 1. Generate deterministic channel ID
    let channel_id = ChannelId::derive(&config);

    // 2. Emit fact into existing relational context journal
    self.emit_fact(ChatFact::ChannelCheckpoint {
        channel_id,
        epoch: 0,
        base_gen: 0,
        window: 1024,
    }).await;

    // 3. Channel is immediately usable
    // Key derivation: KDF(ContextRoot, ChannelId, epoch)
    channel_id
}
}

Why This Works

Category A operations work because:

  1. Encryption keys already exist - derived from established context
  2. Facts are CRDTs - eventual consistency is sufficient
  3. No coordination needed - shared state already agreed upon
  4. Worst case is delay - not security issue

Category B: Deferred Operations

Characteristics:

  • Local effect pending until agreement reached
  • UI shows intent immediately with "pending" indicator
  • May require approval from capability holders
  • Automatic rollback on rejection

Examples

OperationImmediate ActionAgreement RequiredOn Rejection
Change channel permissionsShow "pending"Admin approvalRevert, notify
Remove channel memberShow "pending removal"Admin consensusKeep member
Transfer ownershipShow "pending transfer"Recipient acceptanceCancel transfer
Rename channelShow "pending rename"Member acknowledgmentKeep old name
Archive channelShow "pending archive"Admin approvalStay active

Implementation Pattern

#![allow(unused)]
fn main() {
// Category B operations create proposals
async fn change_permissions_deferred(
    &mut self,
    channel_id: ChannelId,
    changes: PermissionChanges,
) -> ProposalId {
    // 1. Create proposal (does not apply effect yet)
    let proposal = Proposal {
        operation: Operation::ChangePermissions { channel_id, changes },
        requires_approval_from: vec![CapabilityRequirement::Role("admin")],
        threshold: ApprovalThreshold::Any,
        timeout_ms: 24 * 60 * 60 * 1000, // 24 hours
    };

    // 2. Emit proposal fact
    let proposal_id = self.emit_proposal(proposal).await;

    // 3. UI shows "pending" state
    // Effect applies when threshold approvals received
    // Auto-reverts on timeout or rejection
    proposal_id
}
}

Approval Thresholds

#![allow(unused)]
fn main() {
pub enum ApprovalThreshold {
    /// Any single holder of the required capability
    Any,
    /// All holders must approve
    Unanimous,
    /// k-of-n approval
    Threshold { required: usize },
    /// Percentage of holders
    Percentage { percent: u8 },
}
}

Category C: Consensus-Gated Operations

Characteristics:

  • Operation does NOT proceed until ceremony completes
  • Partial state would be dangerous or irrecoverable
  • User must wait for confirmation
  • Uses choreographic protocols with session types

Key rotation and membership-change ceremonies (adding devices, changing guardians, group membership changes, etc.) follow a shared contract documented in docs/118_key_rotation_ceremonies.md.

Frontends should start and monitor these ceremonies via the portable workflow layer:

  • aura_app::workflows::ceremonies::start_device_enrollment_ceremony (add device)
  • aura_app::workflows::ceremonies::start_device_removal_ceremony (remove device)
  • aura_app::workflows::ceremonies::monitor_key_rotation_ceremony (shared progress polling)

Examples

OperationWhy Blocking RequiredRisk if Optimistic
Add contact (new relationship)Creates cryptographic contextNo shared keys possible
Create groupMulti-party key agreementInconsistent member views
Add member to groupChanges group keysForward secrecy violation
Guardian rotationKey shares distributed atomicallyPartial rotation = unusable keys
Recovery executionAccount state replacementPartial recovery = corruption
OTA hard forkBreaking protocol changePartial activation = network split
Device revocationSecurity-critical removalAttacker acts before propagation

Implementation Pattern

Category C operations use existing ceremony infrastructure:

#![allow(unused)]
fn main() {
// Category C operations block until ceremony completes
async fn add_contact(&mut self, invitation: Invitation) -> Result<ContactId> {
    // 1. Initiate ceremony
    let ceremony_id = self.ceremony_executor
        .initiate_invitation_ceremony(invitation)
        .await?;

    // 2. Block until completion (user sees progress UI)
    loop {
        match self.ceremony_executor.get_status(&ceremony_id)? {
            CeremonyStatus::Committed => {
                // Context established, contact usable
                return Ok(ContactId::from_ceremony(&ceremony_id));
            }
            CeremonyStatus::Aborted { reason } => {
                return Err(AuraError::ceremony_failed(reason));
            }
            _ => {
                // Still in progress, show status to user
                tokio::time::sleep(POLL_INTERVAL).await;
            }
        }
    }
}
}

Ceremony Properties

All Category C ceremonies implement:

  1. Prestate Binding: CeremonyId = H(prestate_hash, operation_hash, nonce)

    • Prevents concurrent ceremonies on same state
    • Ensures exactly-once semantics
  2. Atomic Commit/Abort:

    • Either fully committed or no effect
    • No partial state possible
  3. Epoch Isolation:

    • Uncommitted key packages are inert
    • No explicit rollback needed on abort
  4. Session Types:

    • Protocol compliance enforced at compile time
    • Communication errors impossible

Decision Tree

Use this tree to categorize new operations:

Does this operation establish or modify cryptographic relationships?
│
├─ YES: Does the user need to wait for completion?
│       │
│       ├─ YES (new context, key changes) → Category C (Blocking Ceremony)
│       │   Examples: add contact, create group, guardian rotation
│       │
│       └─ NO (removal from existing context) → Category B (Deferred)
│           Examples: remove from group (epoch rotation in background)
│
└─ NO: Does this affect other users' access or policies?
       │
       ├─ YES: Is this high-security or irreversible?
       │       │
       │       ├─ YES → Category B (Deferred)
       │       │   Examples: transfer ownership, delete channel, kick member
       │       │
       │       └─ NO → Category A (Optimistic)
       │           Examples: pin message, update topic
       │
       └─ NO → Category A (Optimistic)
           Examples: send message, create channel, block contact

UI Feedback Patterns

Category A: Instant Result with Sync Indicator

┌─────────────────────────────────┐
│ You: Hello everyone!         ✓✓ │  ← Delivered
│ You: Check this out          ✓  │  ← Sent
│ You: New idea                ◐  │  ← Sending
└─────────────────────────────────┘

Effect already applied. Indicator shows sync/delivery status.

Category B: Pending Indicator

┌─────────────────────────────────────────────────────────────────────┐
│ Channel: #project                                                   │
├─────────────────────────────────────────────────────────────────────┤
│ Pending: Remove Carol (waiting for Bob to confirm)                  │
├─────────────────────────────────────────────────────────────────────┤
│ Members:                                                            │
│   Alice (admin)        ✓                                            │
│   Bob (admin)          ✓                                            │
│   Carol                ✓  ← Still has access until confirmed        │
└─────────────────────────────────────────────────────────────────────┘

Proposal shown. Effect NOT applied yet.

Category C: Blocking Wait

┌─────────────────────────────────────────────────────────────────────┐
│                    Adding Bob to group...                           │
│                                                                     │
│    ✓ Invitation sent                                                │
│    ✓ Bob accepted                                                   │
│    ◐ Deriving group keys...                                         │
│    ○ Ready                                                          │
│                                                                     │
│                      [Cancel]                                       │
└─────────────────────────────────────────────────────────────────────┘

User waits. Cannot proceed until ceremony completes.

Status Tracking

SyncStatus (Category A)

#![allow(unused)]
fn main() {
pub enum SyncStatus {
    /// Fact committed locally, not yet synced
    LocalOnly,
    /// Fact synced to some peers
    Syncing { peers_synced: usize, peers_total: usize },
    /// Fact synced to all known peers
    Synced,
    /// Sync failed, will retry
    SyncFailed { retry_at: u64 },
}
}

DeliveryStatus (Messages)

#![allow(unused)]
fn main() {
pub enum DeliveryStatus {
    /// Message queued locally
    Sending,
    /// Message reached at least one recipient
    Sent,
    /// Message reached all online recipients
    Delivered,
    /// Recipient viewed message
    Read,
    /// Delivery failed
    Failed { reason: String },
}
}

ConfirmationStatus (Category B)

#![allow(unused)]
fn main() {
pub enum ConfirmationStatus {
    /// Proposal created, awaiting approvals
    Pending { approvals: usize, required: usize },
    /// Threshold approvals received, applying effect
    Confirming,
    /// Effect applied successfully
    Confirmed,
    /// Some parties approved, others declined
    PartiallyConfirmed { confirmed: Vec<AuthorityId>, declined: Vec<AuthorityId> },
    /// Proposal rejected or timed out
    Rejected { reason: String },
}
}

Effect Policy Configuration

Operations use configurable policies that reference the capability system:

#![allow(unused)]
fn main() {
pub struct EffectPolicy {
    pub operation: OperationType,
    pub scope: ResourceScope,
    pub timing: EffectTiming,
}

pub enum EffectTiming {
    /// Category A: Immediate effect
    Immediate,

    /// Category B: Deferred until approval
    Deferred {
        requires_approval_from: Vec<CapabilityRequirement>,
        timeout_ms: u64,
        threshold: ApprovalThreshold,
    },

    /// Category C: Blocked until ceremony
    Blocking {
        ceremony: CeremonyType,
    },
}
}

Context-Specific Overrides

Contexts can override default policies:

#![allow(unused)]
fn main() {
// Strict security channel: unanimous admin approval for kicks
channel.set_effect_policy(RemoveFromChannel, EffectTiming::Deferred {
    requires_approval_from: vec![CapabilityRequirement::Role("admin")],
    timeout_ms: 48 * 60 * 60 * 1000,
    threshold: ApprovalThreshold::Unanimous,
});

// Casual channel: any admin can kick immediately
channel.set_effect_policy(RemoveFromChannel, EffectTiming::Immediate);
}

Full Operation Matrix

OperationCategoryEffect TimingSecurityNotes
Within Established Context
Send messageAImmediateLowKeys already derived
Create channelAImmediateLowJust facts into context
Update topicAImmediateLowCRDT, last-write-wins
React to messageAImmediateLowLocal expression
Local Authority
Block contactAImmediateLowYour decision
Mute channelAImmediateLowLocal preference
Policy Changes
Change permissionsBDeferredMediumOthers affected
Kick from channelBDeferredMediumAffects access
Archive channelBDeferredLow-MedReversible
High Risk
Transfer ownershipBDeferredHighIrreversible
Delete channelBDeferredHighData loss
Remove from contextBDeferredHighAffects encryption
Cryptographic
Add contactCBlockingCriticalCreates context
Create groupCBlockingCriticalMulti-party keys
Add group memberCBlockingCriticalChanges group keys
Guardian rotationCBlockingCriticalKey shares
Recovery executionCBlockingCriticalAccount state
Device revocationCBlockingCriticalSecurity response

Common Mistakes to Avoid

Mistake 1: Making Everything Category C

Wrong: "Adding a channel member requires ceremony"

Right: If the member is already in the relational context, it's Category A - just emit a fact. Only if they need to join the context first is it Category C.

Mistake 2: Forgetting Context Existence

Wrong: Trying to create a channel before establishing relationship

Right: Contact invitation (Category C) must complete before any channel operations (Category A) are possible.

Mistake 3: Optimistic Key Operations

Wrong: "User can start using new guardians while ceremony runs"

Right: Guardian changes affect key shares. Partial state means unusable keys. Must be Category C.

Mistake 4: Blocking on Low-Risk Operations

Wrong: "Wait for all members to confirm before showing channel"

Right: Channel creation is optimistic. Show immediately, sync status later.