> For the complete documentation index, see [llms.txt](https://govplane.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://govplane.gitbook.io/docs/documentation/sdk-for-javascript-node.js/java-sdk-beta.md).

# Java SDK \[Beta]

Java 21 implementation of the Govplane Runtime SDK.

Evaluate policy decisions in-process, sub-millisecond, with no per-request HTTP.

**Artifact (Maven):**\
<https://www.npmjs.com/package/@govplane/runtime-sdk>

{% hint style="warning" icon="octagon-exclamation" %}
The SDK for Java is still in beta. Test the SDK thoroughly in a controlled environment before moving to production.
{% endhint %}

***

### Requirements

| Tool  | Version |
| ----- | ------- |
| Java  | 21 +    |
| Maven | 3.9 +   |

***

### Quick Start

#### 1. Add the dependency

```xml
<dependency>
    <groupId>com.govplane</groupId>
    <artifactId>gp-runtime-sdk</artifactId>
    <version>0.1.0</version>
</dependency>
```

#### 2. Configure and start the client

```java
import com.govplane.sdk.client.*;
import com.govplane.sdk.types.*;

RuntimeClientConfig config = RuntimeClientConfig
    .builder("<YOUR-RUNTIME-ENDPOINT>", "<YOUR-RUNTIME-KEY>")
    .pollMs(5_000)
    .degradeAfterFailures(3)
    .traceDefaults(TraceOptions.ofLevel(TraceLevel.SAMPLED))
    .build();

RuntimeClient<RuntimeBundleV1> client = new RuntimeClient<>(config);
client.start();
client.warmStart();   // blocks until first bundle (throws on timeout)
```

#### 3. Evaluate

```java
Target target = new Target("api", "/payments/charge", "POST");
Map<String, Object> ctx = Map.of("plan", "enterprise", "country", "US");

Decision decision = client.evaluate(target, ctx);

switch (decision) {
    case Decision.Allow a       -> System.out.println("ALLOW  rule=" + a.ruleId());
    case Decision.Deny d        -> System.out.println("DENY   " + d.reason());
    case Decision.KillSwitch ks -> System.out.println("KILL_SWITCH");
    case Decision.Throttle t    -> System.out.println("THROTTLE");
    case Decision.Custom c      -> System.out.println("CUSTOM " + c.value());
}
```

#### 4. Stop

```java
client.flushTraces().get();   // drain pending trace events
client.stop();
```

***

### Configuration

All options are set via `RuntimeClientConfig.builder(baseUrl, runtimeKey)`:

| Method                                  | Default                           | Description                                      |
| --------------------------------------- | --------------------------------- | ------------------------------------------------ |
| `pollMs(long)`                          | `5000`                            | Normal bundle poll interval (ms)                 |
| `burstPollMs(long)`                     | `500`                             | Poll interval during burst window (ms)           |
| `burstDurationMs(long)`                 | `30000`                           | How long burst mode lasts after a change (ms)    |
| `timeoutMs(long)`                       | `5000`                            | HTTP request timeout (ms)                        |
| `userAgent(String)`                     | `govplane-runtime-sdk-java/0.1.0` | Custom User-Agent header                         |
| `backoffBaseMs(long)`                   | `500`                             | Exponential backoff starting delay (ms)          |
| `backoffMaxMs(long)`                    | `30000`                           | Maximum backoff cap (ms)                         |
| `backoffJitter(double)`                 | `0.2`                             | Jitter fraction applied to backoff               |
| `degradeAfterFailures(int)`             | `3`                               | Consecutive failures before `Degraded` status    |
| `validateContext(boolean)`              | `true`                            | Validate context map entries before evaluation   |
| `contextPolicy(ContextPolicy)`          | `null`                            | Allow/deny-list for context keys                 |
| `parseCustomEffect(boolean)`            | `false`                           | Parse `custom` effect payload into `parsedValue` |
| `traceDefaults(TraceOptions)`           | `null`                            | Default trace options for every evaluation       |
| `onDecisionTrace(TraceSink)`            | `null`                            | Synchronous callback for each trace event        |
| `onDecisionTraceAsync(TraceSinkAsync)`  | `null`                            | Async callback for trace events                  |
| `traceQueueMax(int)`                    | `1000`                            | Max trace events queued for async sink           |
| `traceDropPolicy(TraceQueueDropPolicy)` | `DROP_NEW`                        | What to drop when the queue is full              |
| `onTraceError(Consumer<Throwable>)`     | `null`                            | Error handler for failed async trace emissions   |
| `incidentEnvFlag(String)`               | `GP_RUNTIME_INCIDENT`             | Env var for runtime incident override            |
| `incidentFilePath(String)`              | `null`                            | Path to incident control file                    |
| `incidentFilePollMs(long)`              | `1000`                            | How often to poll the incident file (ms)         |

***

### Decision Types

`Decision` is a sealed interface. All variants carry `decision()`, `reason()`, `policyKey()`, and `ruleId()`.

| Variant               | Extra fields                                                                   |
| --------------------- | ------------------------------------------------------------------------------ |
| `Decision.Allow`      | —                                                                              |
| `Decision.Deny`       | —                                                                              |
| `Decision.KillSwitch` | `killSwitch()` → `Effect.KillSwitchDetails`                                    |
| `Decision.Throttle`   | `throttle()` → `Effect.ThrottleDetails`                                        |
| `Decision.Custom`     | `value()` (raw string), `parsedValue()` (parsed when `parseCustomEffect=true`) |

***

### Trace Options

Control tracing per-call or via `traceDefaults`:

```java
// Level only
TraceOptions opts = TraceOptions.ofLevel(TraceLevel.FULL);

// Force a trace (bypass sampling)
TraceOptions opts = TraceOptions.forced(TraceLevel.FULL);

// Full control
TraceOptions opts = new TraceOptions(TraceLevel.SAMPLED, 0.1, null, null);

// With budget
TraceOptions opts = new TraceOptions(
    TraceLevel.SAMPLED, null, null,
    new TraceOptions.TraceBudgetConfig(100, 60_000)
);
```

| `TraceLevel` | Behaviour                                                         |
| ------------ | ----------------------------------------------------------------- |
| `OFF`        | No trace                                                          |
| `ERRORS`     | Trace only when the decision is `deny` or `kill_switch`           |
| `SAMPLED`    | Trace a fraction of requests (controlled by `sampling` or policy) |
| `FULL`       | Always trace, include all rule evaluations                        |

Access the trace from an evaluation:

```java
DecisionWithTrace result = client.evaluateWithTrace(target, ctx,
    TraceOptions.ofLevel(TraceLevel.FULL));

if (result.hasTrace()) {
    DecisionTrace trace = result.trace();
    System.out.println(trace.traceId());
    System.out.println(trace.winner());
}
```

***

### Client Lifecycle

```
start() → [WarmingUp] → (first bundle) → [Ok]
                     ↘ (n failures) → [Degraded]
stop()
```

| Method                        | Description                                                                   |
| ----------------------------- | ----------------------------------------------------------------------------- |
| `start()`                     | Begin background polling (idempotent)                                         |
| `warmStart()`                 | Block up to 10 s for the first valid bundle                                   |
| `warmStart(timeoutMs, burst)` | Block with custom timeout; `burst=true` speeds up initial poll                |
| `getStatus()`                 | Returns `RuntimeStatus.Ok`, `.WarmingUp`, or `.Degraded`                      |
| `onUpdate(listener)`          | Subscribe to bundle-change events; returns an unsubscribe handle              |
| `onStatus(listener)`          | Subscribe to status-change events; called immediately with current status     |
| `flushTraces()`               | Returns a `CompletableFuture<Void>` that resolves when the trace queue drains |
| `stop()`                      | Shut down the polling scheduler                                               |

***

### Thread Safety

`RuntimeClient` is fully thread-safe. `evaluate()` and `evaluateWithTrace()` may be called concurrently from any number of threads without synchronization. Bundle refreshes happen on a dedicated daemon thread and never block evaluations.

***

### Running the Example

```bash
cd examples/basic
export GP_BASE_URL=<YOUR-RUNTIME-ENDPOINT>
export GP_RUNTIME_KEY=<YOUR-RUNTIME-KEY>

# Compile (requires JAR on classpath; adjust path as needed)
javac -cp ../../target/gp-runtime-sdk-0.1.0.jar src/BasicExample.java -d .

# Run
java -cp ../../target/gp-runtime-sdk-0.1.0.jar:. BasicExample
```

See examples/basic/src/BasicExample.java for the full annotated example.

***

### Building from Source

```bash
export JAVA_HOME=/opt/homebrew/opt/openjdk   # or your Java 21 home
mvn clean package
mvn test
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://govplane.gitbook.io/docs/documentation/sdk-for-javascript-node.js/java-sdk-beta.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
