Mail is an experimental, manually addressed one-recipient plain-text app. Implementation and evidence status are recorded separately below; this document is not a security certification.
The work branch starts at 05975676618690069e708100fc96e125215ad5fa, fetched from develop
on 2026-09-07 using the repository's leumor identity. This is the PR-298 squash merge (#1399).
Compared with inspected 9f51bbcc2de35655fb5151f7fbc0a4a855d98902, it retains additional XML
fallback, profile verification snapshot, mock signature and review-policy/output-path fixes.
| Boundary | Audited gap | Design decision |
|---|---|---|
| Crypto | Ed25519 exists; no implemented mail HPKE | Use pinned BC LTS 2.73.12.1 HPKE base X25519/SHA256/AES128GCM; separate mail inner signature |
| Vault | Publisher identity is a placeholder; generic signing grants do not authorize opening | Independent dedicated signing, recipient and storage identities with purpose-scoped operations |
| Worker | Existing app launchers idle; platform adapters do not forward to processes | Real signed Java worker and fixed authenticated poll/reply broker, never stdout IPC |
| Persistence | Atomic CAS is single-record, not a multi-record transaction | One encrypted bounded dataset contains contacts, drafts, outbox, inbox and replay ledger |
| Transport | Existing generated-document and bounded content-fetch paths | Worker fixes insertion to CHK and validates manually imported CHK read references |
| Lifecycle | Uninstall deletes vault identities even with preserved app data | Data-only recovery requires the same retained vault; uninstall warning is mandatory |
| Evidence | Local tests cannot close protected producer or independent-review gaps | Report crypto, process, live network, recovery and independent review separately |
Remote datastore/transport parties can observe size, timing, the visible recipient selector and CHK access patterns, and can replay, reorder, withhold or corrupt ciphertext. HPKE protects message contents assuming trusted uncompromised endpoints. The out-of-band channel can disclose its own metadata. There is no anonymity or forward-secrecy guarantee: later compromise of a retained recipient private key can expose historical messages. Local expiry or deletion cannot erase network copies.
A substituted contact can redirect future encryption. A contact's self-signature proves control of its signing key, not a person's identity. Full fingerprint comparison and explicit local approval are required. A changed pin invalidates any pending send approval. Contact graphs remain private.
Unrelated apps and origins are denied by current signed manifests, app authentication, purpose capabilities and vault grants. Browser sessions never receive worker tokens or private keys. Malformed inputs are bounded before crypto, fully authenticated before parsing plaintext, and never create replay evidence before signature, pin, recipient and time checks succeed.
The daemon, vault, OS account, worker and own UI are trusted endpoint components. Non-exportable keys do not protect against compromise of those components. Plaintext exists transiently in the worker, vault open/sign calls and intentional browser compose/read responses. It must not persist in ordinary app-data, browser storage, process logs, support exports or public test evidence.
The Java Mail process owns all mailbox decisions and persistence. The daemon owns only vault
cryptography, fixed transient IPC mediation, app-scoped storage and existing network transport.
There is no daemon mailbox engine or generic RPC/proxy. Worker unavailability fails closed.
AppHost selects the Java 25+ executable. The Linux sandbox exposes an external runtime's bin
and lib plus fixed public security files at their JVM lookup paths and required symlink targets;
it does not mount the broader private conf directory. A real bubblewrap regression starts a
relocated runtime with symlinked security configuration and checks that private management
configuration remains inaccessible (skipped explicitly on hosts without usable bubblewrap).
One recipient, UTF-8 text/plain, no attachments or HTML. Body limit is 16 KiB, signed inner
payload 32 KiB, network envelope 64 KiB. The combined private dataset has a 112 KiB plaintext cap
and must also fit the 262144-byte app-data record cap after protection. Contacts, records, replay
entries and outstanding operations have positive application caps: 16 contacts, 16 inbox records,
8 outbox operations and 128 replay entries, with four pending worker requests. The byte cap can be
reached first. Intake pauses at quota rather than evicting unexpired replay evidence. Each unfinished
outbox reserves completion metadata space before commit.
Send approval binds exact draft/contact bytes. Sealed bytes and stable operation/queue identifier
commit before insertion. Retry reuses those bytes, including after uncertain enqueue outcomes.
An unexpired failed insert restarts its stable queue identifier through
POST /api/v1/queue/requests/restart; a missing queue request re-enqueues the sealed bytes.
The retry regression exercises the registered Platform API router with a Mail app principal and
a simulated queue runtime; it is not evidence of live network publication.
Inserted means network insertion only; it does not mean delivered or read. Acceptance commits
message and authenticated replay identity together. Re-encryption of the same signed message is
a duplicate; conflicting authenticated content under the same identity is rejected.
Private backups contain protected app state, not vault keys, and remain sensitive. Restore merges available current replay tombstones and contact revocations and enters a recovery state. New sending and receiving remain paused after restore; this version intentionally has no resume operation. Retained messages remain readable. An older full operator backup can revert all local evidence; no independent monotonic authority exists, so replay protection is not rollback-proof. Missing keys block recovery and must never create a misleading empty account. Bundle rollback preserves current keys/grants/data. Default uninstall destroys required keys, even when app data is preserved; data-only backups can consequently become unreadable.
The complete storage ciphertext carries a vault-created Ed25519 signature in a separate storage domain, bound to the app, account and retained storage/signing identities and epochs. The vault verifies it before returning plaintext for dataset load or restore. HPKE base-mode encryption alone cannot prove local provenance: anyone with a storage public key could otherwise forge a dataset, including inbox entries. Only the current process-only storage operation can produce this proof; the existing Mail signing route does not accept its domain. Both key-purpose grants are required. This protects against replacement data from outside the trusted endpoint, not an authorized compromised worker. See exact storage framing.
Earlier unsigned experimental Mail datasets and backups are rejected as invalid. No automatic migration is safe because legacy data has no writer proof. Preserve old data for separate trusted recovery; do not treat it as verified Mail state. The inner schema-1 dataset and retained vault identity records remain unchanged. App-bundle rollback continues to use the current vault's checks. Downgrading the daemon to a vault implementation without writer verification reintroduces this flaw and cannot open the new authenticated storage format.
The external app-service interface remains closed and disabled; consumer proposals are deferred. The own-app UI/worker path is the selected implementation scope. No automatic inbox delivery, rendezvous, USK mailbox, email address, SMTP/IMAP, Freemail interoperability, receipts, unattended sending, ratchet or production security approval is implied.
The Java worker, fixed own-app broker, purpose-scoped vault operations, encrypted CAS dataset,
manual ciphertext insertion/fetch and own-origin control surface are implemented. Platform API
contract 25 adds experimental mail.control, vault.mail.sign, vault.mail.open and
vault.mail.storage; baseline 1.0 remains frozen at contract 19. The new queue completion route
is process-only and requires Mail control as well as queue read permission. The six-profile
current export keeps the original five-profile review as an explicit unchanged selection.
Local integration on Java 25.0.4.1 passed all 26 Mail app tests. These include two independently
signed AppHost workers with separate real vaults and durable app-data stores, the production
MailWorkerBroker, literal read/reply, signed update/rollback and restart deduplication. Actual
child-process kills after sealed commit and after simulated insertion recover the same operation
and ciphertext. Stop/re-entrant vault/data races reject old tokens and replies without deadlock.
Transport in these tests is deliberately simulated; this is level 2 evidence, not a live-node test.
The SDK's 36 tests and developer tool's 226 tests also passed, including new executable SDK Mail
routing, session, Unicode and size-bound checks. Primitive vector tests use the published RFC
known answers and a separately generated public signed-message fixture; see the
wire specification.
The process-log check rejects private-value canaries and permits only an empty log or the exact observed Java 25 native-access advisory for the pinned BC JAR. This is narrower than a complete support-bundle/audit-export canary sweep. HTTP origin policy has existing platform coverage and Mail principal-denial tests; the local process harness does not simulate a hostile browser origin. Neither limitation is independent security-review evidence.
./gradlew spotlessApply build assembleCryptadDist completed successfully with strict dependency
verification on Java 25. The root suite reported 13719 tests, zero failures and seven skipped
platform/provider/benchmark cases. AppHost separately retained two Windows-only skips. Distribution
assembly and the configured Debian packaging path completed; the configured RPM task was skipped.
Existing non-blocking analyzer findings and below-threshold coverage reports remain visible; build
success is not a claim that every repository analyzer is clean. The new Mail production SpotBugs
report has no findings. Focused doclint covers all 15 new production Java files with no warnings.
The requested stable-content-profile-review, stable-platform-api-1x,
stable-legacy-plugin-migration, app-platform and app-platform-docs self-tests passed. These
self-tests exercise their tools and fixtures; only the separate executable profile review can
establish the selected original profiles' current local conformance. No new Mail certification
command is introduced: local evidence comes from the executed Java/JavaScript tests, not
caller-written pass receipts. No Stable Mail eligibility is asserted.
Live insertion requires an explicit operator-approved pair of independent disposable nodes and public synthetic text. The opt-in demonstration is provided but was not executed during implementation. Offline checks cannot report live operations as observed. PR-296/297 protected producers, PR-298 exact-head CI and historical/independent profile interoperability remain open in Phase 12.
See the cross-version observed experiment for the new supervisor/driver boundary and current implementation versus observation status. Its finite Mail cases preserve the existing queue restart route, sealed bytes, quotas and paused restore state. No local adapter test establishes a live delivery or closes the review/lifecycle items below.
Review the composition and deployment boundaries independently before using real private mail. Observe the exact-target two-node command, including restart and reply, only with explicit operator authorization. Complete hostile-origin and full support/audit canary integration coverage. Design retained-key rotation and a reviewed restore-resume policy separately. Explicit same-key contact renewal is described in Mail lifecycle; this version uses one active account/key epoch, does not export private keys, and remains paused after restore. Keep external consumer proposals disabled until a narrow approval interface is implemented and tested. None of this work activates Stable, baseline 1.1 or the missing protected Phase 12 producers.
The app worker writes the exact fixed crypta.mail.initialization.v1 marker to the existing
mail-state/dataset record before the first vault mutation. The marker contains only its format
identifier, without account identifiers, key material or user data. Until the marker is replaced,
only initialization or explicit retained-key restore can produce usable mailbox state.
Initialization reconciles at most one identity per Mail role, reuses successful creations whose
responses were lost, and CAS-replaces the marker with the encrypted schema-1 mailbox. Normal
schema-1 dataset layout is unchanged. No extra journal remains after completion. Its encrypted
storage wrapper requires the writer authentication described above.
The initial absent-record write relies on the existing single current worker; subsequent writes
use the observed record digest.
Retained identity authority is checked by the vault before new key creation, including identities
hidden from an app by revoked metadata grants. Reconciliation never restores grants or replaces
unavailable keys. Missing data with retained identities and no marker remains recovery-required;
an unmarked partial setup from an older bundle cannot safely be inferred to be a new account.
Complete setup before rolling back to a bundle that does not recognize the marker. App-owned
backup is available only after completion. An operator restoring an older raw app-data snapshot
containing a setup marker is subject to the existing rollback limitation: without an independent
monotonic authority, the worker cannot distinguish that snapshot from actual unfinished setup.
Every resumed setup therefore persists a fresh initializationRecoveryEpoch and displays it with
an explicit prior-replay-history warning in initialization/status results. Intake is available in
that visibly flagged epoch; it is not a claim of rollback-proof replay prevention.