Queue
Reference documentation for the local intent queue — lifecycle management, persistence, state transitions, and trust tier validation.
QueueStatus
The queue status lifecycle has four states:
Pending ──→ SyncedProvisional ──→ SyncedFinalized
│ │
└──────────────┴──→ Failed { stage, reason }
States
| State | Description |
|---|---|
Pending | Created locally, waiting for connectivity. Intent is stored in the local queue |
SyncedProvisional | Accepted by the exit target (L1 confirmation for payments, backend acceptance for tickets/actions). Not yet finalized |
SyncedFinalized | Confirmed by the exit target. Terminal state — no further transitions possible |
Failed { stage, reason } | Rejected at a specific pipeline stage. Carries FailureStage and RejectionReason for structured error handling |
Transition Rules
| From | To | Trigger | Enforced by |
|---|---|---|---|
| Pending | SyncedProvisional | mark_provisional() | Core QueueEntry |
| SyncedProvisional | SyncedFinalized | finalize() | Core QueueEntry |
| Pending | Failed | fail() | Core QueueEntry |
| SyncedProvisional | Failed | fail() | Core QueueEntry |
| SyncedFinalized | (none) | — | Terminal — immutable |
Key invariants:
SyncedFinalizedcannot transition to any other state (terminal)finalize()fromPendingis rejected — must pass throughSyncedProvisionalfirstFailedentries retain their failure information for debugging and compliance
Content-Hash Dedup
The queue rejects duplicate payloads at creation time using FNV-1a content hashing:
fn enqueue_intent(entry: &QueueEntry, current_queue: &[QueueEntry]) -> Result<(), RejectionReason> {
if current_queue.iter().any(|q| q.intent.content_hash == entry.intent.content_hash) {
return Err(RejectionReason::DuplicateInLocalQueue);
}
Ok(())
}
The hash is payload-only, excluding trust tier and envelope. Duplicate payloads are rejected regardless of trust tier.
Encrypted Persistence
AES-256-GCM
Queue data can be encrypted at rest:
- Algorithm: AES-256-GCM (12-byte random nonce, 16-byte auth tag)
- Scope: All
QueueEntryfields — payloads, signatures, status, timestamps - Key: 32-byte raw key, app-supplied. The SDK does not derive, store, rotate, or validate the key
- Activation: Must be called before any intent creation. Rejects if queue already contains unencrypted entries
Initialization
// Expo: platform-managed key (recommended)
import { initWithSecureStorage } from '@zypp-labs/expo-sdk';
initWithSecureStorage(platformKey); // 32 bytes from iOS Keychain / Android Keystore
// Expo: custom key management
import { initWithEncryption } from '@zypp-labs/expo-sdk';
initWithEncryption(myKey); // 32 bytes from your key management
// Unity
ZyppSdk.InitWithSecureStorage(platformKey);
ZyppSdk.InitWithEncryption(myKey);
Both initWithSecureStorage and initWithEncryption are identical at runtime. initWithSecureStorage signals the key comes from platform secure storage.
KindRegistry
The KindRegistry validates that intents using FreeFormPayload (Ticket and Action) have registered their kind string and expected trust tier before they can be queued.
Registration
import { registerKind } from '@zypp-labs/expo-sdk';
const warnings = registerKind({
kind: 'swap_tokens',
intent_type: 'Action',
trust_tier: 'Signed',
fields: []
});
Registration returns Vector<LintWarning> — warns when high-stakes action kinds (matching patterns like "spend", "trade", "claim") are registered as Unsigned.
Validation Gates
The queue enforces two validation gates before accepting intents:
- Mandatory registration: Non-Payment intents with unregistered kinds are rejected
- Trust tier cross-check: Intent's trust tier must meet or exceed the registered kind's expected tier
Lint
import { lint } from '@zypp-labs/expo-sdk';
// Audit all registered kinds and queued intents
const warnings = lint();
// Each warning has: kind, severity (Info/Warning/Error), message
Lint is a development-time safety net, not runtime enforcement. Actual tier enforcement happens at template processing time.
Connectivity Reporting
The sync engine monitors device connectivity and exposes a quality signal:
import { reportConnectivity } from '@zypp-labs/expo-sdk';
// Available quality levels
reportConnectivity('offline');
reportConnectivity('degraded');
reportConnectivity('good');
The sync engine uses this signal to coordinate settlement timing and routing strategy.
Offline Transfer
Queued intents can be transferred between devices without network connectivity:
import { createTransferPacket, decodeTransferPacket } from '@zypp-labs/expo-sdk';
// Create transfer packet (base64-encoded, versioned)
const { packetB64 } = createTransferPacket(intentId);
// Decode on receiving device
const decoded = decodeTransferPacket(packetB64);
The transfer layer produces compact binary packets suitable for BLE, NFC, QR codes, or any nearby-device channel.
Next Steps
- Explore the Intent type system
- Configure Settlement routing
- Learn about state machine security