← klaro.services
SentinelConsentraKlaroShieldBundlesPricing

Middleware Reference

Every option, default, and behavior -- checked against the source in src/middleware/.

← Back to Docs

retries(options?)

Exponential backoff with full jitter. Retries only 429 and 5xx by default (plus ECONNRESET/ETIMEDOUT/ECONNREFUSED network errors) -- a 400 or 401 is never retried, since burning a rate-limit budget on a call that will never succeed is worse than failing fast.

OptionTypeDefaultNotes
maxnumber3Total attempts including the first -- max: 3 means up to 2 retries.
backoff"exponential" | "fixed""exponential"Exponential doubles the delay each attempt; fixed uses baseDelayMs every time.
baseDelayMsnumber500Base delay for the first retry.
maxDelayMsnumber10000Caps exponential growth so it can't run away.
isRetryable(error) => boolean429/5xx + network errorsOverride to match a provider's own non-standard error shape.
import { retries } from "@klaroshield/sdk";

const r = retries({ max: 3, backoff: "exponential" });

budget(options)

Estimates cost from the provider's own usage field against a small built-in pricing table (gpt-4o-mini, gpt-4o, gpt-4-turbo, gpt-3.5-turbo, claude-3-5-sonnet, claude-3-5-haiku, claude-3-opus), persists spend to .klaro/budget.jsonl. Checked BEFORE each call using spend from prior calls -- it can't know the cost of the call about to happen, only enforce the cap based on everything so far.

OptionTypeDefaultNotes
maxMonthlyUsdnumberrequiredMonthly spend cap.
storage"local""local"Only "local" exists today -- Klaro Cloud can later sync budgets across machines.
onExceeded(spentUsd, capUsd) => voidthrowsCalled instead of thrown when the cap is hit, so you decide what happens.
import { budget } from "@klaroshield/sdk";

const b = budget({
  maxMonthlyUsd: 50,
  onExceeded: (spent, cap) => console.warn(`Over budget: $${spent}/${cap}`),
});

secrets(options?)

Deep-scans every call for OpenAI/Anthropic/AWS/GitHub keys, JWTs, and generic bearer tokens -- deliberately narrow, high-confidence patterns only. A scanner with a high false-positive rate trains developers to ignore it, which is worse than not having one.

OptionTypeDefaultNotes
mode"mask" | "block""mask"mask replaces matches with a placeholder before the call goes out; block throws instead, so the call never sends at all.
import { secrets } from "@klaroshield/sdk";

const s = secrets({ mode: "block" }); // throws instead of masking

pii(options?)

Deep-scans for email, phone (US format), SSN, and credit card numbers (real issuer prefix/length rules, not just "16 digits in a row").

OptionTypeDefaultNotes
mode"mask" | "block""mask"Same semantics as secrets().
typesPiiType[]all fourRestrict to specific types, e.g. types: ["email", "phone"].
import { pii } from "@klaroshield/sdk";

const p = pii({ mode: "mask", types: ["email", "phone"] });

validation(options)

Re-runs the WHOLE call (not just re-parsing) if the response isn't valid JSON or fails your schema's safeParse -- LLM non-determinism is often fixed by a fresh attempt, not a re-parse of the same broken output. Duck-typed against Zod's shape, so Zod itself isn't a hard dependency.

OptionTypeDefaultNotes
schema{ safeParse(data): ... }requiredAny object with a Zod-shaped safeParse method.
extractText(result) => string | undefinedtries choices[0].message.content, content[0].text, textOverride for a response shape the default doesn't recognize.
maxRetriesnumber2How many times to retry on parse/validation failure.
import { validation } from "@klaroshield/sdk";
import { z } from "zod";

const v = validation({ schema: z.object({ total: z.number() }) });

logging(options?)

Persisted to .klaro/logs.jsonl regardless of display format -- klaro stats/explain/dashboard read this file, so format: "silent" means "don't print to stdout," not "don't record anything."

OptionTypeDefaultNotes
format"pretty" | "json" | "silent""pretty"pretty is colored stdout; json prints one JSON object per line; silent prints nothing but still persists.
import { logging } from "@klaroshield/sdk";

const l = logging({ format: "json" }); // pipe to your own log aggregator