ReferenceSDKs
JVM SDK
The world.chio Gradle composite: the core sidecar client, the Spring Boot filter, the Flink streaming envelopes, and their configuration keys.
Source
This page reflects the Gradle composite at sdks/jvm/ in the chio repository: settings.gradle.kts, build.gradle.kts, and the three included projects chio-sdk-jvm, chio-spring-boot, and chio-streaming-flink. The root project name, the group, the version, and each module's Java version render from the sdks dataset at the pin. The worked application is examples/hello-spring-boot. None of these files carries a specification status line or the RFC 2119 keywords; the Kotlin source is the truth for every name below.
Synopsis
Include the SDK tree as a composite build, declare the coordinate, then construct a client.
// settings.gradle.kts
includeBuild("../../sdks/jvm")
// build.gradle.kts
implementation("world.chio:chio-spring-boot:0.1.0")
// Application code
import world.chio.sdk.ChioClient
val client = ChioClient("http://127.0.0.1:9090")Modules
The root project is chio-jvm, and sdks/jvm/build.gradle.kts:8-12 sets group to world.chio and version to 0.1.0 for every project. settings.gradle.kts:20-22 includes the three below and no others. Each module sets its own Java source and target compatibility, and the streaming module sets a higher one than the other two.
| Module | Java | What it holds |
|---|---|---|
chio-sdk-jvm | 17 | The blocking sidecar client, canonical JSON, hashing, the receipt types, and the streaming envelope builders. Its only api dependencies are the Kotlin standard library and Jackson. |
chio-spring-boot | 17 | The servlet filter and its auto-configuration. Carries the core as an api dependency (chio-spring-boot/build.gradle.kts:14), so naming the starter is enough. |
chio-streaming-flink | 21 | The Flink operator. Takes the core as an api dependency and Flink itself as compileOnly (chio-streaming-flink/build.gradle.kts:35-36). |
A fourth directory, sdks/jvm/chio-kernel-mobile, sits beside the composite with its own settings.gradle.kts and packages an Android AAR (chio-kernel-mobile/README.md:1-4). The composite's settings file does not include it, so a Gradle build of chio-jvm does not build it. sdks/README.md:14 lists it alongside the three composite modules.
Installation
Consume the modules as a Gradle composite build. One includeBuild line in settings.gradle.kts points at the SDK tree, and Gradle substitutes the world.chio coordinates for the projects it finds there. examples/hello-spring-boot does this, which is why that example needs no Maven repository beyond Maven Central for Spring itself.
includeBuild("../../sdks/jvm")With the composite build in place, declare the starter the way any other dependency is declared. The substituted projects are :chio-sdk-jvm, :chio-spring-boot, and :chio-streaming-flink.
dependencies {
implementation(platform("org.springframework.boot:spring-boot-dependencies:3.2.2"))
implementation("world.chio:chio-spring-boot:0.1.0")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation(platform("org.springframework.boot:spring-boot-dependencies:3.2.2"))
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
}Both the example and the starter talk to a running Chio sidecar. SidecarPaths.DEFAULT_BASE_URL is http://127.0.0.1:9090 (SidecarPaths.kt:8).
ChioClient
world.chio.sdk.ChioClient (ChioClient.kt:27-33) is the blocking HTTP client. It takes a base URL defaulting to SidecarPaths.DEFAULT_BASE_URL and a java.time.Duration timeout defaulting to five seconds. It implements AutoCloseable, though close() is a no-op: java.net.http.HttpClient has no close() before JDK 21 (ChioClient.kt:52-58).
It also implements ChioClientLike (ChioClientLike.kt:8-17), the two-method interface the Flink operator calls, so a test double replaces the client without an HTTP server. That interface's default verifyReceipt returns false (ChioClientLike.kt:16), so an implementation that forgets to override it denies rather than allows.
| Method | Line | Endpoint | Returns |
|---|---|---|---|
health and isHealthy | 64, 70 | GET /chio/health | Map, Boolean |
evaluateToolCall | 83 | POST /v1/evaluate/advisory | ChioReceipt, or throws |
evaluateToolCallAdvisory | 105 | POST /v1/evaluate/advisory | ChioReceipt |
evaluateHttpRequest(request, capabilityToken) | 152 | POST /chio/evaluate | EvaluateResponse |
evaluateHttpRequest(requestId, method, …) | 184 | POST /chio/evaluate | EvaluateResponse |
verifyReceipt(receipt: ChioReceipt) | 223 | POST /v1/receipts/verify | Boolean |
verifyHttpReceipt(receipt: HttpReceipt) | 248 | POST /chio/verify | VerifyReceiptResponse |
verifyReceipt(receipt: HttpReceipt) | 254 | POST /chio/verify | VerifyReceiptResponse |
verifyReceiptChain(receipts) | 260 | none | Boolean |
ChioClient.collectEvidence(receipts) | 323 | none | List<GuardEvidence> |
The two verifyReceipt overloads
The name verifyReceipt is overloaded on its argument type, and the two overloads reach different endpoints and return different types. Read the argument, not the name.
verifyReceipt(receipt: ChioReceipt): Boolean(ChioClient.kt:223) posts to/v1/receipts/verifyand collapses the response to one boolean. It requiresok,authorized,signer_trusted,receipt_id_valid,signature_valid, andparameter_hash_validall true,receipt_kindmediated_decision,boundary_classprevent,trust_levelmediated, andresultin the setallow,authorized,Authorized. Any missing field reads as false.verifyReceipt(receipt: HttpReceipt): VerifyReceiptResponse(ChioClient.kt:254) is a one-line alias forverifyHttpReceipt, which posts to/chio/verifyand returns the parsed structure rather than a verdict.
HTTP request evaluation
evaluateHttpRequest is the API the Spring Boot filter uses. It has two overloads: one takes a built ChioHttpRequest plus an optional capability token, the other takes the fields and builds the request for a Java caller, defaulting the timestamp to the current second (ChioClient.kt:184-215). A capability token, when present, rides in the X-Chio-Capability header.
import world.chio.sdk.CallerIdentity
import world.chio.sdk.ChioClient
import world.chio.sdk.ChioHttpRequest
val client = ChioClient("http://127.0.0.1:9090")
val response = client.evaluateHttpRequest(
ChioHttpRequest(
requestId = "req-1",
method = "GET",
routePattern = "/pets",
path = "/pets",
caller = CallerIdentity.anonymous(),
timestamp = System.currentTimeMillis() / 1000,
),
)Any allow-shaped response is integrity-checked before the client hands it back: the embedded receipt must be a mediated authorization, and verifyHttpReceipt must authorize it. A non-authorizing allow raises ChioError with code chio_invalid_receipt rather than letting a forged allow through (ChioClient.kt:164-178).
Advisory tool-call evaluation
evaluateToolCallAdvisory posts to /v1/evaluate/advisory, checks the advisory receipt's integrity, and returns it. The response is wrapped in the chio.sidecar.advisory-evaluation.v1 envelope, which sets authorization to false and authorizationBasis to advisory_only. A response that does not identify that basis raises ChioError (ChioClient.kt:300-305).
val advisory = client.evaluateToolCallAdvisory(
capabilityId = "cap-fraud",
toolServer = "flink://fraud-job",
toolName = "events:consume:transactions",
parameters = mapOf("body_length" to 42L, "body_hash" to "..."),
)
println("advisory receipt: " + advisory.id)evaluateToolCall (ChioClient.kt:83) is the ChioClientLike implementation. It calls the advisory route and then throws ChioDeniedError on both paths: one message for an observation outcome of dropped, another for an advisory evaluation that is not an authoritative authorization decision (ChioClient.kt:96-102). Against the advisory-only sidecar route it therefore never returns. Treat a returned value as authorization and the exception as the expected non-authorization signal.
Client-side chain verification
verifyReceiptChain (ChioClient.kt:260) walks a Merkle chain of receipts with no network round-trip. Each receipt's contentHash must equal the SHA-256 of the canonical JSON of its predecessor, computed through CanonicalJson.
val chained: Boolean = client.verifyReceiptChain(receipts)Canonical JSON
CanonicalJson (CanonicalJson.kt) is the Jackson-backed canonicalizer every receipt hash flows through. It matches Python's json.dumps(sort_keys=True, separators=(",", ":"), ensure_ascii=True) byte for byte: map keys and object properties sorted in code-point order, no insignificant whitespace, non-ASCII escaped as lowercase \uXXXX, and null map values preserved rather than dropped.
import world.chio.sdk.CanonicalJson
val bytes: ByteArray = CanonicalJson.writeBytes(mapOf("b" to 2, "a" to 1))
val text: String = CanonicalJson.writeString(mapOf("b" to 2, "a" to 1))
// text == {"a":1,"b":2}Prefer writeBytes when the consumer hashes the output. world.chio.sdk.Hashing.sha256Hex accepts either a String or a ByteArray and emits lowercase hex.
Receipt types
The receipt object graph lives in world.chio.sdk. Every type carries Jackson bindings to the snake_case wire names and implements java.io.Serializable, so it crosses Flink operator boundaries.
| Type | Source | Role |
|---|---|---|
ChioHttpRequest | ChioTypes.kt:168 | Request posted to /chio/evaluate. |
CallerIdentity, AuthMethod | ChioTypes.kt:56, :19 | Extracted caller and how it authenticated (bearer, api_key, cookie, anonymous). |
Verdict | ChioTypes.kt:77 | Kernel HTTP verdict: allow or deny, plus reason, guard, and httpStatus. |
HttpReceipt | ChioTypes.kt:128 | Signed receipt for an HTTP evaluation. |
EvaluateResponse | ChioTypes.kt:188 | Verdict, receipt, and evidence. |
VerifyReceiptResponse | ChioTypes.kt:199 | Structured authority result from /chio/verify. |
ChioPassthrough | ChioTypes.kt:233 | The record for a request the filter let past without evaluating it. |
ChioErrorResponse | ChioTypes.kt:245 | The JSON body the filter writes on a deny or a sidecar failure. |
ChioErrorCodes | ChioTypes.kt:257 | The five error-code constants, listed under Errors. |
GuardEvidence | ChioTypes.kt:116 | Per-guard evaluation evidence. |
ChioReceipt | ChioReceipt.kt:15 | Signed tool-call receipt: action, decision, evidence. |
Decision, ToolCallAction | Decision.kt:13, ToolCallAction.kt:10 | Tool-call verdict and the hashed action parameters. |
Authorization predicates sit on the types rather than in caller code. HttpReceipt.isAuthorized() requires receiptKind mediated_decision, boundaryClass prevent, trustLevel mediated, a null observationOutcome, and an allow verdict. The Receipt format page carries the full field set.
Spring Boot starter
chio-spring-boot auto-configures a servlet filter as soon as it is on the classpath. META-INF/spring.factories registers world.chio.ChioAutoConfiguration under EnableAutoConfiguration, so a minimal application needs no extra wiring.
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
@RestController
class PetsController {
@GetMapping("/pets")
fun pets(): Map<String, Any> = mapOf("pets" to emptyList<Any>())
}The filter fails closed. A sidecar it cannot reach produces a 502 with a structured JSON body (ChioFilter.kt:142 and :177). A denied evaluation returns the verdict's httpStatus, falling back to 403 (ChioFilter.kt:154). An allowed request reaches the chain only after the receipt passes the semantic check and the signature check, and the filter then sets X-Chio-Receipt-Id to the receipt id (ChioFilter.kt:188). It reads a capability token from the X-Chio-Capability header or the chio_capability query parameter (ChioFilter.kt:40).
What the starter adds
ChioIdentityExtractor.ktdefines theIdentityExtractorFntype alias anddefaultIdentityExtractor, which reads anAuthorization: Bearertoken, thenX-API-Key, then the cookie header, then falls back to an anonymous identity.CachedBodyHttpServletRequest.ktwraps the request so the filter hashes the full body without consuming the stream that downstream filters and controllers read.compat/ChioSdkAliases.ktdeclares package-level typealiases soworld.chio.*resolves theworld.chio.sdk.*types the filter uses.compat/ChioSidecarClient.ktwrapsChioClientso a starter consumer callsevaluateandverifyReceiptwithout duplicating the HTTP logic. ItsverifyReceiptreturns whether the verification authorizes the receipt.
Configuration
ChioProperties (ChioAutoConfiguration.kt:24-38) binds under the chio prefix. The auto-configuration itself is conditional on chio.enabled, and a missing value counts as enabled (:44).
chio:
sidecar-url: http://127.0.0.1:9090
timeout-seconds: 5
on-sidecar-error: deny # reserved: the filter always denies sidecar errors
enabled: true
url-patterns:
- "/*"
filter-order: 1| Key | Default | Purpose |
|---|---|---|
sidecar-url | CHIO_SIDECAR_URL env, else http://127.0.0.1:9090 | Base URL of the sidecar kernel. |
timeout-seconds | 5 | HTTP timeout for sidecar calls. |
on-sidecar-error | deny | Reserved. The filter fails closed on sidecar errors regardless of this value. |
enabled | true | Toggle the auto-configured filter. |
url-patterns | ["/*"] | Servlet URL patterns the filter guards. |
filter-order | 1 | Filter order; lower runs first. |
For header-driven caller lookup and pattern-based routing, supply a ChioFilterConfig with custom identityExtractor and routeResolver functions. The default resolver returns the raw path.
import world.chio.ChioFilterConfig
val config = ChioFilterConfig(
sidecarUrl = "http://127.0.0.1:9090",
routeResolver = { _, path ->
if (path.startsWith("/pets/")) "/pets/{petId}" else path
},
)A worked application
examples/hello-spring-boot registers the filter explicitly rather than relying on auto-configuration, which is what a service wants when only some routes are governed. It guards /hello and /echo and leaves /healthz alone.
@SpringBootApplication
class HelloSpringBootApplication {
@Bean
fun chioFilterRegistration(): FilterRegistrationBean<ChioFilter> {
val filter = ChioFilter(
ChioFilterConfig(
sidecarUrl = System.getenv("CHIO_SIDECAR_URL") ?: "http://127.0.0.1:9090",
),
)
return FilterRegistrationBean<ChioFilter>().apply {
setFilter(filter)
addUrlPatterns("/hello", "/echo")
order = Ordered.HIGHEST_PRECEDENCE
}
}
}
fun main(args: Array<String>) {
runApplication<HelloSpringBootApplication>(*args)
}Run it with examples/run-hello-smokes.sh hello-spring-boot, which starts a trust control plane, the application, and a sidecar, then walks the three routes. An allowed request carries X-Chio-Receipt-Id; a POST /echo without a capability returns 403 and a ChioErrorResponse body carrying chio_access_denied, the deny reason, the receipt id, and the suggestion provide a valid capability token in the X-Chio-Capability header or chio_capability query parameter (ChioFilter.kt:162). The refusal is a receipt, not a dropped request.
Streaming primitives
The core carries the envelope builders that chio-streaming-flink emits to Kafka. Each pins a schema version and a fixed header set.
ReceiptEnvelope. The receipt side-output envelope, versionchio-streaming/v1, with theX-Chio-ReceiptandX-Chio-Verdictheaders (ReceiptEnvelope.kt:46-52).DlqRouter. Builds the dead-letter record, versionchio-streaming/dlq/v1(DlqRouter.kt:131). It rejects non-deny receipts withChioValidationErrorand routes on an exact topic map with a default fallback.SyntheticDenyReceipt. Stamps a deny receipt carrying thechio-streaming/synthetic-deny/v1marker (SyntheticDenyReceipt.kt:20) when the sidecar is unreachable in fail-closed mode. Its reason is prefixed[unsigned], andkernelKeyandsignatureare empty.
Errors
Errors live in world.chio.sdk.errors and all extend ChioError, which carries a code string.
ChioError: the base exception, withmessage,code, andcause.ChioDeniedError: structured deny with guard, reason, receipt id, hint, and the rest of the deny payload;fromWireandtoWireround-trip the Python shape.ChioConnectionErrorandChioTimeoutError: transport failures.ChioValidationErrorandChioStreamingError: envelope and streaming invariants.
import world.chio.sdk.errors.ChioDeniedError
try {
client.evaluateToolCall(
capabilityId = "cap-fraud",
toolServer = "flink://fraud-job",
toolName = "events:consume:transactions",
parameters = mapOf("body_length" to 42L),
)
} catch (error: ChioDeniedError) {
println("not authorized: " + error.message)
}ChioErrorCodes (ChioTypes.kt:257-263) holds the codes: chio_access_denied, chio_sidecar_unreachable, chio_evaluation_failed, chio_invalid_receipt, and chio_timeout. Each is the string the sidecar returns in its error body, and each matches the Schemas and errors taxonomy.
Parity
Canonical JSON, synthetic-deny, and the DLQ builders each carry a parity test tag and assert byte equality against Python vectors. The Bindings API page carries the cross-language invariants every binding conforms against.
Related
- SDKs: the other language bindings and their package names.
- Python SDK: the client this one mirrors, method for method.
- HTTP substrate: the sidecar endpoints in the table above.
- Receipt format: the fields the receipt types bind to.
- Bindings API: the canonical JSON and hashing contract the parity tests check.