Conversation
Add HTTP and Unix-domain-socket transports for the shared Admin JSON-RPC API, with an attach console for trusted node operators. Include transport configuration, socket permissions, request handling, bounded client lifecycle, and startup isolation regression tests.
| } | ||
|
|
||
| private void deleteDirectoryWithDirectEntries(Path directory) throws IOException { | ||
| try (DirectoryStream<Path> entries = Files.newDirectoryStream(directory)) { |
There was a problem hiding this comment.
[MUST] Starting a second node with the same socketDirectory deletes the first node's live socket path, so new attach connections to the first node fail. This loop also deletes unrelated files.
Please either give each node its own IPC directory, or support a shared directory safely, and limit startup/shutdown cleanup to the entries the node owns. If the directory is shared, leave it in place while other entries remain.
Please add a regression test showing that starting and stopping a second node does not break the first node's endpoint.
| resp.setContentLength(0); | ||
| return; | ||
| } | ||
| rpcServer.handle(req, resp); |
There was a problem hiding this comment.
[MUST] jsonrpc4j swallows parser-limit exceptions here and returns HTTP 200 with an empty body, while IPC turns the same failure into -32603. Malformed JSON also gets the string "null" as the response id.
Please handle these failures in the shared Admin request handling, so that both transports return valid, consistent JSON-RPC errors, with a JSON null id when the request id cannot be determined.
Please also change the depth/token tests to assert the error body, and to check that no Admin method was invoked.
| ByteArrayOutputStream output = new ByteArrayOutputStream(); | ||
|
|
||
| try { | ||
| jsonRpcServer.handleRequest(input, output); |
There was a problem hiding this comment.
[MUST] Batch handling is left to jsonrpc4j and does not follow JSON-RPC 2.0. HTTP and IPC both behave this way:
[]returns[][{"jsonrpc":"2.0","method":"admin_example","params":["a","b"]}]returns[null][{"jsonrpc":"2.0","method":"admin_example","params":["x","y"]},{"jsonrpc":"2.0","method":"admin_example","params":["a","b"],"id":2}]returns[null,{"jsonrpc":"2.0","id":2,"result":"a:b"}]
Please handle batches in the shared Admin dispatch layer: return a single -32600 error for an empty batch, leave notifications out of the response, and return no body when every entry is a notification.
Please add empty-batch, notification-only, and mixed-batch tests for both HTTP and IPC.
| */ | ||
| @Component | ||
| @Slf4j(topic = "API") | ||
| public class AdminRpcServlet extends RateLimiterServlet { |
There was a problem hiding this comment.
[SHOULD] Extending RateLimiterServlet puts Admin HTTP on the same global quota as the public API, so public traffic can delay admin requests, or get them rejected when apiNonBlocking is enabled. If a local reverse proxy makes public requests appear to come from the same loopback address as admin requests, they also share the per-IP quota.
Please give Admin HTTP an independent rate-limit policy, and decide what an overloaded request should return. The inherited {"Error":"lack of computing resources"} body is not a JSON-RPC response.
There was a problem hiding this comment.
Addressed in 0e624ea. Given its trusted-operator, low-frequency usage, Admin HTTP now extends HttpServlet and does not participate in public API rate limiting. This removes both the shared-quota dependency and the inherited REST-style rate-limit response.
Existing Host, media-type, request-size, and parser checks remain in place, along with HTTP latency metrics. Added regression coverage through service() confirming that Admin requests succeed when the public limiter rejects, under both blocking and non-blocking configurations. All 37 related tests and Checkstyle passed locally.
| exit(0); | ||
| } | ||
| List<ParameterDescription> assignedParameters = arguments.getAssignedParameters(); | ||
| if (arguments.isAttachMode()) { |
There was a problem hiding this comment.
[SHOULD] FullNode.main() already handles attach mode and returns before calling Args.setParam(), so this branch and the attach fields stored here are unused in the current FullNode startup path. They also let a direct caller skip node configuration without any error.
Please keep attach dispatch in FullNode, remove this duplicate state from Args, and reject attach options explicitly if they ever reach this method.
| System.err.println("Disconnected from server before receiving a response."); | ||
| return EXIT_FAILURE; | ||
| } | ||
| IpcResponse parsedResponse = IpcResponse.parse(response); |
There was a problem hiding this comment.
[SHOULD] The response is never checked against the request that was sent. {"result":true}, or a response with a different id, still counts as success and exits 0.
Please check that jsonrpc is "2.0", that the response id matches the request, and that exactly one of result or error is present. An invalid or mismatched response should exit with a non-zero status, since scripts use this status to tell whether the command succeeded.
| maxMessageSize = 4194304 | ||
| } | ||
|
|
||
| # Administrative API settings. Disabled by default. |
There was a problem hiding this comment.
[SHOULD] Please also add disabled Admin HTTP/IPC examples to framework/src/main/resources/config.conf. The defaults from reference.conf apply at runtime, but the config file we ship to operators doesn't show any of the new settings.
Next to the examples, please note that:
- HTTP must only be exposed on a trusted network.
- For IPC, a non-empty
node.admin.ipc.socketDirectorymust be an existing absolute directory, and the full socket path must not exceed 100 bytes once encoded. - IPC request size is controlled by
node.rpc.maxMessageSize, so operators know that changing it affects both gRPC and IPC.
What does this PR do?
This PR adds the Admin JSON-RPC transport foundation requested by #6497. A single annotated
AdminJsonRpcinterface is shared by an HTTP endpoint and a local Unix-domain-socket service. The initialadmin_examplemethod verifies typed command dispatch and annotation-based JSON-RPC error handling; additional administrative methods can be added to the same interface.In this PR, JSON-RPC defines the shared API and message format, while HTTP and IPC are the two transports:
Admin HTTP service
The HTTP service is available to FullNode processes at
POST /adminand is disabled by default. It binds Jetty to the configured address and port. Enabling it on a non-loopback address emits a warning.The servlet validates the
Hostheader against the configured virtual-host allowlist, while accepting IPv4 and IPv6 literals. It acceptsapplication/json,application/json-rpc, andapplication/*+jsonmedia types, and rejects unsupported content types with HTTP 415. JSON-RPC parsing reuses a constrained object mapper with nesting-depth and token-count limits. JSON-RPC results, including protocol errors, use HTTP 200 responses.Admin HTTP is intended for trusted node operators. Deployments must restrict access to loopback or a controlled management network. Under this trust model, Admin HTTP intentionally does not apply the public JSON-RPC
node.jsonrpc.maxBatchSizeandnode.jsonrpc.maxResponseSizelimits. The existing HTTP request-size limit and JSON nesting-depth and token-count limits still apply.The HTTP transport configuration is:
Admin IPC service
The IPC service is also FullNode-only and disabled by default. By default it creates an endpoint at:
node.admin.ipc.socketDirectorycan select another socket root and must be an existing absolute directory on a POSIX-compatible filesystem. The service creates the privateipcdirectory with owner-only access and sets the socket to0600. It refuses to replace a symbolic link or non-directory at the private directory path.The complete encoded socket path is limited to 100 bytes for portability across supported Unix-domain-socket implementations. If the path is longer, startup fails with guidance to configure a shorter
node.admin.ipc.socketDirectory; it does not silently relocate the endpoint.IPC uses newline-delimited, single-line JSON-RPC messages and the same
AdminJsonRpc, error resolver, and constrained JSON mapper as HTTP. Request size is bounded by the existingnode.rpc.maxMessageSizevalue, whose default is 4 MiB. Each client has a ten-minute idle timeout. The bounded client executor supports concurrent console sessions and immediately closes connections that arrive after all handlers are occupied. Unexpected accept failures use a five-second retry delay.Startup failures and normal shutdown both clean up owned sockets and the private directory. Shutdown closes active clients before stopping executors so blocked native socket reads do not unnecessarily delay node termination.
IPC console
The FullNode executable can attach to an active node without initializing another node instance:
A single command can be executed for scripting:
Attach mode is handled immediately after CLI argument parsing and before
CommonParameter, Logback, database, witness, or node services are initialized.--execrequires--attach, an empty socket path is rejected, and attach mode cannot be combined with--config.The JLine console derives command names, parameter names, and parameter types from the annotated Admin API. It supports quoted arguments, typed JSON conversion, sorted help, canonical command completion, formatted JSON results,
help,exit, andquit. One-shot execution returns a non-zero process status for invalid commands, JSON-RPC errors, communication failures, disconnection before a response, or the 30-second response timeout.Supporting changes
HttpServicenow supports binding a service to a specific listen address. The regular JSON-RPC servlet and the Admin transports share the new constrainedJsonRpcMapper, and supported JSON media-type matching is centralized inJsonRpcMediaType.The framework adds JLine for the interactive console and junixsocket for Unix-domain-socket support. Dependency verification metadata is updated accordingly.
Why are these changes required?
Administrative operations need a local, scriptable interface without starting a second node or exposing the existing public APIs as privileged management endpoints. The Unix-domain socket provides a private local transport, while the optional HTTP endpoint supports controlled integration when explicitly enabled.
Sharing one typed Admin API across both transports keeps command names, parameters, results, and JSON-RPC errors consistent. Explicit address binding, virtual-host validation, parser limits, filesystem permissions, bounded clients, and deterministic cleanup provide safer operational defaults.
Testing
The PR adds or updates tests covering:
The related Admin HTTP, IPC, CLI, configuration, and FullNode tests, together with production and test checkstyle checks, passed during development.
Follow-up
Runtime parameter export and Peer management commands will be implemented separately using the Admin transports introduced here.