| name | krpc |
| description | Authoring and calling KRPC (tech.krpc) services. Use when writing or debugging a service that depends on tech.krpc / tech.krpc.ext, or when you see @RpcService, RpcResult<ā¦>, @UnsafeWeb, discoverāinvoke over HTTP, or a Quarkus/GraalVM native build error from a krpc consumer. Covers the five rules that bite first (method signature, RpcResult, soft/hard errors, boxed DTOs, @UnsafeWeb), the HTTP agent discoverāinvoke path, and where native-image help lives. Do NOT use for non-krpc gRPC or plain JVM builds. |
krpc ā authoring & calling krpc services (thin shim)
SoT is the krpc repo SPEC.md ā the tool-agnostic authoring handbook with
file:line code evidence. This skill is a trigger + quick-reference only; when
it and SPEC.md disagree, SPEC.md wins. Governance authority is AGENTS.md
- Full handbook:
references/SPEC.md bundled next to this skill (verbatim,
release-synced copy of the repo-root SPEC.md ā CI fails if they diverge), so the
skill stays self-contained when copied into a consumer project. In the krpc repo
itself, the root SPEC.md is the canonical copy.
- Agents discovering/calling a running service: read
docs/agent-guide.md.
- Native-image consumers:
SPEC.md §13 + the krpc-native-build skill.
Five rules that bite first (condensed from SPEC §1ā§6)
Each line is a pointer, not the spec. Read the cited SPEC section before relying on it.
- Method signature ā SPEC §1. Every RPC method is
RpcResult<Dto> m(OneDto)
or m(): the return type MUST be RpcResult<ā¦> and there is at most one
parameter. Violate either and the method is silently dropped ā not
registered, no startup error, fails to resolve at call time. Merge multiple
inputs into one DTO.
- RpcResult envelope ā SPEC §2.
code 0=OK / >0=business error
(no negative codes); msg is non-null only when code>0; data is
non-null only when code==0. Build with RpcResult.ok(data) /
RpcResult.error(code,msg); callers read data only after isOk().
- Soft vs hard errors ā SPEC §3. Business failures return
RpcResult.error(code,msg) ā do not throw. System / security / validation /
unexpected errors throw (the server wraps them in a gRPC Status, message
truncated to 100 chars). Never use a Java exception to carry a business error.
- Boxed DTO fields ā SPEC §4. Scalar DTO fields MUST be boxed
(
Integer/Long/Boolean/Float/Double), never primitives: JSON
serialization is NON_NULL, so a primitive serializes as 0/false and
destroys the "absent vs zero" distinction. Avoid Map and enum in response DTOs.
byte[] is the one type passed bare on the wire (not JSON-boxed) ā but TS/Dart
clients cannot send byte[] as input.
- @UnsafeWeb ā SPEC §6. TYPE-level
@UnsafeWeb marks a service reachable
directly by browsers/frontend over the HTTP gateway; without it the service
name is --prefixed (hidden) and stays service-to-service only. Add it ONLY to
frontend-facing services. @UnsafeWeb(requireCredential=true) (class) or
@UnsafeWeb.RequireCredential (method) requires a JWT ā you own the hardening.
Agent call path ā HTTP discover ā invoke (P0, ADR-0004)
A running krpc server can be introspected and called over plain HTTP/1.1 JSON ā no
gRPC stack. These endpoints are on the plain-HTTP port (http.port, default
8080), which is separate from the gRPC gateway port (rpc.server.port, default
50051).
curl http://HOST:8080/agent/discover
curl -X POST http://HOST:8080/agent/invoke \
-H 'Content-Type: application/json' \
-d '{"service":"<Service>","method":"<method>","input":{ ... }}'
service/method are app-relative (the app/ prefix is stripped): call
quickstart/Hello/hello as {"service":"Hello","method":"hello"}.
- Errors ride a
code field in the JSON body (gRPC-style status); the HTTP status
line stays 200 (transport emits only 200/404/500).
- Credential is not bypassed relative to gRPC. The
Authorization: Bearer <jwt>
header is forwarded into the same credential check as a normal call; a
requireCredential service is checked no differently here. The agent path forwards
only Authorization (plus client-id / traceparent) ā no Cookie header,
so the cookie credential fallback works on the web/gRPC path but not on the agent
surface (current impl); send a bearer token. Actual rejection depends on auth
being configured ā the check enforces only when a verifier is registered
(rpc.server.jwks set + loaded); missing/unloadable JWKS with exitOnJwksError off
silently skips it (SPEC §8.7, exitOnJwksError=true in prod).
- Auth and rate-limiting are the gateway's job, not krpc core ā put these two
paths behind a gateway (ADR-0004).
@UnsafeWeb(agentTool=ā¦) is not built yet ā a P1 design in ADR-0004. P0 gates
agent exposure on @UnsafeWeb only; there is no narrower agent-tool subset today.
- Caveats: JVM-mode only (native reflection-config for these handlers is not
added); and in a minimal Quarkus app the handler beans must be retained ā see
docs/agent-guide.md for the runtime note and a real discoverāinvoke transcript.
Native image (GraalVM / Quarkus)
Building a krpc consumer as a native image? The single source of truth is
SPEC.md §13 (version matrix, ext-rpc server support, build recipe,
reflection coverage, checklist). The krpc-native-build skill is the trigger +
fallback cheat-sheet for the same content.