Contract (L1)
1. Crates and handle¶
The workspace-wide table is architecture/L2-crate-topology.
The runtime contract is separate from the node implementations so the bundled desktop app embeds the authority now and a remote-authority deployment puts a replica behind the same contract later:
posthaste-contract-core
the shared wire vocabulary: typed MailOperation (the one operation enum),
MutationRequest/MutationReceipt, MutationSettlementState, opaque ids,
RuntimeFrame/ViewFrame/ViewSnapshot/MailListViewState, RuntimeAdapterError,
mutation_args, mail_query
posthaste-runtime-api
the typed, wire-free domain RPC trait: returns serde domain types, no frames
posthaste-client-link
the client↔runtime link-ops trait (RuntimeLink): forward_mutation,
subscribe_runtime_frames -> RuntimeFrame stream, link/view-stream ops
caller context, links, views, coverage, resources
posthaste-runtime
the near node: RuntimeHandle implements runtime-api + client-link
RuntimeBuildConfig and RuntimeBuild output, RuntimeShutdownHandle
view service, mutation coordinator, resource resolver, pending set
assembly of config, secrets, SQLite store, domain service, event bus,
and the account supervisor (owned by posthaste-authority-server, the far node)
There is no separate replica-runtime crate: a deployment whose authority is remote implements the same two contract traits from the same near-node crate, with the pending set converging toward the remote authority.
The contract crates are free of Axum, Tauri, React, provider-client,
SQLite-table, and replica-table types. posthaste-contract-core additionally
sits on the wasm-pure frontier (serde-only; no tokio, reqwest, rusqlite,
mail-parser, axum).
2. Authority server handle shape¶
The authority implementation splits transport-neutral runtime code from adapter code:
RuntimeHandle cloneable implementation of the runtime-api + client-link
contract traits (the split of the former RuntimeCore)
AuthorityRuntimeCore config repo, secret store, SQLite store, domain service,
account supervisor, event bus/history, query/view service,
mutation coordinator, resource resolver
RuntimeShutdownHandle graceful shutdown and task joins
The seam is load-bearing: Axum and Tauri adapters call the same runtime contract instead of rebuilding service/store/supervisor state. Mail behavior is never duplicated in the two transports.
3. Adapter-neutral caller context¶
Runtime handle methods receive an adapter-neutral caller context:
Adapters authenticate and translate transport identity into this context. The runtime uses it for authorization, diagnostics, mutation attribution, and settlement routing. The handle must not depend on HTTP headers, Tauri window labels, or React component state for authorization decisions.
4. Runtime API groups¶
The shared runtime contract exposes these method groups:
| Group | Purpose |
|---|---|
| Lifecycle | Start enabled accounts, reload config, report status, shut down gracefully. |
| Reads | Local runtime/API projections for settings, accounts, mailboxes, smart mailboxes, tags, messages, conversations, and query pages. |
| Views | Connect renderer links, open/update/close views, subscribe to view frames. |
| Mutations | Run named mutations, inspect mutation state, retry, undo, resolve conflicts. |
| Events | Subscribe to runtime events and read event history for API catch-up/snapshot. |
| Resources | Resolve body, attachment, and source resources into bytes, handles, or scoped capability URLs. |
These groups do not live on one god-trait: the typed domain reads are
posthaste-runtime-api (wire-free, serde domain types) and the link, view,
frame-stream, and mutation-forward ops are posthaste-client-link.
Adapters do not reach around the handle to private service/store fields for a method that belongs to the runtime contract.
5. Security and storage contract¶
5.1 Runtime-owned roots¶
Bundled mode resolves runtime-owned roots before building the handle:
| Root | Contents |
|---|---|
| Config root | Non-secret app/account settings, smart-mailbox definitions, automation rules, secret references, account assets (e.g. logos). |
| State root | SQLite store, event history when stored outside SQLite, sync cursors, mutation idempotency records, provider work queues, runtime status snapshots, daemon/bridge metadata only when a daemon mode owns it. |
| Cache root | Body/blob, attachment, and source/raw-message cache data and metadata. |
The cache root may be a subdirectory of the state root; it is still runtime-owned and not renderer storage. Dev/test builds may override roots through explicit process configuration; production uses platform application data/config/cache locations. Overrides must not make renderer-owned directories the runtime roots.
5.2 Renderer-owned storage¶
Renderer-owned storage is limited to presentation and connection-profile state: theme/appearance, window geometry and pane sizes, layout/sort UI and recent surfaces, connection-profile metadata with no secrets, and transient adapter/link state. It must not contain SQLite copies, message bodies, attachment bytes, event history, mutation idempotency records, provider credentials, refresh/access/API tokens, or runtime secret references usable without the runtime.
5.3 Secret stores¶
Provider credentials, refresh tokens, and access tokens stay in the authority server secret store; config may contain secret references and redacted status only. A desktop client profile's remote API token for a non-bundled deployment lives in a client secret store distinct from the runtime provider-secret store; connection-profile JSON may reference it but must not inline it. Secrets are never written to packaged JavaScript, localStorage, renderer-owned JSON, logs, crash reports, URLs, query strings, or static assets.
6. Assertions¶
| ID | Sev. | Assertion |
|---|---|---|
| runtime-contract-implementation-neutral | MUST | The shared runtime contract is free of SQLite-table, provider-client, replica-table, invalidation-command, Axum, Tauri, and React types. |
| runtime-handle-transport-neutral | MUST | Mail behavior is exposed through one runtime contract implemented by the authority server handle rather than duplicated in Axum and Tauri transports. |
| runtime-handle-no-transport-types | MUST | Runtime handle methods use domain/runtime inputs and adapter-neutral caller context, not Axum, Tauri, or React types. |
| runtime-owned-roots | MUST | Config, state, and cache roots that contain mail authority data are owned by the runtime, not renderer storage. |
| renderer-storage-limited | MUST | Renderer-owned storage contains presentation/profile metadata only and no mail authority state, bodies, attachments, event history, mutation idempotency records, or provider secrets. |
| provider-secrets-runtime-store | MUST | Provider credentials and tokens live in the runtime secret store with only references/redacted status in config. |
| client-token-store-split | MUST | Non-bundled remote API tokens, when supported by the desktop client, live in a client secret store distinct from runtime provider secrets and are not inlined in profile JSON. |
| no-secret-logs-urls | MUST | Secrets, bearer tokens, capability URLs, Authorization headers, sensitive query strings, message bodies, and attachment bytes are not written to logs or diagnostics. |