Skip to main content

Durable delivery

Scope and identity

A durable command has a caller-owned command ID, one logical operation, its recipient event copies, and delivery intents for the relevant relays. These identities must not be confused with a socket write or a recipient read receipt. This contract covers shared machinery used by the text-message contract. Ephemeral typing and upload-dependent media have additional, separate rules.

Supported behavior

Commit before effect

shared.delivery.commit-before-effect — Before a newly created message is handed to transport, its local state, outgoing operation and required recipient copies must be committed. Transmission must not run inside the SQLite transaction. The caller retains the command ID across uncertain results; the owning operation contract defines its replay and conflict behavior.

Relay acceptance

shared.delivery.relay-acceptance — A successful socket write must not mark an event accepted. Acceptance requires a correlated relay result. A terminal acknowledgement that races the socket-write completion remains authoritative; repeating that completion must not undo it. Aggregate completion requires all required delivery intents to be accepted.

Relay acceptance is evidence about the relay. It does not prove that the other person's device received, displayed or read the message. Application labels must preserve this distinction.

Delivery recovery

shared.delivery.recovery — Reopening an account must retain unfinished operations and permit recovery of expired delivery leases. A reconnect must reclaim eligible work without multiplying an already-owned delivery claim. Terminal outcomes and retryable work remain distinguishable.

A retryable relay rejection commits its outcome before recovery. Reconnecting must retain accepted intents and reclaim only unfinished work, rather than republishing the same ID into an incompatible in-memory delivery state. Synchronization resend frames obey both the relay event-count and serialized byte bounds; one oversized aggregate must not create a repeating reconnect failure for otherwise valid stored events. Incoming traffic does not extend the delivery lease: processed results and periodic wakes check the original deadline. A terminal acknowledgement handled at that deadline takes effect before timeout recovery.

New account events reserve space for the minimum EVENT publish envelope. Previously stored events that exceed this wire budget retain their original signed history, but are excluded from synchronization fingerprints. Their unfinished delivery intents become locally rejected with event_too_large; accepted and rejected relay history remains unchanged. Cleanup advances the account revision even when no new delivery can be claimed.

Persistence semantics

The durable operation, recipient copies, per-relay intent states and lease correlation live in Rust-owned account storage. A transport generation or socket can end without erasing that intent. See the relay-session architecture for frame, queue and correlation rules and the account runtime for the storage implementation.

Observable behavior

The operation result exposes committed local state; transport snapshots expose its progress and failures. Android renders these states and delegates retry semantics to Rust. It must not infer acceptance from network connectivity, absence of an exception, or a local message row.

Verification and limits

The Kotlin account-command witness checks that authored facade results expose the committed operation before a transport action. The host runner reconciles compiled test annotations with JUnit results; an annotated method silently excluded by JUnit cannot count as executed evidence.

Mapped cases cover durable enqueue/claim/accept transitions, socket-write versus acceptance, complete intent aggregation, acknowledgement races, reopen recovery and reconnect claims. They are focused state-machine and integration scenarios. Systematic process termination at every transaction/socket boundary, arbitrary network fault sequences, and every live-relay deployment are not established by this first contract set.

No execution report is bundled with this documentation. Linked test cases describe verification scope; their existence is not a passing result.

InvariantRequired environmentsExecution evidence
shared.delivery.commit-before-effect
rust-core
rust-host, android-jvmmissing
3 mapped test cases
  • storage::tests::enqueue_claim_and_accept_are_durable_transitionssemantic; sdk, cargo:softchat
  • com.softcose.softchat.sdk.AccountRuntimeTest#account commands commit before sockets and expose complete product viewsfacade; sdk, gradle:library:test
  • runtime::tests::account_events_reserve_their_publish_frame_before_acceptancesemantic; sdk, cargo:softchat
Shared requirements
shared.delivery.relay-acceptance
rust-core
rust-hostmissing
3 mapped test cases
  • runtime::tests::delivery_claim_and_results_never_treat_socket_write_as_acceptancesemantic; sdk, cargo:softchat
  • runtime::tests::operation_state_requires_every_relay_intent_to_acceptsemantic; sdk, cargo:softchat
  • session::tests::acknowledgement_may_race_socket_handoff_and_confirmation_is_idempotentsemantic; sdk, cargo:softchat
shared.delivery.recovery
rust-core
rust-hostmissing
8 mapped test cases
  • storage::tests::account_reopen_recovers_views_and_expired_delivery_leasessemantic; sdk, cargo:softchat
  • runtime_facade::product_tests::account_transport_reclaims_a_delivery_once_after_reconnectsemantic; sdk, cargo:softchat
  • account_transport::tests::transport_retryable_ack_preserves_terminal_outcomes_and_reclaims_oncesemantic; sdk, cargo:softchat
  • account_transport::tests::transport_sync_resends_fit_frame_bytes_and_keep_every_eventsemantic; sdk, cargo:softchat
  • account_transport::tests::transport_delivery_expiry_is_not_postponed_by_inbound_trafficsemantic; sdk, cargo:softchat
  • storage::tests::legacy_wire_oversized_events_do_not_hide_sync_fingerprintssemantic; sdk, cargo:softchat
  • storage::tests::legacy_wire_oversized_deliveries_fail_locally_without_starving_valid_worksemantic; sdk, cargo:softchat
  • storage::tests::delivery_expiration_on_another_relay_advances_revision_without_a_new_claimsemantic; sdk, cargo:softchat
Shared requirements