Skip to content

feat: complete OCPP 2.1 transport — SEND, CALLRESULTERROR, WebSocket compression, JWS signed messages - #9

Merged
shiv3 merged 24 commits into
mainfrom
feat/ocpp21-send-callresulterror
Jun 18, 2026
Merged

feat: complete OCPP 2.1 transport — SEND, CALLRESULTERROR, WebSocket compression, JWS signed messages#9
shiv3 merged 24 commits into
mainfrom
feat/ocpp21-send-callresulterror

Conversation

@shiv3

@shiv3 shiv3 commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Summary

Completes OCPP 2.1 OCPP-J transport support. A full audit against the OCPP 2.1
spec (Parts 0/1/2/4 + appendices) confirmed the message set was already complete
(91/91 messages) but three transport-layer capabilities were missing. This PR
adds all three:

  1. SEND (MessageTypeId 6) & CALLRESULTERROR (MessageTypeId 5) — the two
    message types introduced in OCPP 2.1 Part 4.
  2. WebSocket compression (RFC 7692) — Part 4 §3.4 ("Required" for CSMS).
  3. Signed Messages (JWS) — Part 4 Chapter 7, for the action-bearing
    CALL/SEND messages.

What's included

SEND & CALLRESULTERROR

  • core/ocppj: parse/encode for types 5 & 6; unknown message types ignored per §4.4.
  • core/dispatcher: inbound routing, no-response SEND handler dispatch (with
    panic recovery), DoSend fire-and-forget, and active CALLRESULTERROR on an
    invalid inbound CALLRESULT (2.1 only).
  • Typed API: ocppj.SendMessage, cp.Send/OnSend, csms.Send/OnSend.
  • Codegen: request-only SEND messages; generated NotifyPeriodicEventStream.

WebSocket compression (RFC 7692)

  • transport.CompressionMode enum; enabled by default (NoContextTakeover) on
    both CP and CSMS, negotiated automatically and degrading gracefully.
  • cp.WithCompression / csms.WithCompression options.
  • E2E test asserts permessage-deflate is negotiated on the upgrade response.

Signed Messages (JWS)

  • core/ocppj/signing: Signer/Verifier/UnwrapPayload using
    go-jose/go-jose/v4; Flattened JWS, ES256/RS256/RS384 only, x5t#S256 plus
    OCPPAction/OCPPMessageTypeId protected headers.
  • Framing: [type, id, "<Action>-Signed", {JWS}].
  • Dispatcher: outbound signing, inbound verify/unwrap (§7.2),
    RequireSignatureVerification.
  • Typed API: cp/csms WithSigner/WithVerifier/WithRequireSignature.

Spec decisions (documented in code)

  • Signed-message wire format follows §4.2 (a 4-element CALL); the §7.1
    {Extension} element is omitted because it contradicts §4.2.1 ("a CALL always
    consists of 4 elements"). Isolated so a future OCA erratum is a small change.
  • Signing scope: wire-integrated for action-bearing CALL/SEND only.
    Action-less replies (CALLRESULT/CALLERROR/CALLRESULTERROR) are out of
    scope (spec-undefined); the signing primitives are public for manual use.

Testing

  • ~60 new tests across core/ocppj, core/dispatcher, cp/csms,
    core/ocppj/signing, and internal/e2e (TDD throughout, including spec-aligned
    rejection cases: tampered payload, wrong key, disallowed alg, header mismatch).
  • go build ./..., go vet ./..., and go test ./... all pass.

Dependencies

  • Promotes github.com/go-jose/go-jose/v4 to a direct dependency (it was already
    present in the module graph as a transitive dependency).

🤖 Generated with Claude Code

shiv3 added 24 commits June 18, 2026 20:10
The typed handler interfaces + RegisterCSMS/RegisterCP are the recommended way
to register handlers, so make the README quickstart and the CSMS examples use
them: embed UnimplementedCSMSHandler and implement only the messages you handle.

- README CSMS quickstart uses v16h.RegisterCSMS; csms.On kept as a documented
  single-handler/dynamic alternative.
- csms-minimal, migration-after: small handler structs.
- csms-full: *app implements CSMSHandler (embeds UnimplementedCSMSHandler);
  the per-message closures become methods registered in one RegisterCSMS call.
@shiv3
shiv3 merged commit ef462be into main Jun 18, 2026
5 checks passed
@shiv3
shiv3 deleted the feat/ocpp21-send-callresulterror branch June 18, 2026 15:22
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