Import & Export
Export and import allows you to persist inbox credentials for later use. This is useful for debugging failed tests, manual testing scenarios, and cross-environment verification.
Overview
Section titled “Overview”Export/Import enables you to:
- Persist inbox credentials to disk
- Resume inbox access in later test runs
- Debug email issues by replaying scenarios
- Share inboxes across environments (securely)
Export Format
Section titled “Export Format”Exported inbox data is stored as JSON:
{ "version": 1, "expiresAt": "2024-01-15T10:30:00Z", "inboxHash": "hash123...", "serverSigPk": "base64url...", "secretKey": "base64url...", "exportedAt": "2024-01-14T10:30:00Z"}The public key is not included in exports as it can be derived from the secret key during import.
Basic Export
Section titled “Basic Export”To Object
Section titled “To Object”Inbox inbox = client.createInbox();
// Export to objectExportedInbox exported = client.exportInbox(inbox);
// Or export by email addressExportedInbox exported = client.exportInbox(inbox.getEmailAddress());
// Access propertiesString email = exported.getEmailAddress();String expiresAt = exported.getExpiresAt();To File
Section titled “To File”Inbox inbox = client.createInbox();
// Export directly to JSON fileclient.exportInboxToFile(inbox, Path.of("inbox.json"));Basic Import
Section titled “Basic Import”From Object
Section titled “From Object”// Restore inbox from exported dataInbox restored = client.importInbox(exported);
// Use restored inbox normallyEmail email = restored.waitForEmail();From File
Section titled “From File”// Import from JSON fileInbox restored = client.importInboxFromFile(Path.of("inbox.json"));
// Use restored inboxList<Email> emails = restored.listEmails();ExportedInbox Properties
Section titled “ExportedInbox Properties”| Property | Type | Description |
|---|---|---|
version | int | Export format version (currently 1) |
emailAddress | String | Inbox email address |
expiresAt | String | ISO 8601 expiration timestamp |
inboxHash | String | Unique inbox identifier |
serverSigPk | String | Server signature public key (ML-DSA-65, base64url) |
secretKey | String | ML-KEM-768 secret key (base64url) |
exportedAt | String | ISO 8601 export timestamp |
Validation
Section titled “Validation”The validate() method performs comprehensive integrity checks:
| Check | Description |
|---|---|
| Version | Must be 1 (current format version) |
| Required fields | All fields must be present and non-empty |
| Email format | Must contain exactly one @ character |
| Timestamp format | expiresAt and exportedAt must be valid ISO 8601 |
| Base64url encoding | Keys must be valid base64url-encoded strings |
| Secret key size | Must be exactly 2400 bytes (raw ML-KEM-768) |
| Server signature key size | Must be exactly 1952 bytes (raw ML-DSA-65) |
| Expiration | Inbox must not have expired |
ExportedInbox exported = client.exportInbox(inbox);
// Validate data integritytry { exported.validate();} catch (InvalidImportDataException e) { System.err.println("Export data is invalid:"); for (String error : e.getErrors()) { System.err.println(" - " + error); }}
// Check expiration manually (also done by validate())Instant expires = Instant.parse(exported.getExpiresAt());if (expires.isBefore(Instant.now())) { throw new IllegalStateException("Inbox has expired");}Security Best Practices
Section titled “Security Best Practices”Use Temporary Files
Section titled “Use Temporary Files”Path tempFile = Files.createTempFile("inbox-", ".json");try { client.exportInboxToFile(inbox, tempFile); // Use file...} finally { Files.deleteIfExists(tempFile);}Never Commit to Version Control
Section titled “Never Commit to Version Control”*-inbox.json*.inbox.json/test-exports/Encrypt for Persistent Storage
Section titled “Encrypt for Persistent Storage”// If you must persist, encrypt the exportExportedInbox exported = client.exportInbox(inbox);String json = gson.toJson(exported);String encrypted = encryptionService.encrypt(json);Files.writeString(Path.of("inbox.enc"), encrypted);Use Cases
Section titled “Use Cases”Debug Failed Tests
Section titled “Debug Failed Tests”Export inbox on test failure for later debugging:
class EmailTest { private Inbox inbox; private boolean failed = false;
@AfterEach void cleanup(TestInfo info) throws IOException { if (failed && inbox != null) { Path path = Path.of( "build/failed-inboxes", info.getDisplayName().replaceAll("[^a-zA-Z0-9]", "_") + ".json" ); Files.createDirectories(path.getParent()); client.exportInboxToFile(inbox, path); System.out.println("Exported inbox to: " + path); } }
@Test void testEmailFlow() { try { inbox = client.createInbox(); // Test code... } catch (AssertionError | Exception e) { failed = true; throw e; } }}Reproduce the issue later:
Inbox inbox = client.importInboxFromFile( Path.of("build/failed-inboxes/testEmailFlow.json"));
for (Email email : inbox.listEmails()) { System.out.println("Subject: " + email.getSubject()); System.out.println("From: " + email.getFrom()); System.out.println("Text: " + email.getText()); System.out.println("---");}Manual Testing
Section titled “Manual Testing”Create a long-lived inbox for manual testing:
Inbox inbox = client.createInbox( CreateInboxOptions.builder() .ttl(Duration.ofHours(24)) .build());
System.out.println("Send test emails to: " + inbox.getEmailAddress());System.out.println("Expires: " + inbox.getExpiresAt());
// Export for later useclient.exportInboxToFile(inbox, Path.of("manual-test-inbox.json"));Check results later:
Inbox inbox = client.importInboxFromFile(Path.of("manual-test-inbox.json"));List<Email> emails = inbox.listEmails();System.out.println("Received " + emails.size() + " emails");Raw Email Analysis
Section titled “Raw Email Analysis”Get raw MIME content for detailed debugging:
Inbox inbox = client.importInboxFromFile(Path.of("debug-inbox.json"));
for (Email email : inbox.listEmails()) { System.out.println("=== " + email.getSubject() + " ===");
// Get raw MIME content String raw = inbox.getRawEmail(email.getId()); System.out.println(raw);}Error Handling
Section titled “Error Handling”Invalid Import Data
Section titled “Invalid Import Data”try { Inbox inbox = client.importInbox(exported);} catch (InvalidImportDataException e) { System.err.println("Invalid data: " + e.getMessage()); // Data may be corrupted, incomplete, or tampered with}Inbox Already Exists
Section titled “Inbox Already Exists”try { Inbox inbox = client.importInbox(exported);} catch (InboxAlreadyExistsException e) { // Inbox was already imported in this client Inbox existing = client.getInbox(exported.getEmailAddress());}Expired Inbox
Section titled “Expired Inbox”Check expiration before importing:
ExportedInbox exported = loadFromFile(path);
Instant expires = Instant.parse(exported.getExpiresAt());if (expires.isBefore(Instant.now())) { throw new IllegalStateException( "Inbox expired at " + exported.getExpiresAt() );}
Inbox inbox = client.importInbox(exported);File Not Found
Section titled “File Not Found”try { Inbox inbox = client.importInboxFromFile(Path.of("inbox.json"));} catch (IOException e) { System.err.println("Could not read file: " + e.getMessage());}Testing Patterns
Section titled “Testing Patterns”JUnit 5 Extension
Section titled “JUnit 5 Extension”class SaveOnFailureExtension implements TestWatcher { @Override public void testFailed(ExtensionContext context, Throwable cause) { // Get inbox from test instance Object testInstance = context.getRequiredTestInstance(); if (testInstance instanceof EmailTestBase base) { Inbox inbox = base.getInbox(); if (inbox != null) { try { Path path = Path.of( "build/failed-inboxes", context.getDisplayName() + ".json" ); Files.createDirectories(path.getParent()); base.getClient().exportInboxToFile(inbox, path); } catch (IOException e) { // Ignore } } } }}
@ExtendWith(SaveOnFailureExtension.class)abstract class EmailTestBase { protected abstract Inbox getInbox(); protected abstract VaultSandboxClient getClient();}Reusable Test Data Helper
Section titled “Reusable Test Data Helper”class TestInboxes { private static final Path EXPORTS_DIR = Path.of("src/test/resources/inboxes"); private final VaultSandboxClient client;
public TestInboxes(VaultSandboxClient client) { this.client = client; }
public void save(String name, Inbox inbox) throws IOException { Files.createDirectories(EXPORTS_DIR); client.exportInboxToFile(inbox, EXPORTS_DIR.resolve(name + ".json")); }
public Inbox load(String name) throws IOException { return client.importInboxFromFile(EXPORTS_DIR.resolve(name + ".json")); }}Limitations
Section titled “Limitations”| Limitation | Description |
|---|---|
| Inboxes expire | Export doesn’t extend TTL - inbox still expires at original time |
| Same API key required | Import must use the same API key that created the inbox |
| Contains private keys | Security risk if export files are leaked |
| Metadata only | Export contains credentials, not the emails themselves |
Best Practices
Section titled “Best Practices”-
Security First
- Use temp files and delete after use
- Never log or commit private keys
- Encrypt exports if persistence is required
-
Check Expiration
- Validate expiration before import
- Use appropriate TTL when creating inboxes
- Handle expired imports gracefully
-
Validate Data
- Call
validate()before import - Catch and handle import exceptions
- Log failures for debugging
- Call
-
Appropriate Use Cases
- Debugging failed tests
- Manual testing scenarios
- Cross-environment verification
- Not for long-term storage (inboxes expire)
Next Steps
Section titled “Next Steps”- Managing Inboxes - Create and configure inboxes
- Waiting for Emails - Email delivery strategies
- CI/CD Testing - Continuous integration patterns