Integrations

Java

Pure-JVM AI agent detection for the JDK HttpServer and Servlet containers

Overview

Checkpoint for Java embeds the same kya-os-engine WASI build the .NET SDK and the DNS gateway run, and executes it through Chicory, a pure-Java WebAssembly runtime. There are no native dependencies, and it requires JDK 21 or later.

ModulePurpose
checkpoint-coreEngine host (C-ABI over WASI), detection API, dashboard policy and telemetry client, enforcement planner
checkpoint-jdk-httpservercom.sun.net.httpserver.Filter adapter (also builds the shaded jar used for vendored installs)
checkpoint-servletjakarta.servlet.Filter adapter for servlet containers

Every request is classified by the embedded engine, producing identical verdicts to the DNS gateway and the .NET and TypeScript SDKs (asserted by the java-wasi row of pnpm cross-runtime-parity), and detections report to the dashboard's /api/v1/log-detection with sdk.runtime: "jvm".

Prerequisites

  • A Checkpoint project: get your Project ID and API key from Installations in the dashboard.
  • JDK 21 or later.
  • A com.sun.net.httpserver based app, or a jakarta.servlet container.

Installation

Not yet on a public registry

The Java SDK is not published to Maven Central or any other public registry today. Build it from source with mvn -f packages/checkpoint-java/pom.xml verify, or vendor the shaded checkpoint-jdk-httpserver jar produced by that build into your own artifact repository.

The Maven coordinates, once you have a jar built or vendored:

<dependency>
  <groupId>ai.knowthat.checkpoint</groupId>
  <artifactId>checkpoint-core</artifactId>
  <version>0.2.0</version>
</dependency>
<dependency>
  <groupId>ai.knowthat.checkpoint</groupId>
  <artifactId>checkpoint-jdk-httpserver</artifactId>
  <version>0.2.0</version>
</dependency>

Swap checkpoint-jdk-httpserver for checkpoint-servlet if you run inside a servlet container. An app that already carries a different Jackson version should use the *-shaded.jar variant (everything relocated under ai.knowthat.checkpoint.shaded.*) or pin Jackson through its own dependency management, since the thin jars resolve Chicory, ASM, and Jackson onto the host classpath.

Quick Setup

Checkpoint checkpoint = Checkpoint.create(CheckpointOptions.builder()
    .projectId("<project-id>")
    .apiKey("sk_live_...")
    // The builder default is OBSERVE, which never blocks. Set ENFORCE to
    // actually short-circuit.
    .enforcementMode(CheckpointOptions.EnforcementMode.ENFORCE)
    .onAgentDetected(EnforcementAction.BLOCK)
    .build());

// jdk.httpserver:
context.getFilters().add(CheckpointFilter.wrap(checkpoint));
// servlet containers:
new CheckpointServletFilter(checkpoint);

Call checkpoint.warmup() during your app's own startup to pre-warm the engine pool off the request path; both adapters already call it synchronously during their own init, so size your container's health-check timeout accordingly.

Enforce vs Observe Mode

enforcementMode defaults to OBSERVE, which never short-circuits a request: every response the engine decided carries KYA-Mode: observe, and whatever onAgentDetected (or the dashboard policy) would have done, the request passes through with KYA-Would-Have-Been: <block|redirect|instruct|challenge> and KYA-Would-Have-Been-Reason: <threshold|deny_list|configured_action|...>. The telemetry event reports enforcement.action: "log" with the withheld action in enforcement.wouldBlock. Set ENFORCE to actually block, redirect, instruct, or challenge.

onAgentDetected defaults to EnforcementAction.LOG and confidenceThreshold defaults to 70.

Client IP: Trust Profiles

The client IP the engine sees is the direct TCP peer by default, which is the load balancer's own address once anything sits in front of the app: the engine's Tier-2 vendor-IP cross-match then never fires against the real caller. Both adapters ship the same Direct/Vercel/Cloudflare/TrustedProxy header-precedence profiles the TypeScript and .NET SDKs have:

  • checkpoint-servlet: CheckpointServletFilter's two-argument constructor takes a ClientIpTrustPolicy, or configure it via the clientIpPolicy (vercel / cloudflare / trusted-proxy) and clientIpTrustedHops (hop count for trusted-proxy) filter init-params. Default stays directPeer().
  • checkpoint-jdk-httpserver: RemoteIpResolver.vercel(), .cloudflare(), and .trustedProxy(int hops) factories, passed to CheckpointFilter.create / .wrap the same way directPeer() always was. A fifth, Checkpoint-specific profile, RemoteIpResolver.gateway(), trusts the gateway's KYA-Client-IP header for an origin deployed directly behind the Checkpoint Cloudflare gateway; it additionally verifies the direct peer against Cloudflare's published edge IPv4 ranges before trusting the header at all.

Every profile is opt-in, and none of them is safe to select on an origin any untrusted caller can also reach directly. Either adapter also accepts a fully custom RemoteIpResolver / ClientIpResolver for a topology none of the built-in profiles cover.

Response Headers

Every response includes detection metadata:

HeaderValueWhen
KYA-Detectedtrue / falseAlways
KYA-Confidence0-100Always
KYA-ClassHuman / AiAgent / Bot / IncompleteDataAlways
KYA-AgentAgent nameWhen detected
KYA-VerificationVerification method (e.g. signature, kya-http-delegated)When detected
KYA-Correlation-Id32-hex correlation idAlways
KYA-Reason-CodesComma-joined detection/* codes (see Reason codes)When any apply

If a burst of traffic exhausts the bounded WASM engine pool (maxEngineInstances, engineAcquireTimeout), the request degrades to detection-unavailable rather than blocking on a cold instantiation: KYA-Detection: unavailable is stamped, and under failOpen (the default) the request still passes through with KYA-Failopen: true.

Signature and Delegation Verification

ChatGPT's RFC 9421 HTTP Message Signatures are verified whenever a request carries Signature / Signature-Input headers: the SDK reconstructs the signature base and verifies it with the JDK's native Ed25519 support against a live-fetched public key, no configuration required. A verified signature yields 100% confidence.

Separately, a request that presents a delegation credential (Authorization: Bearer <VC-JWT>, or the KYA-Delegation marker header for hosts that strip Authorization) with no kya-http/2 signature routes to the engine's standalone delegation-verify entry instead of plain detection. Unlike the .NET SDK's live did:web resolution, the Java SDK resolves trusted delegation roots, DID documents, and revocation lists from static, operator-configured maps on CheckpointOptions rather than fetching them live.

Dashboard Reporting

Detection events are buffered and flushed as a batch to POST /api/v1/log-detection, fire-and-forget after the request completes; pure human traffic is not reported. Requires projectId and apiKey to be set: without them, detection and enforcement still run, only the dashboard hop is skipped. Raw IP and full URLs are reported by default; .telemetryHashIp(true) on the builder salts the IP with a project-scoped SHA-256 hash before it leaves the process, and .telemetryStripQuery(true) strips the ?query suffix from reported paths and URLs.

Coming in the Next Release

The following are in progress on open branches and not yet part of a released Java SDK version; they are described here so this page does not need a rewrite the moment they ship, but nothing below should be treated as current behavior:

  • A build-time check that projectId is the dashboard project UUID (not its friendly id) whenever browserPosture is configured, since the posture cookie name and its signed claim are both the UUID.
  • Additional /log-detection telemetry fields: the resolved enforcement mode, the deciding composed-policy id, a per-stage engine trace, request latency, the verified browser-posture claim, and a reported-identity block.
  • Additional EngineConfig options on CheckpointOptions covering tenant-policy handling for verified-vendor traffic, a Noisy-OR probability combiner (shadow or live), a session-velocity escalation hint, a scanner-misclassification hint, and trusting a fixed set of Cloudflare-supplied transport-signal headers when this deployment sits behind Cloudflare exclusively.
  • A bounded seen-nonce cache for kya-http replay detection.

Build

mvn -f packages/checkpoint-java/pom.xml verify

The embedded kya-os-engine.wasm is sha256-pinned by a drift test; re-baselining is documented alongside that test. Versioning follows the .NET precedent: one version for all modules, set in the parent pom.xml. Block responses currently use the .NET SDK's JSON response mode; the plain-text agent-notice mode and its KYA-Block-Reason header parity with .NET are not ported yet.

Next Steps