@cleanstart/cleanlib-sdk
v0.5.1
Published
CleanLibrary JavaScript/TypeScript SDK — verdict + remediation + contract enforcement (envelope schema lockfile + canonical reason-codes).
Maintainers
Readme
@cleanstart/cleanlib-sdk
CleanStart CleanLibrary SDK for JavaScript and TypeScript. Verdict + remediation + contract enforcement across every developer surface (VS Code / Cursor / MCP tools / CLI gates).
Install
npm install @cleanstart/cleanlib-sdkQuick start
Verdict (App customer-verdict surface)
import { HttpVerdictClient } from "@cleanstart/cleanlib-sdk";
const verdict = new HttpVerdictClient("https://cleanapp.clnstrt.dev", {
bearer: process.env.CLEANLIB_API_KEY,
});
const v = await verdict.fetchVerdict("npm", "lodash", "4.17.21");
console.log(v.decision); // ALLOW | DENY | WARNRemediation (vector-verdict-api /remediation surface)
import { HttpRemediationClient } from "@cleanstart/cleanlib-sdk";
const remediation = new HttpRemediationClient({
bearer: process.env.CLEANLIB_REMEDIATION_BEARER,
// baseUrl defaults to https://vector-verdict-api-431643481641.us-central1.run.app
});
const result = await remediation.getRemediation("npm", "cors");
switch (result.kind) {
case "present":
// sparse 7-block composite: fix / upstream_lag / blast_radius /
// fix_trust / deadline / reachability / recommended_version
console.log(result.data.recommended_version);
break;
case "not_in_substrate":
console.log("No remediation data for this package.");
break;
case "transient":
console.error(result.error.message); // retry-after-backoff
break;
}Contract enforcement — verdict envelope + reason codes
import {
VerdictEnvelopeV1Schema,
ReasonCode,
type VerdictEnvelopeV1,
} from "@cleanstart/cleanlib-sdk";
const envelope: VerdictEnvelopeV1 = {
status: "DENY",
reason_code: ReasonCode.VERDICT_KEV_LISTED,
human_message: "CVE-2021-44228 is on the CISA KEV catalog.",
as_of: "2026-05-28",
};Surface renderers (hover, MCP tool result, CLI stderr) consume the same envelope. Surface-specific UX is allowed; surface-specific semantics are not.
Gate — verdict() / enforce() (CX-8)
Two ways to consume one assessment. A blocked verdict is a successful assessment, not an error — only couldn't get a verdict is the not_assessed arm, so a transport / coverage failure can never be mistaken for "no findings, proceed".
import { verdict, enforce, assessed, couldNotAssess } from "@cleanstart/cleanlib-sdk";
// `assessed(state)` = you got a verdict of some tier; `couldNotAssess(cause)` =
// you could not. Both build the Acquisition that verdict()/enforce() consume.
// 1. verdict() — RETURNS style. A Block is the `assessed` arm, never thrown.
const v = verdict(assessed("malicious"));
if (v.kind === "assessed") {
console.log(v.assessment.tier, v.assessment.exitCode, v.assessment.isAllowed);
// "block" 1 false
}
// "not assessed" is a distinct returned arm — still NOT clean:
const na = verdict(assessed("not_yet_assessed"));
// na.kind === "assessed", na.assessment.isAllowed === false, exitCode 2 (warn, fail-closed)
// A couldn't-get-a-verdict surfaces as the `not_assessed` arm — never silently clean:
const miss = verdict(couldNotAssess({
kind: "coverage_incomplete",
reasonCode: "SCAN_ABORTED",
message: "3 of 40 coordinates unreachable",
}));
// miss.kind === "not_assessed"
// 2. enforce() — gate use. `allow` IFF clean-to-proceed.
const gate = enforce(assessed("not_yet_assessed"));
switch (gate.kind) {
case "allow": break; // proceed
case "blocked": process.exit(gate.exitCode); // 1 (block) or 2 (warn)
case "not_assessed": process.exit(gate.exitCode); // fail-closed to 1 — a
// coverage failure never exits 0
}The same contract holds byte-for-byte in sdk-py, sdk-go, and the Rust cleanlib-client reference — all assert against the shared CX8_GATE_EXPECTED.json conformance fixture.
Public surface (v0.4.1)
| Export | Kind | Notes |
|---|---|---|
| HttpVerdictClient | class | App /v1/customer/verdicts/... + scan / audit / policyPreview / riskAccept / fetchBytes |
| HttpRemediationClient | class | vector-verdict-api /api/v1/remediation/:eco/:name with 5-min cache + 1 retry on 5xx |
| HttpEnrichClient | class | cleanlib-enrich client skeleton (verb methods land in v0.4.2) |
| VerdictEnvelopeV1Schema | const | JSON Schema draft 2020-12 lockfile |
| VerdictEnvelopeV1 | type | Derived from the schema via json-schema-to-ts |
| ReasonCode / ReasonCodeT | const + type | Canonical reason-code enum (15 entries) |
| RemediationResponse, RemediationBlock, RemediationOrAbsent | types | Sparse-by-design wire shape + discriminated union |
| CleanlibClient | class (deprecated) | Back-compat re-export of HttpVerdictClient; scheduled removal in v1.0.0 |
Errors: CleanlibError, TransportError, HttpError, VerdictNotFoundError.
Development
nvm use # uses .nvmrc → Node 22
npm install
npm test # vitest (67 unit + contract tests, 15 snapshots)
npm run typecheck
npm run buildLicense
UNLICENSED — proprietary; CleanStart Inc. / Triam Security commercial use.
