Unsafe integration examples
Concrete Ownfold integration mistakes that expose plaintext, weaken ownership checks, break recovery, or create misleading security guarantees.
These examples are intentionally unsafe. Do not adapt them into production code.
Persisting a plaintext fallback
// Unsafe: the server receives the journal content regardless of the encrypted envelope.
await api.createJournal({
encryptedPayload,
searchableTitle: journal.title,
backupContent: journal.content,
})
Store only the validated encrypted envelope and the minimum routing metadata the application truly needs. Server-side full-text search over plaintext is outside the E2EE boundary.
Use vaultServer.validateEncryptedRecordWrite() before persistence. It rejects this entire request
because searchableTitle and backupContent are unexpected siblings of encryptedPayload.
Sending recovery material to the server
// Unsafe: server logs, middleware, and backups can capture both values.
await api.saveRecovery({ recoveryKit, recoveryPassword })
Recovery Kit generation, download, import, verification, and opening must stay in the browser. The coordination server stores only the active kit identifier and verification status.
Storing raw keys in web storage
// Unsafe: any same-origin script can read this synchronously.
localStorage.setItem("rootKey", exportedRootKey)
Use VaultClient and its IndexedDB storage implementation. Public APIs deliberately do not export
raw root keys.
Trusting client ownership
// Unsafe: an attacker chooses another account's user ID.
const server = createVaultServer({
adapter,
getUserId: async ({ request }) => request.headers.get("x-user-id"),
})
Resolve identity from a verified server session. Every adapter operation must bind vault, device, pairing, and rotation state to that authenticated identity.
Decrypting with untrusted context
// Unsafe: the envelope supplies the expected routing context itself.
await vault.decryptJson({
payload: record.encryptedPayload,
namespace: record.encryptedPayload.context.namespace,
recordId: record.encryptedPayload.context.recordId,
})
Use the namespace and record identifier expected by the application route or repository lookup. Authenticated context detects ciphertext substitution only when compared with an independent expectation.
Reusing account passwords
Do not automatically use, derive from, transmit, or reset the Recovery Kit password with the host account password. Doing so couples server authentication compromise and password reset to vault-key security and creates a misleading recovery promise.
Capturing vault pages
Session replay, DOM snapshots, request-body logging, analytics properties, crash breadcrumbs, and support screenshots can all collect decrypted data. Disable them on every route or component where plaintext can appear, including unlock and recovery errors.
See Host security guidance and the deployment checklist for the corresponding production controls.