# Initial managed API contract Status: initial port slice, API revision 1. No change to protobuf or media wire formats. These are shared infrastructure APIs; the client-facing API follows with the client core. ## Protocol `VoiceCat.Protocol` generates `Voicecat.V1` protobuf messages from the existing schema. `ControlFraming.TryReadFrame(ref ReadOnlySequence, out ReadOnlySequence)` extracts a payload and advances input only when a full frame exists. Returned memory borrows the input's lifetime. Lengths above 16 MiB throw `InvalidDataException`. Empty payloads are valid. `WriteFrame` and `WriteEnvelope` target `IBufferWriter`; oversized outgoing payloads throw before output is written. `ReadEnvelopesAsync(PipeReader, CancellationToken)` produces parsed envelopes and advances consumed pipe data. It does not complete or dispose the caller's reader. Clean EOF ends enumeration; partial EOF and oversized frames throw `InvalidDataException`; malformed protobuf throws `InvalidProtocolBufferException`. Cancellation propagates. A connection owner must close on protocol errors or cancellation partway through a frame; partial frame bytes may already be consumed. Fragments are consumed as they arrive so frames larger than pipe backpressure thresholds make progress. Stopping enumeration between envelopes preserves the next frame. `VoiceFrameHeader` is an immutable value with type, flags, codec, SSRC, sequence, and timestamp. `Write(Span)` writes its 20-byte big-endian representation; `TryRead` accepts at least 20 bytes and preserves unknown type/flag/codec values. Higher layers decide which values they support. ## Media encryption `MediaEncryptor` and `MediaDecryptor` each own one directional 32-byte session key and mutable packet state. Use one owner at a time; they provide no synchronization. Production constructs them from TLS exporter keys when TLS is implemented. Raw-key constructors support conformance tests and the future TLS integration. `MediaEncryptor.Encrypt(VoiceFrameHeader, ReadOnlySpan, Span)` writes the full header plus ciphertext and 16-byte tag and returns packet length. It replaces the supplied sequence with its own counter, starting at zero. Capacity and overlap errors throw before reserving a counter. Reserved counters are never reused after encryption failure. At `ulong.MaxValue`, encryption throws and requires a new session. `MediaDecryptor.TryDecrypt(ReadOnlySpan, Span, out VoiceFrameHeader, out int)` authenticates and decrypts a complete packet. Short packets, failed tags, replays, and packets outside the 64-packet window return false with default header and zero bytes written. Authentication failure clears the attempted plaintext region; structural/replay rejection leaves storage untouched. Callers must only consume output after success. Invalid storage capacity and overlapping buffers throw. The nonce is four zero bytes plus the big-endian header counter. All 20 header bytes are authenticated associated data. The replay window advances after authentication. The platform ChaCha20-Poly1305 implementation is preferred; BouncyCastle is used when platform support is absent. Both produce the same wire bytes. The fallback currently allocates per packet; audio and relay allocation guarantees are later checkpoints. Dispose both objects to clear their owned key arrays and release platform crypto resources. Use after disposal throws `ObjectDisposedException`.