# Communication key lifetimes

Document: SPEC-016. Revision: 0.5. Date: 2026-10-27. Lifecycle: voice implementation profile under D-081/D-082; text protocol selection remains proposed. Revision 0.3 references the D-084 Opus envelope without changing key lifetime or overlap. Revision 0.2 clarifies recovery from concurrent revision changes during initial joining as well as renewal. Supersedes SPEC-003/004/009 static-device-key encryption for new E2EE voice calls supporting `voice-keys/1`. Text encryption, device identity, recovery, retention and server-readable calls retain their existing contracts. D-172–D-180 scope is accepted only as specified below; SPEC-013 records the approved one-message history exception.

## Approved video-delivery successor — D-167

D-167/SPEC-024 keeps five-minute key expiry, staged replacement, revocation exclusion and no expired-key fallback. Presentation readiness is per recipient: an unready listener waits without blocking other authorized receivers. Cryptographic recipient epochs must follow effective membership/device/security changes rather than cosmetic state revisions in the managed successor. Current native revision-bound keys remain compatible; no nonce, key lifetime or excluded-recipient rule is relaxed. See [SPEC-024](024-presentation-delivery.md).


## Direct voice and relay fan-out (D-086)

D-086 and [SPEC-017](017-voice-routing.md) supersede per-recipient audio encryption for `voice-fanout/1`. Existing ephemeral pairs now protect temporary sender-key distribution. Their signed offers, activation, five-minute lifetime, two-second receive-only overlap and no-overlap revocation rules remain authoritative. Sender secrets and acknowledgments are bound to these offers; failed distribution cannot extend an expired key.

## Accepted scope

D-081 separates device identity, communication-key rotation and history retention. Preserve readable history within SPEC-006's seven-day retention, existing device linking and password-plus-recovery-key restoration. Implement temporary voice keys first and evaluate a standard ratcheting text protocol. Expiring encryption keys does not authorize disappearing messages or deletion of retained text keys.

D-082 selects fresh voice keys on call membership changes, at most five minutes of use, and at most two seconds of old-key overlap for delayed packets. Failed renewal pauses encrypted audio rather than extending expired keys. Successful periodic renewal is silent. Existing PTT clearing on membership/policy changes, one active call, channel/thread isolation, foreign approval checks and forwarding deadlines remain authoritative.

## Voice profile

Use the existing pinned libsodium dependency: fresh X25519 `crypto_kx_keypair`, directional `crypto_kx_*_session_keys`, and XChaCha20-Poly1305 AEAD with random 24-byte nonces. Authenticate each ephemeral public offer with the existing approved device's Ed25519 signing key. This is a Sloppy Voice application profile using documented library primitives, not a claim of Noise/MLS interoperability or an independent security audit. Never wrap a media key with the long-lived device or recovery encryption key.

An offer contains exactly `profile: voice-keys/1`, persistent destination `serverId`, `room`, room `revision`, qualified account `domain` and `account`, device UUID `device`, attendance UUID `session`, fresh UUID `id`, monotonically increasing integer `generation`, X25519 `publicKey` and `signature`. The signature covers canonical JSON without `signature`. The first offer is generation 1; retries preserve its exact bytes. New offers use a greater generation and different key/ID. Offers are scoped to the actual thread room for thread calls.

The authenticated `/voice/lease` request may carry `keyOffer` beside `voiceId`. Only the current approved device owning unexpired attendance can publish, with the current room revision, matching identity and valid signature. The server retains only public offers and deadlines on the existing attendance record. Same-offer retries cannot extend deadlines. Lower generations, substituted same-generation offers, wrong context and unapproved keys fail. Membership/policy revision changes invalidate offers immediately without resetting the attendance generation counter.

A concurrent revision change can invalidate a key offer without revoking attendance. On `voice_key` rejection of a publication, refresh the existing authenticated attendance without an offer, apply the returned context and publish its fresh key through the ordinary renewal path. This applies to the first publication during joining as well as later renewals. It does not authorize another join, extend a key deadline, bypass membership/device validation, or select plaintext/static keys. Audio stays gated until the current keys and forwarding authority are ready. Other errors retain their existing failure behavior.

Lease state advertises `voiceKeys: {profile: voice-keys/1}` for encrypted rooms. Each participant's `voice.keys` contains at most two public grants `{offer, validForMS, activateInMS}`. These are remaining durations, computed by the server, never client-selected lifetimes. The client anchors them at the start of its own lease request, subtracting network time. Other room polling cannot extend key authority. An identical grant can shorten but never extend an already observed deadline. Clock rollback or a resumed context beyond its local hard deadline clears media-key state.

Publish a replacement ten seconds before its local hard expiry. The server stages a periodic replacement five seconds before activation so other participants can fetch it through their existing one-second lease loop. Initial and revision-change offers activate immediately. Receivers may establish the staged pair before activation; senders keep using their current pair until activation. Keep one predecessor only for receiving, for at most two seconds after replacement and never beyond its original expiry plus the overlap. No overlap survives membership/policy changes, leave, revoked trust or an invalid forwarding lease. Stop sending at five minutes even if a host replays/extends a grant. Keep secrets only in client memory and overwrite owned mutable buffers on retirement; do not persist/export them through the vault, history, recovery or diagnostics. Managed runtimes cannot promise forensic erasure of every engine/OS copy.

Each peer pair orders its ephemeral public keys lexicographically to select libsodium's client/server roles; both public keys must differ. Bind the full two signed offers, sender/target devices and attendance IDs, destination, room, revision and packet sequence into authenticated additional data. Transmit a `voice-keys/1` wrapper with sender/recipient key IDs and AEAD ciphertext inside the existing base64 `ciphertext` field. The existing relay's nonce/packet/lease/revision bounds remain unchanged; it never receives secrets. Reject the old static-box format on this profile. Existing replay windows and playout limits remain in force across periodic rotation.

Require `voice-keys/1` in the client's capability header for encrypted voice admission/renewal. An updated client rejects an encrypted call on a host lacking the state capability; neither side silently selects static keys or plaintext. D-084 additionally requires the Opus capability in both encryption modes (SPEC-009). The voice-key wrapper encrypts the complete Opus envelope without changing key grants or lifetimes. Server-readable media uses the updated signed audio envelope. Text and existing account sessions stay compatible. An old encrypted call must rejoin with updated client assets after activation.

## Text and recovery evaluation

MLS (RFC 9420) is the preferred candidate for group text evaluation; selection of a library or migration is not yet approved. Compare a browser integration of OpenMLS with ts-mls, and compare pairwise Double Ratchet sessions for the small alpha device set. Measure browser/desktop support, version interoperability, crash-safe state changes, multi-tab serialization, concurrent commits, offline catch-up, device removal, foreign approval and bounded storage before selecting an implementation.

Preserving an encrypted archive/recovery copy preserves an independent route to that content. Transport forward secrecy cannot be advertised as protection from compromise of that archive or recovery secret. A server password check is not an extra cryptographic factor for previously copied recovery ciphertext. Future archive keys and rotation must preserve membership intervals, original expiration, signatures/provenance and existing readable history. No automatic recovery-code expiry or replacement is selected. Historical signatures retain their public verification evidence. Standard protocol evaluation is recorded in the [research review](reviews/2026-10-20-key-lifetimes.md).

## Held-input continuity on additions (D-177)

D-177 supersedes the requirement to release/repress PTT solely because a new voice participant is added. Preserve the existing speaker's held intent and original PTT duration deadline while preparing authenticated current-context keys; send only when current forwarding/key authority is valid. A participant addition must not authorize old-context packets for the new listener. Removals, device/key revocation, moderator changes, policy changes, leave and focus/lock still stop or fence transmission as specified. Periodic key lifetime and overlap bounds remain unchanged. The [increment review](reviews/2026-10-27-public-usability-operations.md) tracks the continuity regression and delivery status.

## Configuration

These are fixed voice protocol rules, with no deployment, administrator or user override. No new persisted secret setting is introduced. Shared membership, approval, media leases and history settings remain in their owning specifications.

| Property | Type, units and value | Scope and validation |
| --- | --- | --- |
| `VOICE_KEYS.lifetimeMS` | Integer milliseconds, 300000; confirmed D-082 | Per ephemeral generation; local creation and server acceptance each bound use; neither retries nor refresh extend it. |
| `VOICE_KEYS.overlapMS` | Integer milliseconds, 2000 maximum; confirmed D-082 | One previous generation; revoked context has zero overlap. |
| `VOICE_KEYS.renewalLeadMS` | Integer milliseconds, 10000 | Fixed engineering bound within the selected lifetime; allows publication before hard expiry. |
| `VOICE_KEYS.activationLeadMS` | Integer milliseconds, 5000 | Fixed engineering bound for periodic replacement; initial/context replacement is immediate. |
| Public offers | Maximum two per active attendance | Current and staged successor; no private key is stored at the server. |
| Generation | Positive safe integer | Per attendance; strictly increasing for replacements; exact retry is idempotent. Exhaustion requires a new call. |
| Text key lifetime | OPEN — not selected | No change to current text/recovery encryption while the standard protocol is evaluated. |

## Testing

D-177 controller/browser fixtures preserve shortcut/manual held intent across an additive participant revision while fresh keys are pending, resume without repress after readiness, retain the original duration deadline, and stop on release/removal/moderation. Existing key/lease expiry and old-context rejection units remain authoritative.

SPEC-025's transitional managed trial copies currently valid native ephemeral sender-media material into memory-only transform workers. It cannot extend an original key deadline or use a removed recipient's context. Increasing native revision replaces the authenticated transform context; older frames fail verification. Additional native cosmetic rekeys remain a documented trial limitation, not completion of SPEC-024's independent managed recipient epochs. Static identity-box secrets and recovery seeds remain excluded.

Unit-test directional agreement, signature/key/context substitution, malformed/low-order keys, distinct keys across calls/revisions/renewals, old/new overlap boundaries, expiry during a held press, replay, clock changes, delayed responses, pending replacement failure, and destruction of owned private/derived buffers. Demonstrate that static device/recovery secrets cannot derive the new media secret from a recorded public exchange. This is a regression property, not a cryptographic proof or audit.

Server tests cover admission capability, signer/current-device checks, revision races, idempotent publication, generation rollback, bounded public storage, deadlines, and foreign approval loss. Browser integration exercises actual encrypted audio, periodic rotation without a new join, failed-renewal pause, membership rekey/fresh PTT, channel/thread isolation and server-readable calls. Run existing conversation, linked-device/recovery and foreign-call regressions. Physical listening and platform acceptance remain distinct from automated evidence.

Client regression coverage must force the room revision to change between initial attendance and key publication: recover with the same attendance, publish for the new revision, and retain the fresh-press requirement. A denied membership response must still close the attempted call. The disposable hosted fixture repeats an actual authority revision race against the Cloudflare control service and native relay.

## Acceptance criteria

- Adding a voice participant preserves held PTT intent and its original duration deadline; transmission resumes only under valid current-context keys. Release, removal and moderation still stop it.

- A valid initial encrypted join recovers a concurrent key-context rejection without rejoining or transmitting before fresh keys are ready; revoked membership still prevents connection.
- E2EE voice uses temporary authenticated keys, never static device or recovery encryption keys, and rejects a legacy encrypted-media fallback.
- Five-minute expiry and the two-second receive-only overlap hold under fake clocks, delayed requests and repeated grants; forbidden membership/revision contexts have no overlap.
- Successful renewal preserves an ongoing permitted talkspurt; expiry or failed authority stops capture/publication and requires a fresh press after recovery.
- No ephemeral secret appears in persistent storage, host records, history copies, recovery payloads or logs; owned secret buffers are cleared on retirement/leave.
- Joining/leaving/revocation rekeys the remaining authorized call participants, while text selection and another thread's voice keys remain isolated.
- Existing text, seven-day retention, linked-device history and recovery remain readable and pass their regression suites.
- Text protocol/library choices and their unvalidated integration requirements remain explicit; no ratcheting-text or audited-security claim precedes supporting evidence.
