Chio/Docs
LOGIN · JOIN

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.

kotlin
// 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.

ModuleJavaWhat it holds
chio-sdk-jvm17The 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-boot17The 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-flink21The 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.

examples/hello-spring-boot/settings.gradle.kts:17kotlin
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.

examples/hello-spring-boot/build.gradle.kts:18-28kotlin
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.

MethodLineEndpointReturns
health and isHealthy64, 70GET /chio/healthMap, Boolean
evaluateToolCall83POST /v1/evaluate/advisoryChioReceipt, or throws
evaluateToolCallAdvisory105POST /v1/evaluate/advisoryChioReceipt
evaluateHttpRequest(request, capabilityToken)152POST /chio/evaluateEvaluateResponse
evaluateHttpRequest(requestId, method, …)184POST /chio/evaluateEvaluateResponse
verifyReceipt(receipt: ChioReceipt)223POST /v1/receipts/verifyBoolean
verifyHttpReceipt(receipt: HttpReceipt)248POST /chio/verifyVerifyReceiptResponse
verifyReceipt(receipt: HttpReceipt)254POST /chio/verifyVerifyReceiptResponse
verifyReceiptChain(receipts)260noneBoolean
ChioClient.collectEvidence(receipts)323noneList<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/verify and collapses the response to one boolean. It requires ok, authorized, signer_trusted, receipt_id_valid, signature_valid, and parameter_hash_valid all true, receipt_kind mediated_decision, boundary_class prevent, trust_level mediated, and result in the set allow, authorized, Authorized. Any missing field reads as false.
  • verifyReceipt(receipt: HttpReceipt): VerifyReceiptResponse (ChioClient.kt:254) is a one-line alias for verifyHttpReceipt, which posts to /chio/verify and 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.

kotlin
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).

kotlin
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.

kotlin
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.

kotlin
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.

TypeSourceRole
ChioHttpRequestChioTypes.kt:168Request posted to /chio/evaluate.
CallerIdentity, AuthMethodChioTypes.kt:56, :19Extracted caller and how it authenticated (bearer, api_key, cookie, anonymous).
VerdictChioTypes.kt:77Kernel HTTP verdict: allow or deny, plus reason, guard, and httpStatus.
HttpReceiptChioTypes.kt:128Signed receipt for an HTTP evaluation.
EvaluateResponseChioTypes.kt:188Verdict, receipt, and evidence.
VerifyReceiptResponseChioTypes.kt:199Structured authority result from /chio/verify.
ChioPassthroughChioTypes.kt:233The record for a request the filter let past without evaluating it.
ChioErrorResponseChioTypes.kt:245The JSON body the filter writes on a deny or a sidecar failure.
ChioErrorCodesChioTypes.kt:257The five error-code constants, listed under Errors.
GuardEvidenceChioTypes.kt:116Per-guard evaluation evidence.
ChioReceiptChioReceipt.kt:15Signed tool-call receipt: action, decision, evidence.
Decision, ToolCallActionDecision.kt:13, ToolCallAction.kt:10Tool-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.

kotlin
@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.kt defines the IdentityExtractorFn type alias and defaultIdentityExtractor, which reads an Authorization: Bearer token, then X-API-Key, then the cookie header, then falls back to an anonymous identity.
  • CachedBodyHttpServletRequest.kt wraps the request so the filter hashes the full body without consuming the stream that downstream filters and controllers read.
  • compat/ChioSdkAliases.kt declares package-level typealiases so world.chio.* resolves the world.chio.sdk.* types the filter uses.
  • compat/ChioSidecarClient.kt wraps ChioClient so a starter consumer calls evaluate and verifyReceipt without duplicating the HTTP logic. Its verifyReceipt returns 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).

yaml
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
KeyDefaultPurpose
sidecar-urlCHIO_SIDECAR_URL env, else http://127.0.0.1:9090Base URL of the sidecar kernel.
timeout-seconds5HTTP timeout for sidecar calls.
on-sidecar-errordenyReserved. The filter fails closed on sidecar errors regardless of this value.
enabledtrueToggle the auto-configured filter.
url-patterns["/*"]Servlet URL patterns the filter guards.
filter-order1Filter 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.

kotlin
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.

examples/hello-spring-boot/src/main/kotlin/example/hello/HelloSpringBootApplication.kt:11-31kotlin
@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, version chio-streaming/v1, with the X-Chio-Receipt and X-Chio-Verdict headers (ReceiptEnvelope.kt:46-52).
  • DlqRouter. Builds the dead-letter record, version chio-streaming/dlq/v1 (DlqRouter.kt:131). It rejects non-deny receipts with ChioValidationError and routes on an exact topic map with a default fallback.
  • SyntheticDenyReceipt. Stamps a deny receipt carrying the chio-streaming/synthetic-deny/v1 marker (SyntheticDenyReceipt.kt:20) when the sidecar is unreachable in fail-closed mode. Its reason is prefixed [unsigned], and kernelKey and signature are empty.

Errors

Errors live in world.chio.sdk.errors and all extend ChioError, which carries a code string.

  • ChioError: the base exception, with message, code, and cause.
  • ChioDeniedError: structured deny with guard, reason, receipt id, hint, and the rest of the deny payload; fromWire and toWire round-trip the Python shape.
  • ChioConnectionError and ChioTimeoutError: transport failures.
  • ChioValidationError and ChioStreamingError: envelope and streaming invariants.
kotlin
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.

  • 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.