> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rubric-app.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How it works

> What the Rubric SDK does in your process and what it sends over the wire.

You install the Rubric SDK in the same process as your agent. It runs three background tasks and exposes a single hot-path call (`evaluate()`) that your tool dispatcher invokes.

```
┌─────────────────────────────────┐
│  Your agent process             │
│                                 │
│  ┌─────────────────────────┐    │
│  │ Rubric SDK              │    │
│  │   evaluator             │    │
│  │   bundle cache  ⟵ poll  │ ─→ Rubric API
│  │   audit sink    ⟶ batch │ ─→ Rubric API
│  │   identity      ⟶ JWT   │ ─→ Rubric API
│  └─────────────────────────┘    │
│         ▲                       │
│         │ in-process            │
│  ┌──────┴──────┐                │
│  │  Tool call  │                │
│  └─────────────┘                │
└─────────────────────────────────┘
```

## What the SDK does

The SDK has three responsibilities, all of which run in your process without blocking your tool dispatch.

### Evaluator

`evaluate()` is sub-millisecond — it interprets your policy bundle against the tool call's metadata and returns `allow` / `deny` synchronously, without a network round-trip on the hot path. Bundles are pre-compiled when received so regex evaluation is a constant-time operation per call.

### Bundle cache

A background poller refreshes the bundle every 30 seconds. The bundle is kept in memory and used for every evaluation until the next poll succeeds. If Rubric is unreachable, the SDK keeps using the last-known-good bundle — your agent continues to enforce policy even during a network outage.

The bundle is content-hashed; the poller sends `?since=<hash>` so unchanged bundles return `304 Not Modified` instead of re-downloading. Incoming bundles are also checked for version monotonicity: a bundle whose `bundleVersion` is lower than the cached one, or whose `builtAt` is significantly older, is rejected to defeat rollback attempts.

### Audit sink

Every `evaluate()` enqueues an audit event into an in-memory queue. A background flush task batches events and ships them — default cadence is every second or 100 events, whichever comes first. Your code never waits on the network.

If the queue fills (default cap: 10,000 events) or repeated ship failures exhaust retries, events are dropped and a counter increments. The counters are surfaced through `getStats()` so your monitoring can alarm on silent drops.

### Identity

On startup the SDK exchanges your enrollment token for a short-lived (60 minute) signed identity. A background task refreshes it ahead of expiry. The signed identity is what authenticates the bundle pulls and audit uploads — your enrollment token is only used at boot.

## What gets sent over the wire

The SDK makes three classes of HTTPS request:

| Request                                    | Purpose                                                                        |
| ------------------------------------------ | ------------------------------------------------------------------------------ |
| `POST /v1/identities/enroll` / `…/refresh` | Exchange the enrollment token for a signed identity; refresh it before expiry. |
| `GET /v1/bundle?since=<hash>`              | Pull the latest policy bundle scoped to this agent.                            |
| `POST /v1/events`                          | Ship batched audit events.                                                     |

That's the whole egress surface. There is no telemetry, no anonymous usage reporting, and no other endpoint the SDK calls.

## Trust model

| Credential                 | Issued to                                | Can do                                                              | Cannot do                                                                                                           |
| -------------------------- | ---------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Enrollment token (`enr_…`) | Operators, via the dashboard             | Bootstrap a new agent's identity                                    | Ship audit events, read bundles, impersonate an existing agent                                                      |
| Signed identity (60 min)   | The SDK at boot, refreshed automatically | Pull the agent's bundle; ship audit events attributed to that agent | Impersonate a different agent — Rubric rejects any event whose agent id doesn't match the identity it was sent with |

Enrollment tokens are designed to be safe to commit to your deployment manifest. They can only register; if leaked the worst-case impact is "attacker creates an extra agent row in your org," which is revocable from the dashboard.

## Failure modes

| Failure                     | Behavior                                                                                                                                  |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Rubric API unreachable      | SDK uses last-known-good bundle. Audit events queue in memory and flush on recovery.                                                      |
| Bundle stale                | The SDK exposes `lastPullAt` and `lastBundleChangeAt` so your harness can enforce a max-age if you want to fail closed after a threshold. |
| Identity expires mid-outage | The refresh task retries with backoff. The still-valid identity continues working until it expires.                                       |
| Process restart             | Enrollment is idempotent on `agentName` — restarting returns the same agent id with a fresh signed token.                                 |
| Identity revoked            | Enrollment is rejected. Operator must un-revoke from the dashboard or pick a different `agentName`.                                       |
