fix(gateway): secure control and terminal ownership

This commit is contained in:
sechmachine
2026-07-30 11:15:48 +07:00
parent df75b1d250
commit baf4073f68
16 changed files with 488 additions and 36 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-07-30
@@ -0,0 +1,29 @@
## Context
The native Apollo session and production QUIC gateway already quiesce media before terminal delivery. A public independent-client test proved that immediate `CloseWithError` can overtake the queued reliable stream frame, while waiting for client connection closure leaves tunnel ownership unbounded.
## Goals / Non-Goals
**Goals:**
- Deliver one terminal event before gateway-owned closure.
- Bound closure when a client remains open or omits the receipt.
- Preserve cleanup, input release, reservation, and durable state behavior.
**Non-Goals:**
- A generic acknowledgement or lifecycle framework.
- Any Apollo protocol, media, Server authority, or dependency change.
## Decisions
- Reuse Protocol `control.ack.v1` type `0x03` as an empty terminal receipt.
- Hold the receipt-state lock across the terminal write, arm one receipt slot only after a successful write, and consume it in the gateway rather than provider feedback.
- Wait at most two seconds for receipt, then close and clean up regardless.
- Serialize the bounded native event queue and evict one older feedback item only when necessary to retain a terminal event.
## Risks / Trade-offs
- [Client omits receipt] → Close at the two-second bound and retain durable cleanup behavior.
- [Feedback queue is saturated] → Sacrifice one older nonterminal feedback event rather than lose terminal ownership.
- [Receipt is malformed, duplicate, or early] → Fail the session closed without provider mutation.
@@ -0,0 +1,24 @@
## Why
P3C-018, P3C-019, P3C-021, and P3C-027 require terminal feedback, bounded cleanup, durable state, and explicit input release. A public independent-client regression proved that immediate QUIC closure loses the terminal event, while the old behavior left the tunnel open until the client closed it.
## What Changes
- Consume the Protocol-owned terminal receipt on `control.ack.v1` inside the gateway rather than forwarding it to Apollo.
- Quiesce media before terminal delivery and close the gateway-owned tunnel after receipt or a bounded receipt deadline.
- Guarantee a terminal event survives saturation of the bounded native feedback queue.
- Preserve provider cleanup, reservation release, reconnectable disconnect reporting, and `cleanup_pending`.
## Capabilities
### New Capabilities
None.
### Modified Capabilities
- `provider-session-lifecycle`: Make terminal delivery and gateway-owned bounded closure executable rather than dependent on client connection closure.
## Impact
The pure-Go GPLv3 gateway control and native Apollo session paths change. The Protocol repository remains the wire-contract owner; the Server remains the durable authority. No cgo, sidecar, direct provider route, decode/transcode path, dependency, or proprietary source is introduced. Failure to receive a valid receipt before the bound is a hard session close, not a fallback.
@@ -0,0 +1,23 @@
## MODIFIED Requirements
### Requirement: Provider terminal events end forwarding
Encrypted provider termination and unexpected provider disconnect SHALL quiesce provider ingestion and queued/new media forwarding before the existing reliable typed terminal event is delivered. The client SHALL return the Protocol-owned terminal receipt after decoding that event. The gateway SHALL close the Verse tunnel after that receipt or a bounded receipt deadline even when the client keeps the connection open, release the session reservation, and report the appropriate durable provider/session state. The receipt MUST be consumed by the gateway and MUST NOT be forwarded to the provider. A fixed drain delay MUST NOT stand in for reliable control delivery.
#### Scenario: Host termination closes the tunnel
- **WHEN** the native provider emits an authenticated termination event and an independent client returns its terminal receipt
- **THEN** queued and newly injected media cannot cross the Verse transport after observation, and the gateway closes the tunnel, releases the reservation, and completes the durable lifecycle transition
#### Scenario: Unexpected provider disconnect is reconnectable
- **WHEN** required provider transport disconnects without acknowledged provider termination
- **THEN** forwarding stops, the final typed disconnect reaches the client, and the Server receives the existing reconnectable lifecycle state rather than a termination claim
#### Scenario: Client omits terminal receipt
- **WHEN** the terminal event is written but the client remains open without returning a valid receipt
- **THEN** the gateway closes the tunnel at the bounded receipt deadline and continues cleanup without restoring media forwarding
### Requirement: Cleanup failure remains durable
Gateway cleanup MUST preserve `cleanup_pending` when provider input release, transport cleanup, authorized cancellation, or durable reporting fails.
#### Scenario: Terminal cleanup fails
- **WHEN** a provider terminal event is handled but required cleanup cannot complete
- **THEN** the session is not reported reusable and durable state remains cleanup pending
@@ -0,0 +1,16 @@
## 1. Regressions
- [x] 1.1 Reproduce terminal event loss with an independent QUIC client and immediate gateway closure
- [x] 1.2 Reproduce terminal loss behind a saturated native feedback queue
## 2. Lifecycle repair
- [x] 2.1 Consume the scoped terminal receipt without provider forwarding
- [x] 2.2 Close acknowledged and non-acknowledged terminal tunnels within bounds
- [x] 2.3 Preserve media quiescence, reservation release, durable state, and cleanup-pending
## 3. Verification
- [x] 3.1 Pin the final immutable Protocol version and pass focused lifecycle/race/resource checks
- [ ] 3.2 Pass complete Data Plane verification and the frozen normative Section 7 qualification
- [ ] 3.3 Record that deterministic fixtures do not prove live Apollo, macOS-client, or physical-firewall interoperability
@@ -1,22 +1,26 @@
# provider-session-lifecycle Specification
## Purpose
TBD - created by archiving change phase3c-gateway-audit-remediation. Update Purpose after archive.
Define terminal media quiescence, bounded Verse tunnel ownership, provider cleanup, reservation release, and durable lifecycle outcomes.
## Requirements
### Requirement: Provider terminal events end forwarding
Encrypted provider termination and unexpected provider disconnect SHALL quiesce provider ingestion and queued/new media forwarding before the existing reliable typed terminal event is delivered, close the Verse tunnel within a bounded interval, release the session reservation, and report the appropriate durable provider/session state. A fixed drain delay MUST NOT stand in for reliable control delivery.
Encrypted provider termination and unexpected provider disconnect SHALL quiesce provider ingestion and queued/new media forwarding before the existing reliable typed terminal event is delivered. The client SHALL return the Protocol-owned terminal receipt after decoding that event. The gateway SHALL close the Verse tunnel after that receipt or a bounded receipt deadline even when the client keeps the connection open, release the session reservation, and report the appropriate durable provider/session state. The receipt MUST be consumed by the gateway and MUST NOT be forwarded to the provider. A fixed drain delay MUST NOT stand in for reliable control delivery.
#### Scenario: Host termination closes the tunnel
- **WHEN** the native provider emits an authenticated termination event
- **THEN** queued and newly injected media cannot cross the Verse transport after observation, and the client tunnel, reservation, and durable lifecycle transition complete
- **WHEN** the native provider emits an authenticated termination event and an independent client returns its terminal receipt
- **THEN** queued and newly injected media cannot cross the Verse transport after observation, and the gateway closes the tunnel, releases the reservation, and completes the durable lifecycle transition
#### Scenario: Unexpected provider disconnect is reconnectable
- **WHEN** required provider transport disconnects without acknowledged termination
- **THEN** forwarding stops and the Server receives the existing reconnectable lifecycle state rather than a termination claim
- **WHEN** required provider transport disconnects without acknowledged provider termination
- **THEN** forwarding stops, the final typed disconnect reaches the client, and the Server receives the existing reconnectable lifecycle state rather than a termination claim
#### Scenario: Client omits terminal receipt
- **WHEN** the terminal event is written but the client remains open without returning a valid receipt
- **THEN** the gateway closes the tunnel at the bounded receipt deadline and continues cleanup without restoring media forwarding
### Requirement: Cleanup failure remains durable
Gateway cleanup MUST preserve `cleanup_pending` when provider input release, transport cleanup, authorized cancellation, or durable reporting fails.
#### Scenario: Terminal cleanup fails
- **WHEN** a provider terminal event is handled but required cleanup cannot complete
- **THEN** the session is not reported released or reusable and durable state remains cleanup pending
- **THEN** the session is not reported reusable and durable state remains cleanup pending