Skip to content

Latest commit

 

History

History
146 lines (102 loc) · 6.7 KB

File metadata and controls

146 lines (102 loc) · 6.7 KB

KeelbaseClient — Delegation tokens & audit reporting

The keelbase-client module gives Java/Spring systems first-class access to two KeelBase capabilities:

  1. Delegated identity as a client (KeelbaseClient) — when a user session holds a KeelBase JWT, obtain a short-lived delegation token bound to a target audience, cache it with proactive refresh, and verify it locally with the shared DELEGATION_SECRET.
  2. Audit reporting (KeelbaseAuditReporter) — report Java-side business actions to the governance audit chain (D2-3a /external/audit), so what happened in your system is visible alongside KeelBase's own AI audit.

Both are auto-configured by the starter; you only add properties and inject the beans.


1. KeelbaseClient — delegation-token lifecycle

Scenario

Your Java system integrates with KeelBase as a client: a user signs into KeelBase (or SSO), your system holds their KeelBase access token, and you need to act on their behalf across system boundaries. KeelBase signs a short-lived delegation JWT (sub = their user id, aud = your target system, default 300s) — see delegated identity for how receivers verify it.

Configuration (keelbase.client.*)

keelbase:
  client:
    base-url: http://localhost:3000   # KeelBase service root (for /api/v1/auth/delegation-token)
    audience: legacy-crm              # optional; falls back to keelbase.delegation.audience
    # connect-timeout: 3s
    # read-timeout: 10s

Usage

@Autowired KeelbaseClient keelbaseClient;

// 1. Obtain a delegation token bound to the target audience (Bearer = the user's KeelBase JWT)
KeelbaseTokenIssue issue = keelbaseClient.obtain(userJwt, "legacy-crm", 300);
// issue.token() / subject() (oidcSub or local:<userId>) / expiresIn() / userId() / audience()

// 2. Cache + proactive refresh: same (jwt, audience) reuses the cached token until ≤30s remain
String token = keelbaseClient.obtainAndCache(userJwt, "legacy-crm", 300);

// 3. Verify a delegation token you received (shared DELEGATION_SECRET, HS256 + audience)
keelbaseClient.verify(token, "legacy-crm");   // throws KeelbaseClientException on bad sig / wrong aud

// 3b. Verify and get the parsed identity (subject / oidcSub / audience / expiresAt)
Map<String, Object> identity = keelbaseClient.verifyAndGet(token, "legacy-crm");
// identity.get("oidcSub") -> map to your local user

Contract (matches KeelBase POST /api/v1/auth/delegation-token):

Item Value
Request { audience: string, ttlSeconds?: 60-3600 (default 300) }, Authorization: Bearer <user JWT>
Response (unwrapped data) { token, subject, expiresIn, userId, audience }
Verified locally HS256 with DELEGATION_SECRET + aud + expiry

keelbase.client.base-url unset → only verify is available; obtain throws a clear configuration error.


2. KeelbaseAuditReporter — report Java-side actions

Configuration (keelbase.audit.*)

Points at the governance control plane (D2-3a). Unset base-url → reporter is disabled and only logs locally (same semantics as KeelBase's GovernanceReporter).

keelbase:
  audit:
    base-url: http://localhost:3001   # governance control plane root
    api-key: ${GOVERNANCE_API_KEY:}   # service identity (x-api-key header)

Usage

@Autowired KeelbaseAuditReporter audit;

audit.report(KeelbaseAuditEvent.builder()
        .userId("42")
        .username("alex")
        .action("compensation.followups.revoke")
        .detail("revoked followup id=7")
        .build());                    // source defaults to "java"; async, non-blocking, silent on failure

The event is sent as POST {base-url}/api/v1/external/audit with x-api-key, landing in the governance audit chain with source: "java". See the example app's FollowupController.revoke, which reports after a real compensation.


3. KeelbasePolicyClient — pulling the governance policy

Your business system can enforce the governance plane's real-time policy locally (tool toggles / confirmation / role allow-lists / audit granularity). Config reuses keelbase.audit.base-url + api-key (governance-plane service identity):

@Autowired KeelbasePolicyClient policy;

Optional<GovernancePolicy> opt = policy.fetch();
opt.ifPresent(p -> {
    // override (null = not overridden; fall back to your local default)
    Boolean confirmation = p.tools().get("create_followup") == null
            ? null : p.tools().get("create_followup").requiresConfirmation();
    String granularity = p.auditGranularity();   // "all" | "write" | "off"
});

Endpoint: GET /api/v1/external/governance/policy on the governance plane with x-api-key (same service identity as audit reporting). tools carries only overridden fields (partial override) — merge with your own tool defaults to get effective values. With base-url/api-key unset → Optional.empty() (all-local defaults); HTTP/parse failure throws KeelbaseClientException.


4. Side-effect status reverse query

Java-side reconciliation: is a given AI-initiated business action (e.g. followup/7) recorded as a side effect, and has it been revoked? Service identity hits the main app's GET /api/v1/external/effects/:resultType/:resultId:

// config: keelbase.client.base-url (KeelBase main app) + side-effect-api-key (= its GOVERNANCE_API_KEY)
@Autowired KeelbaseClient client;

SideEffectStatus s = client.querySideEffect("followup", 7);
if (!s.found()) {
    // no AI side effect for that action (not AI-created / gone) — treat as ordinary data
} else if (s.revoked()) {
    // revoked (local entity = target soft-deleted)
} else if (s.revokeHint() != null) {
    // B-path proxy_call: revocation goes through the Java compensation endpoint —
    // confirm the revoked state on the Java side (honest boundary)
}

Config (keelbase.client.*):

keelbase:
  client:
    base-url: http://localhost:3000            # KeelBase main app (same base as delegation token)
    side-effect-api-key: ${GOVERNANCE_API_KEY}  # x-api-key service identity the main app accepts

Semantics: local entities (event/todo/crm_task, etc.) — revoked = targetSoftDeleted is the truth; B-path proxy_call has no revoke column on the main-app effect row (revocation goes through the Java compensation endpoint) → revokeHint says "confirm on the Java side" rather than overclaiming. HTTP 404 → SideEffectStatus.notFound() (found=false); missing side-effect-api-key / ≥300 → throws KeelbaseClientException.


Related