[v0.1/P1] Validate drained uniform payloads before dropping them #305

Closed
opened 2026-09-21 12:08:09 +09:00 by natsukium · 1 comment
Owner

Problem

ConnectionDriver::admit_drained has a fast path for uniform uncorrelated Grid, Image, and Push traffic. It checks kind columns, envelope status, and correlation, but deliberately does not decode the protobuf body before dropping it.

This contradicts the IPC contract that an undecodable body is corruption. A malformed or non-protobuf body can therefore be accepted and discarded when the receiving role does not consume that family, while the same frame would fail when decoded by a window client.

The current code comment also says the frame is untrustworthy but still returns Ok(()), which makes the actual corruption policy ambiguous.

Proposed resolution

Choose and document one policy before the wire freeze:

  1. Validate the body in the drain path even when the delivery is discarded; or
  2. Explicitly change the protocol contract so unread uniform payloads are not body-validated, including the consequences for cross-language clients.

The preferred option is a validation-only decode on the non-hot discard path, with tests for malformed protobuf, unknown oneof arms, and invalid row/image payloads.

Acceptance criteria

  • The implementation and docs/reference/ipc.md agree on whether drained payload bodies are validated.
  • Malformed drained payloads have a deterministic result and do not silently weaken the corruption contract.
  • Tests cover both a valid drained uniform frame and malformed/unknown-body variants.

Sources

  • crates/felis-transport/src/driver.rs (admit_drained)
  • docs/reference/ipc.md (corruption and frame validation)
  • crates/felis-transport/src/driver/tests.rs
## Problem `ConnectionDriver::admit_drained` has a fast path for uniform uncorrelated `Grid`, `Image`, and `Push` traffic. It checks kind columns, envelope status, and correlation, but deliberately does not decode the protobuf body before dropping it. This contradicts the IPC contract that an undecodable body is corruption. A malformed or non-protobuf body can therefore be accepted and discarded when the receiving role does not consume that family, while the same frame would fail when decoded by a window client. The current code comment also says the frame is untrustworthy but still returns `Ok(())`, which makes the actual corruption policy ambiguous. ## Proposed resolution Choose and document one policy before the wire freeze: 1. Validate the body in the drain path even when the delivery is discarded; or 2. Explicitly change the protocol contract so unread uniform payloads are not body-validated, including the consequences for cross-language clients. The preferred option is a validation-only decode on the non-hot discard path, with tests for malformed protobuf, unknown oneof arms, and invalid row/image payloads. ## Acceptance criteria - The implementation and `docs/reference/ipc.md` agree on whether drained payload bodies are validated. - Malformed drained payloads have a deterministic result and do not silently weaken the corruption contract. - Tests cover both a valid drained uniform frame and malformed/unknown-body variants. ## Sources - `crates/felis-transport/src/driver.rs` (`admit_drained`) - `docs/reference/ipc.md` (corruption and frame validation) - `crates/felis-transport/src/driver/tests.rs`
Author
Owner

Direction agreed 2026-09-21 after three sol design-review rounds (final verdict PASS). Full plan and batch order: see the comment on #12.

  • #305 — option 1 with an explicit boundary. The uniform drain path
    (Grid/Image/Push) runs a validation-only protobuf decode before discarding,
    so an undecodable body is corruption on every role. What the drain path
    cannot check is stated, not hidden: packed_cells is opaque to
    felis-protocol and felis-transport depends only on felis-protocol, so
    row-codec contents, coordinates relative to shadow dimensions, and image
    transfer sequencing are semantic invariants that only a consuming shadow can
    judge. docs/reference/ipc.md names the two tiers: wire-level validation
    (protobuf structure, unknown arms, stateless limits) on every path; semantic
    validation (ShadowError, image state) on consumers. The uniform fast-path
    decision in docs/explanation/architecture/ipc.md is rewritten in place.
    Tests cover malformed protobuf and unknown arms on the drain path, and a
    malformed opaque row is shown to be a consumer-tier failure.
Direction agreed 2026-09-21 after three `sol` design-review rounds (final verdict PASS). Full plan and batch order: see the comment on #12. - **#305** — option 1 with an explicit boundary. The uniform drain path (Grid/Image/Push) runs a validation-only protobuf decode before discarding, so an undecodable body is corruption on every role. What the drain path cannot check is stated, not hidden: `packed_cells` is opaque to `felis-protocol` and `felis-transport` depends only on `felis-protocol`, so row-codec contents, coordinates relative to shadow dimensions, and image transfer sequencing are semantic invariants that only a consuming shadow can judge. `docs/reference/ipc.md` names the two tiers: wire-level validation (protobuf structure, unknown arms, stateless limits) on every path; semantic validation (`ShadowError`, image state) on consumers. The uniform fast-path decision in `docs/explanation/architecture/ipc.md` is rewritten in place. Tests cover malformed protobuf and unknown arms on the drain path, and a malformed opaque row is shown to be a consumer-tier failure.
Commenting is not possible because the repository is archived.
No description provided.