Add JSON-RPC 2.0 across the manager API, control plane and proplet - #274
Open
JeffMboya wants to merge 12 commits into
Open
Add JSON-RPC 2.0 across the manager API, control plane and proplet#274JeffMboya wants to merge 12 commits into
JeffMboya wants to merge 12 commits into
Conversation
Request and Response carry the 2.0 envelope with an id that accepts a string, a number or null, and rejects every other type. Presence of the id member is decoded separately from its value so a null id stays distinct from an absent one, which is what separates a request from a notification. Errors map the pkg/errors sentinels onto the standard code range plus a server-defined range for not found, conflict, validation and timeout.
The server keeps a method registry and turns a raw payload into a raw reply, so the same dispatcher serves both an HTTP body and an MQTT message without either transport knowing about the other. Batches are answered element by element, notifications are executed but produce no reply, and a batch made up entirely of notifications returns no payload at all. Decode failures report a stable message rather than the raw encoding/json error, which would otherwise leak internal type names to callers.
Method names and parameter shapes live next to the envelope rather than inside any one transport, so the HTTP endpoint, the MQTT control plane and the proplet all name an operation the same way. task.start means the same thing to a caller and to a proplet, which is what lets the manager forward a request instead of translating it.
Binds manager.Service onto the dispatcher so every task, proplet and job operation is reachable by method name. Parameters are validated before the service is touched, and service errors travel through the pkg/errors mapping so a missing task answers with the not found code rather than a generic failure. The binding is transport free: it returns a dispatcher that the HTTP endpoint and the MQTT control plane can both drive.
POST /rpc accepts a single call or a batch and sits behind the same middleware chain as the REST routes, so it inherits the existing auth context handling rather than opening a second unauthenticated door. Transport failures stay HTTP level: an unsupported content type is a 400 and a batch of notifications is a 204. Application failures stay protocol level, answering 200 with an error object, which is what a JSON-RPC client expects. Every existing REST route is untouched.
Start and stop commands can now travel as JSON-RPC requests carrying the task id as the correlation id, gated behind MANAGER_JSONRPC_CONTROL_PLANE and off by default so existing proplets keep receiving the flat payload they already parse. Inbound proplet messages are unwrapped before dispatch, so the manager accepts either encoding regardless of what it sends. Stop was published from four separate call sites with a hand built payload each time; they now share one helper, which is what keeps the two encodings from drifting apart.
Unwrapping happens in MqttMessage::decode, the one place every control message already passes through, so start, stop and every future handler accept both encodings without knowing which one arrived. A payload without a 2.0 version field is returned untouched, which is what keeps proplets working against a manager that has not been switched over. correlation_id exposes the envelope id so a handler can answer the request it was given rather than guessing from the task id.
A proplet now answers in the dialect it was addressed in: a command that arrived wrapped is answered with a Response carrying the same id, and one that arrived flat is answered with the bare ResultMessage as before. Mirroring the request rather than picking an encoding of its own is what lets a new proplet talk to an old manager and the reverse. Failures become an error object instead of a result with an error string beside it, so the failure is visible in the envelope rather than only in the body.
A deployed task registers under its function name, so a caller invokes it by that name rather than by task id and a proplet becomes an ordinary JSON-RPC service. Stopping a task removes its method. The server is off unless PROPLET_RPC_ENABLED is set, binds to loopback by default, and enforces a bearer token when PROPLET_RPC_TOKEN is present. Reaching a proplet directly bypasses the manager, where scheduling and authorization live, so it stays shut until an operator opens it and warns on startup when it is opened without a token. Control messages carrying a method that disagrees with their topic are now dropped rather than acted on.
make lint runs clippy with -D warnings, which turned this deprecation into a build failure for every branch, not just this one.
Call and CallBatch put the manager endpoint within reach of the SDK, and propeller-cli rpc call exposes it from a shell. Error responses come back as a jsonrpc.Error, so a caller can branch on the code instead of parsing a message. Request ids are drawn from a process wide counter, which keeps them distinct when a batch is in flight or two calls overlap.
The dispatch and endpoint tables hid their mock wiring behind a setup closure, so a case could not be read without following the function. Mock method, arguments and returns are now table fields wired inside the loop, matching the table style used in transport_test.go.
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What type of PR is this?
This is a feature because it adds JSON-RPC 2.0 support.
What does this do?
Adds JSON-RPC 2.0 to the manager API, control plane, proplet.
Which issue(s) does this PR fix/relate to?
None.
Have you included tests for your changes?
Yes. Go and Rust unit tests across all three surfaces.
Did you document any new/modified features?
No. Documentation belongs in propeller-docs.
Notes
Additive and off by default. Eleven commits build independently.