Skip to content

Speak agent protocol v2 over UDS and gRPC - #5

Merged
raffaelschneider merged 2 commits into
mainfrom
fix/protocol-v2
Oct 22, 2026
Merged

raffaelschneider merged 2 commits into
mainfrom
fix/protocol-v2

Conversation

@raffaelschneider

Copy link
Copy Markdown
Contributor

Refs zentinelproxy/zentinel#600. Before this PR, the SDK couldn't complete a handshake with Zentinel on either transport.

It now passes every check of the shared conformance harness (zentinelproxy/zentinel/sdk-conformance, merged in zentinel#607) on UDS and gRPC, against a zentinel built from main: handshake, Configure (nested config acknowledged), request and response header mutations, Block, body inspection, and reconnect. CI runs the harness through sdk-conformance@main with zentinel-ref: main until a release includes zentinel#603. The README's "adds X-Hello and allows" agent passes the hello profile.

Agents built with this SDK could not connect to Zentinel: both UDS runners
used the v1 framing, and the gRPC server registered no service.

  • v2/uds.py: v2 framing (4-byte BE length incl. the type byte, type byte,
    JSON payload) and the message type codes.
  • UDS server: handshake answered with the full UdsHandshakeResponse schema
    (capabilities, features, limits, encoding); Configure (0x17) reaches
    on_configure and is acknowledged with allow, or block 500 on rejection,
    before the next message is read; Ping -> Pong; every AgentResponse carries
    its correlation id in audit.custom; events are handled concurrently.
  • gRPC: zentinel.agent.v2.AgentServiceV2 from the proxy's proto (generated
    code committed). ProcessStream sends its headers up front (the proxy waits
    for them before the handshake), applies ConfigureEvent in order and ends
    the stream on rejection, answers Ping with the same sequence, and maps
    headers, bodies and decisions.
  • One handler per runner, shared by every connection: the proxy may send a
    request's headers and body on different pooled connections. The request
    cache is bounded.
  • run_agent / AgentRunner now delegate to the v2 runner; the v1 framing
    helpers are removed from protocol.py.
  • examples/conformance_agent.py (the harness's conformance agent) and
    examples/hello_agent.py (the README agent); tests/integration runs them
    through the shared conformance harness, replacing the v1 mocks; CI gets a
    conformance job using zentinelproxy/zentinel/sdk-conformance.
  • Requires grpcio>=1.72.1 and protobuf>=6.30 (generated-code floors).
  • Version 0.3.0.

Agents built with this SDK could not connect to Zentinel: both UDS runners
used the v1 framing, and the gRPC server registered no service.

- v2/uds.py: v2 framing (4-byte BE length incl. the type byte, type byte,
  JSON payload) and the message type codes.
- UDS server: handshake answered with the full UdsHandshakeResponse schema
  (capabilities, features, limits, encoding); Configure (0x17) reaches
  on_configure and is acknowledged with allow, or block 500 on rejection,
  before the next message is read; Ping -> Pong; every AgentResponse carries
  its correlation id in audit.custom; events are handled concurrently.
- gRPC: zentinel.agent.v2.AgentServiceV2 from the proxy's proto (generated
  code committed). ProcessStream sends its headers up front (the proxy waits
  for them before the handshake), applies ConfigureEvent in order and ends
  the stream on rejection, answers Ping with the same sequence, and maps
  headers, bodies and decisions.
- One handler per runner, shared by every connection: the proxy may send a
  request's headers and body on different pooled connections. The request
  cache is bounded.
- run_agent / AgentRunner now delegate to the v2 runner; the v1 framing
  helpers are removed from protocol.py.
- examples/conformance_agent.py (the harness's conformance agent) and
  examples/hello_agent.py (the README agent); tests/integration runs them
  through the shared conformance harness, replacing the v1 mocks; CI gets a
  conformance job using zentinelproxy/zentinel/sdk-conformance.
- Requires grpcio>=1.72.1 and protobuf>=6.30 (generated-code floors).
- Version 0.3.0.
@raffaelschneider
raffaelschneider merged commit a7a33af into main Oct 22, 2026
4 checks passed
@raffaelschneider
raffaelschneider deleted the fix/protocol-v2 branch October 22, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant