Inboxes
Inboxes are the core concept in VaultSandbox. Each inbox is an isolated, encrypted email destination with its own unique address and encryption keys.
What is an Inbox?
Section titled “What is an Inbox?”An inbox is a temporary, encrypted email destination that:
- Has a unique email address (e.g.,
[email protected]) - Uses client-side encryption (ML-KEM-768 keypair)
- Expires automatically after a configurable time-to-live (TTL)
- Is isolated from other inboxes
- Stores emails in memory on the gateway
Creating Inboxes
Section titled “Creating Inboxes”Basic Creation
Section titled “Basic Creation”using VaultSandbox.Client;
var client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .Build();
var inbox = await client.CreateInboxAsync();
Console.WriteLine(inbox.InboxHash); // "Rr02MLnP7F0pRVC6QdcpSIeyklqu3PDkYglvsfN7Oss"Console.WriteLine(inbox.ExpiresAt); // DateTimeOffsetWith Options
Section titled “With Options”var inbox = await client.CreateInboxAsync(new CreateInboxOptions{ Ttl = TimeSpan.FromHours(1), // Default: 1 hour});Note: Requesting a specific email address may fail if it’s already in use. The server will return an error.
Inbox Properties
Section titled “Inbox Properties”EmailAddress
Section titled “EmailAddress”Type: string
The full email address for this inbox.
Console.WriteLine(inbox.EmailAddress);// "[email protected]"Send emails to this address to have them appear in the inbox.
InboxHash
Section titled “InboxHash”Type: string
A unique cryptographic hash identifier for the inbox. This is used internally for encryption and identification purposes.
Console.WriteLine(inbox.InboxHash);// "Rr02MLnP7F0pRVC6QdcpSIeyklqu3PDkYglvsfN7Oss"Note: This is not the same as the local part of the email address. The email address local part (e.g., a1b2c3d4 in [email protected]) is different from the InboxHash.
ExpiresAt
Section titled “ExpiresAt”Type: DateTimeOffset
When the inbox will automatically expire and be deleted.
Console.WriteLine(inbox.ExpiresAt);// 2024-01-16T12:00:00.000+00:00
// Check if inbox is expiring soonvar timeUntilExpiry = inbox.ExpiresAt - DateTimeOffset.UtcNow;Console.WriteLine($"Expires in {timeUntilExpiry.TotalHours:F1} hours");Inbox Lifecycle
Section titled “Inbox Lifecycle”┌─────────────────────────────────────────────────────────┐│ Inbox Lifecycle │└─────────────────────────────────────────────────────────┘
1. Creation client.CreateInboxAsync() → IInbox ↓ - Keypair generated client-side - Public key sent to server - Unique email address assigned - TTL timer starts
2. Active ↓ - Receive emails - List/read emails - Wait for emails - Monitor for new emails
3. Expiration (TTL reached) or Manual Deletion ↓ client.DeleteInboxAsync(emailAddress) or TTL expires - All emails deleted - Inbox address freed - Keypair destroyedWorking with Inboxes
Section titled “Working with Inboxes”Listing Emails
Section titled “Listing Emails”var emails = await inbox.GetEmailsAsync();
Console.WriteLine($"{emails.Count} emails in inbox");
foreach (var email in emails){ Console.WriteLine($"{email.From}: {email.Subject}");}Getting a Specific Email
Section titled “Getting a Specific Email”var email = await inbox.GetEmailAsync(emailId);
Console.WriteLine(email.Subject);Console.WriteLine(email.Text);Waiting for Emails
Section titled “Waiting for Emails”// Wait for any emailvar email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(30)});
// Wait for specific emailvar email = await inbox.WaitForEmailAsync(new WaitForEmailOptions{ Timeout = TimeSpan.FromSeconds(30), Subject = "Password Reset",});Deleting Emails
Section titled “Deleting Emails”// Delete specific emailawait inbox.DeleteEmailAsync(emailId);Deleting Inbox
Section titled “Deleting Inbox”Inboxes can be deleted via the client or by disposing them:
// Delete inbox via clientawait client.DeleteInboxAsync(inbox.EmailAddress);
// Or dispose the inbox (stops subscription but doesn't delete from server)await inbox.DisposeAsync();Inbox Isolation
Section titled “Inbox Isolation”Each inbox is completely isolated:
var inbox1 = await client.CreateInboxAsync();var inbox2 = await client.CreateInboxAsync();
// inbox1 cannot access inbox2's emails// inbox2 cannot access inbox1's emails
// Each has its own:// - Email address// - Encryption keys// - Email storage// - Expiration timeTime-to-Live (TTL)
Section titled “Time-to-Live (TTL)”Inboxes automatically expire after their TTL:
Default TTL
Section titled “Default TTL”// Uses server's default TTL (1 hour)var inbox = await client.CreateInboxAsync();Custom TTL
Section titled “Custom TTL”// Expire after 1 hourvar inbox = await client.CreateInboxAsync(new CreateInboxOptions{ Ttl = TimeSpan.FromHours(1)});
// Expire after 10 minutes (useful for quick tests)var inbox = await client.CreateInboxAsync(new CreateInboxOptions{ Ttl = TimeSpan.FromMinutes(10)});
// Expire after 7 daysvar inbox = await client.CreateInboxAsync(new CreateInboxOptions{ Ttl = TimeSpan.FromDays(7)});Checking Expiration
Section titled “Checking Expiration”var timeLeft = inbox.ExpiresAt - DateTimeOffset.UtcNow;
if (timeLeft < TimeSpan.FromMinutes(5)){ Console.WriteLine("Inbox expiring soon!");}Import and Export
Section titled “Import and Export”Inboxes can be exported and imported for:
- Test reproducibility
- Sharing between environments
- Backup and restore
Export
Section titled “Export”var exportData = await inbox.ExportAsync();
// Save to fileawait File.WriteAllTextAsync("inbox.json", JsonSerializer.Serialize(exportData));Import
Section titled “Import”var json = await File.ReadAllTextAsync("inbox.json");var exportData = JsonSerializer.Deserialize<InboxExport>(json);
var inbox = await client.ImportInboxAsync(exportData!);
// Inbox restored with all encryption keysFile-Based Export/Import
Section titled “File-Based Export/Import”The client also provides convenient file-based methods:
// Export to fileawait client.ExportInboxToFileAsync(inbox, "inbox.json");
// Import from filevar inbox = await client.ImportInboxFromFileAsync("inbox.json");Security Warning: Exported data contains private keys. Treat as sensitive.
Best Practices
Section titled “Best Practices”CI/CD Pipelines
Section titled “CI/CD Pipelines”Short TTL for fast cleanup:
var inbox = await client.CreateInboxAsync(new CreateInboxOptions{ Ttl = TimeSpan.FromHours(1)});Always clean up:
var inbox = await client.CreateInboxAsync();try{ // Run tests}finally{ await client.DeleteInboxAsync(inbox.EmailAddress);}Manual Testing
Section titled “Manual Testing”Longer TTL for convenience:
var inbox = await client.CreateInboxAsync(new CreateInboxOptions{ Ttl = TimeSpan.FromDays(1)});Export for reuse:
// Export after creatingvar exportData = await inbox.ExportAsync();await File.WriteAllTextAsync("test-inbox.json", JsonSerializer.Serialize(exportData));
// Reuse in later sessionsvar json = await File.ReadAllTextAsync("test-inbox.json");var importData = JsonSerializer.Deserialize<InboxExport>(json);var inbox = await client.ImportInboxAsync(importData!);Production Monitoring
Section titled “Production Monitoring”Monitor expiration:
var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
while (await timer.WaitForNextTickAsync()){ var timeLeft = inbox.ExpiresAt - DateTimeOffset.UtcNow; if (timeLeft < TimeSpan.FromMinutes(10)) { Console.WriteLine($"Inbox {inbox.EmailAddress} expiring in {timeLeft.TotalMinutes:F0} minutes"); }}Common Patterns
Section titled “Common Patterns”Dedicated Test Inbox (xUnit)
Section titled “Dedicated Test Inbox (xUnit)”public class EmailTests : IAsyncLifetime{ private IVaultSandboxClient _client = null!; private IInbox _inbox = null!;
public async Task InitializeAsync() { _client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .Build();
_inbox = await _client.CreateInboxAsync(new CreateInboxOptions { Ttl = TimeSpan.FromHours(2) }); }
public async Task DisposeAsync() { await _client.DeleteInboxAsync(_inbox.EmailAddress); await _client.DisposeAsync(); }
[Fact] public async Task Password_Reset_Should_Send_Email() { await TriggerPasswordReset(_inbox.EmailAddress);
var email = await _inbox.WaitForEmailAsync(new WaitForEmailOptions { Timeout = TimeSpan.FromSeconds(10) });
Assert.NotNull(email); }}Dedicated Test Inbox (NUnit)
Section titled “Dedicated Test Inbox (NUnit)”[TestFixture]public class EmailTests{ private IVaultSandboxClient _client = null!; private IInbox _inbox = null!;
[OneTimeSetUp] public async Task SetUp() { _client = VaultSandboxClientBuilder.Create() .WithBaseUrl(url) .WithApiKey(apiKey) .Build();
_inbox = await _client.CreateInboxAsync(new CreateInboxOptions { Ttl = TimeSpan.FromHours(2) }); }
[OneTimeTearDown] public async Task TearDown() { await _client.DeleteInboxAsync(_inbox.EmailAddress); await _client.DisposeAsync(); }
[Test] public async Task Password_Reset_Should_Send_Email() { await TriggerPasswordReset(_inbox.EmailAddress);
var email = await _inbox.WaitForEmailAsync(new WaitForEmailOptions { Timeout = TimeSpan.FromSeconds(10) });
Assert.That(email, Is.Not.Null); }}Multiple Inboxes
Section titled “Multiple Inboxes”var user1Inbox = await client.CreateInboxAsync();var user2Inbox = await client.CreateInboxAsync();var adminInbox = await client.CreateInboxAsync();
// Each inbox receives emails independentlyawait SendWelcomeEmail(user1Inbox.EmailAddress);await SendWelcomeEmail(user2Inbox.EmailAddress);await SendAdminReport(adminInbox.EmailAddress);Inbox Pool
Section titled “Inbox Pool”public class InboxPool : IAsyncDisposable{ private readonly IVaultSandboxClient _client; private readonly ConcurrentQueue<IInbox> _pool = new(); private readonly int _size;
public InboxPool(IVaultSandboxClient client, int size = 5) { _client = client; _size = size; }
public async Task InitializeAsync() { for (var i = 0; i < _size; i++) { var inbox = await _client.CreateInboxAsync(); _pool.Enqueue(inbox); } }
public IInbox? Get() { return _pool.TryDequeue(out var inbox) ? inbox : null; }
public async ValueTask DisposeAsync() { while (_pool.TryDequeue(out var inbox)) { await _client.DeleteInboxAsync(inbox.EmailAddress); } }}Troubleshooting
Section titled “Troubleshooting”Inbox Not Receiving Emails
Section titled “Inbox Not Receiving Emails”Check:
- Email is sent to correct address
- Inbox hasn’t expired
- DNS/MX records configured correctly
- SMTP connection successful
// Verify inbox still existstry{ var emails = await inbox.GetEmailsAsync(); // Will throw if inbox expired}catch (InboxNotFoundException){ Console.WriteLine("Inbox has expired");}Inbox Already Exists Error
Section titled “Inbox Already Exists Error”When requesting a specific email address:
try{ var inbox = await client.CreateInboxAsync(new CreateInboxOptions { });}catch (InboxAlreadyExistsException){ // Address already in use, generate random instead var inbox = await client.CreateInboxAsync();}Inbox Expired
Section titled “Inbox Expired”try{ var emails = await inbox.GetEmailsAsync();}catch (InboxNotFoundException){ Console.WriteLine("Inbox has expired"); // Create new inbox var newInbox = await client.CreateInboxAsync();}Next Steps
Section titled “Next Steps”- Email Objects - Learn about email structure
- Managing Inboxes - Common inbox operations
- Import/Export - Advanced inbox persistence
- API Reference: Inbox - Complete API documentation