feat(protocol): define gateway control envelopes
This commit is contained in:
@@ -29,4 +29,11 @@ Registered channels are `control.ack.v1`, `control.cancel.v1`, `clipboard.text.v
|
||||
application flow IDs 10, 11, and 12 and a path-MTU-safe payload limit of 1,179 bytes;
|
||||
larger encoded units use at most 16 validated fragments. Clipboard payloads are UTF-8
|
||||
JSON text contracts and remain subject to the 65,536-byte text limit and explicit
|
||||
authorization.
|
||||
Server-owned direction, rate, and loop-token policy as defined in
|
||||
`gateway-clipboard-v1.md`.
|
||||
|
||||
Within an active Phase 3C gateway session, `input.sequenced.v1` and the
|
||||
bidirectional reliable `control.ack.v1` payloads additionally use the exact
|
||||
provider-neutral grammars in [gateway-input-feedback-v1.md](gateway-input-feedback-v1.md).
|
||||
Those grammars do not alter this datagram header or make provider traffic visible to
|
||||
the Verse client.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Gateway clipboard text v1
|
||||
|
||||
`clipboard.text.v1` is a reliable, authenticated gateway-only `ChannelFrame`
|
||||
flow. Its UTF-8 JSON payload is a `GatewayClipboardText` object:
|
||||
|
||||
```json
|
||||
{"direction":"client_to_provider","text":"text","encoding":"utf-8","loop_token":"base64url-token"}
|
||||
```
|
||||
|
||||
`direction` is exact: the client may send only `client_to_provider`, and the
|
||||
gateway may send only `provider_to_client`. The text contains no file name,
|
||||
URL, binary value, or client-folder field and is at most the Server-owned
|
||||
`clipboard_policy.max_text_bytes` value. `loop_token` is a 16--128 ASCII
|
||||
base64url-character token generated by the originating endpoint. An endpoint MUST retain
|
||||
recent token/value pairs only for the bounded policy window and MUST suppress a
|
||||
matching reflected value; a mismatched, malformed, expired, or replayed token
|
||||
is rejected without clipboard mutation.
|
||||
|
||||
The gateway receives the policy only in authenticated session work. A disabled
|
||||
direction, a rate above `max_updates_per_minute`, invalid UTF-8, an oversized
|
||||
payload, or an unknown field fails closed. Clipboard bytes are never emitted to
|
||||
provider-state, audit, telemetry, or error payloads.
|
||||
|
||||
For every accepted, loop-suppressed, or policy/rate/provider/malformed rejection,
|
||||
the gateway sends an mTLS control-plane `GatewayClipboardAudit` record. It contains
|
||||
only the session identifier, direction, bounded text-byte count, outcome, and a
|
||||
fixed reason code; it contains neither text nor loop token. The Server persists it
|
||||
against the broker session using the authenticated gateway identity.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Gateway input and feedback v1
|
||||
|
||||
This grammar is carried only in an authenticated Phase 3C gateway session. It
|
||||
is deliberately provider-neutral: it never carries provider routes,
|
||||
certificates, credentials, opaque provider packets, clipboard bytes, files, or
|
||||
client-folder data. It does not change the v1 datagram header or any existing
|
||||
release candidate.
|
||||
|
||||
## `input.sequenced.v1` payload (`VGI1`)
|
||||
|
||||
All multibyte fields are unsigned big-endian. The payload has exactly six bytes
|
||||
of header followed by the declared body:
|
||||
|
||||
| Offset | Size | Field | Rule |
|
||||
|---:|---:|---|---|
|
||||
| 0 | 4 | magic | ASCII `VGI1` |
|
||||
| 4 | 1 | kind | one of the kinds below |
|
||||
| 5 | 1 | payload length | exact body byte count |
|
||||
| 6 | N | body | exact kind-specific body |
|
||||
|
||||
The decoder rejects an unknown kind, non-exact length, nonzero reserved byte,
|
||||
unsupported controller index, malformed UTF-8, a non-scalar UTF-8 value, or a
|
||||
payload larger than the channel limit before provider translation. A false
|
||||
keyboard or mouse state and a zeroed controller state are explicit releases;
|
||||
they are retained by the gateway and replayed as individual provider releases
|
||||
during cleanup.
|
||||
|
||||
| Kind | Name | Exact body |
|
||||
|---:|---|---|
|
||||
| `0x01` | keyboard | `state` (`0` release, `1` press), `modifiers` (one byte), nonzero `scancode` (u16). |
|
||||
| `0x02` | mouse button | `state` (`0` release, `1` press), `button` (`1` through `5`), reserved `0`. |
|
||||
| `0x03` | relative mouse | `delta_x` (i16), `delta_y` (i16). |
|
||||
| `0x04` | UTF-8 scalar | exactly one valid UTF-8 Unicode scalar, one through four bytes. |
|
||||
| `0x05` | controller state | `controller` (0 through 15), `active_mask` (u16), `button_flags` (u16), `left_trigger` (u8), `right_trigger` (u8), `left_x` (i16), `left_y` (i16), `right_x` (i16), `right_y` (i16), `extra_button_flags` (u16). A zero `active_mask` and zero state is release. |
|
||||
|
||||
Keyboard, mouse button, UTF-8, and controller messages are delivered over the
|
||||
gateway's reliable ordered input flow. Relative mouse is a state change, not a
|
||||
pressed-state entry. The gateway maps the validated values to the provider's
|
||||
separate keyboard, mouse, UTF-8, and controller control messages; it does not
|
||||
forward this envelope to the provider.
|
||||
|
||||
## Reliable control payload (`VGF1`)
|
||||
|
||||
`control.ack.v1` remains the existing authenticated bidirectional reliable
|
||||
control flow. Within an active gateway session, its provider-feedback payload
|
||||
is the following exact envelope:
|
||||
|
||||
| Offset | Size | Field | Rule |
|
||||
|---:|---:|---|---|
|
||||
| 0 | 4 | magic | ASCII `VGF1` |
|
||||
| 4 | 1 | direction | `0` client-to-gateway; `1` gateway-to-client |
|
||||
| 5 | 1 | type | valid only for the stated direction |
|
||||
| 6 | 2 | payload length | exact payload byte count |
|
||||
| 8 | N | payload | exact type-specific body |
|
||||
|
||||
The client-to-gateway types are `0x01` IDR request (empty) and `0x02` FEC
|
||||
status: `frame_index` (u32), `highest_received_sequence` (u16),
|
||||
`next_contiguous_sequence` (u16), `missing_before_highest` (u16),
|
||||
`total_data_packets` (u16), `total_parity_packets` (u16),
|
||||
`received_data_packets` (u16), `received_parity_packets` (u16),
|
||||
`fec_percentage` (u8), `multi_fec_block_index` (u8), and
|
||||
`multi_fec_block_count` (u8). The gateway maps this fixed 21-byte structure to
|
||||
the provider's unsequenced ENet FEC delivery; it does not put it on the reliable
|
||||
provider input path.
|
||||
|
||||
The gateway-to-client types are `0x10` host termination (`exit_code` u32),
|
||||
`0x11` rumble (`controller` u8, `low_frequency` u16,
|
||||
`high_frequency` u16), and `0x12` HDR mode (`enabled` exactly `0` or `1`). The
|
||||
gateway derives these from authenticated provider control messages, normalizes
|
||||
their bounded fields, and rejects all unrecognized provider feedback. The HDR
|
||||
envelope intentionally carries only the negotiated mode; provider-specific HDR
|
||||
metadata remains behind the gateway boundary.
|
||||
|
||||
Apollo's pinned `src/stream.cpp` source defines separate termination, rumble,
|
||||
and HDR control structures, while Moonlight common-C's `ControlStream.c` and
|
||||
`InputStream.c` separate reliable input/control from UDP media. This Verse
|
||||
grammar is a new normalized contract; it does not copy either implementation or
|
||||
expose its wire format.
|
||||
@@ -4,12 +4,12 @@
|
||||
"header_bytes": 21,
|
||||
"maximum_frame_bytes": 65536,
|
||||
"channels": [
|
||||
{"id": 1, "name": "control.ack.v1", "direction": "bidirectional", "max_payload_bytes": 1024},
|
||||
{"id": 1, "name": "control.ack.v1", "direction": "bidirectional", "max_payload_bytes": 1024, "payload_profile": "gateway-feedback-v1"},
|
||||
{"id": 2, "name": "control.cancel.v1", "direction": "client-to-server", "max_payload_bytes": 2048},
|
||||
{"id": 3, "name": "clipboard.text.v1", "direction": "bidirectional", "max_payload_bytes": 65515},
|
||||
{"id": 10, "name": "media.video.v1", "direction": "server-to-client", "max_payload_bytes": 1179},
|
||||
{"id": 11, "name": "media.audio.v1", "direction": "server-to-client", "max_payload_bytes": 1179},
|
||||
{"id": 12, "name": "input.sequenced.v1", "direction": "client-to-server", "max_payload_bytes": 1179}
|
||||
{"id": 12, "name": "input.sequenced.v1", "direction": "client-to-server", "max_payload_bytes": 1179, "payload_profile": "gateway-input-v1"}
|
||||
],
|
||||
"reserved_rejected": ["provider", "vm", "file-transfer", "clipboard.binary"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user