@enfinitos/sdk-operator-web
EnfinitOS Operator Web SDK — a React component library that tenants embed in their own admin UIs to surface the EnfinitOS operator surfaces (rights registry, proof centre, compliance flows, billing, pacing, pilots) without rebuilding the dashboards that ship in apps/web/app/_workspace/.
Who should use this. Any tenant who: - already has an admin UI (Cloudflare-style, AWS-Console-style, or a custom React-based shell) and wants to drop EnfinitOS panels inside their chrome rather than iframe a third-party dashboard; - has their own identity/SSO and wants to issue short-lived JWTs to the SDK without going through the platform's login flow; - prefers a small, themeable, white-label-ready component library over a hosted Workspace experience.
Architecture
┌──────────────────────────────────────────────────┐
│ Tenant Admin UI │
│ (React/Next.js/Remix/Vite — host's choice) │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ <OperatorProvider> │ │
│ │ client = new EnfinitOSOperatorClient(…) │ │
│ │ │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ ThemeProvider (CSS variables, │ │ │
│ │ │ tenant-overridable tokens) │ │ │
│ │ │ │ │ │
│ │ │ <RightsRegistryPanel/> │ │ │
│ │ │ <ProofExplorer/> │ │ │
│ │ │ <ProofVerifier │ │ │
│ │ │ auditorVerify={…}/> │ │ │
│ │ │ <PacingDashboard/> │ │ │
│ │ │ <UsageMeter/> │ │ │
│ │ │ <DsarRequestForm/> │ │ │
│ │ │ <PilotEnrolmentFlow/> │ │ │
│ │ └────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────┘ │
│ │ │
└───────────────────────┼──────────────────────────┘
│ HTTPS, JWT auth
│ X-Org-Id, Accept-Version
▼
┌──────────────────────────────────────────────────┐
│ EnfinitOS Platform API │
│ │
│ /v1/rights /v1/proof │
│ /v1/offers /v1/compliance/{dsar,…} │
│ /v1/challenges /v1/billing/{invoices…} │
│ /v1/consent /v1/pacing/health │
│ /v1/pilot/enrolment │
└──────────────────────────────────────────────────┘
The SDK has no compile-time dependency on apps/api, apps/web, or any other EnfinitOS package. It speaks the public REST contract via Accept-Version negotiation and re-states the contract types in src/types.ts so tenants can install the SDK without dragging in the platform's internal schemas.
Installation
pnpm add @enfinitos/sdk-operator-web
# or: npm install @enfinitos/sdk-operator-web
Peer dependencies (provided by the host):
react^18.0.0 || ^19.0.0react-dom^18.0.0 || ^19.0.0
The SDK pulls in zero runtime UI libraries — no MUI, no Chakra, no Ant Design, no Tailwind. Components ship with inline styles driven by CSS variables; tenants override those variables to brand the SDK to their own chrome.
5-minute getting started
import {
EnfinitOSOperatorClient,
OperatorProvider,
RightsRegistryPanel,
} from "@enfinitos/sdk-operator-web";
const client = new EnfinitOSOperatorClient({
apiBaseUrl: "https://api.enfinitos.com",
orgId: "org_acme",
authToken: () => fetchYourJwt(), // sync or async
// optional:
timeoutMs: 10_000,
retries: 3,
});
export function AdminRightsPage() {
return (
<OperatorProvider client={client}>
<RightsRegistryPanel />
</OperatorProvider>
);
}
That's it. The panel paginates, filters by status / substrate, opens a detail card on click, and exposes lifecycle actions (suspend / resume / revoke) inline.
Theming
The SDK reads a flat dictionary of CSS variables prefixed with --enfinitos-. Override any subset via the tokens prop on <OperatorProvider> or <ThemeProvider>:
<OperatorProvider
client={client}
tokens={{
"--enfinitos-color-brand": "#0066cc",
"--enfinitos-color-brand-fg": "#ffffff",
"--enfinitos-color-text": "#1a1a1a",
"--enfinitos-color-surface": "#ffffff",
"--enfinitos-color-surface-alt": "#f4f6f8",
"--enfinitos-color-border": "#e2e8f0",
"--enfinitos-font-family": "Inter, sans-serif",
}}
>
…
</OperatorProvider>
CSS-variable reference
| Token | Default | Purpose |
|---|---|---|
--enfinitos-color-brand | #2563eb | Primary brand colour — buttons, links, focus accents. |
--enfinitos-color-brand-fg | #ffffff | Text on top of brand. |
--enfinitos-color-accent | #7c3aed | Secondary highlight (rare). |
--enfinitos-color-success | #16a34a | StatusBadge, success rows. |
--enfinitos-color-warning | #d97706 | Warning state. |
--enfinitos-color-danger | #dc2626 | Errors, destructive actions. |
--enfinitos-color-info | #0284c7 | Info chips. |
--enfinitos-color-neutral | #64748b | Neutral chips. |
--enfinitos-color-bg | #0f172a | Page background (if SDK is the root). |
--enfinitos-color-surface | dark glass | Card surface. |
--enfinitos-color-surface-alt | dark glass | Alt rows / hover. |
--enfinitos-color-border | dim slate | Borders. |
--enfinitos-color-text | #e2e8f0 | Primary text. |
--enfinitos-color-text-muted | #94a3b8 | Secondary text. |
--enfinitos-color-text-inverse | #0f172a | Text on inverse surfaces. |
--enfinitos-font-family | system stack | Body font. |
--enfinitos-font-family-mono | mono stack | IDs / hashes. |
--enfinitos-font-size-{xs,sm,md,lg,xl} | 11–20px | Typography scale. |
--enfinitos-font-weight-{regular,medium,bold} | 400/500/600 | Font weight. |
--enfinitos-space-{0..6} | 0–32px | Spacing scale. |
--enfinitos-radius-{sm,md,lg} | 6/10/14 | Radius scale. |
--enfinitos-shadow-{sm,md,lg} | layered | Drop-shadows. |
--enfinitos-focus-ring | brand glow | Focus outline. |
--enfinitos-motion-{fast,medium} | 120/240ms | Animation timing. |
Tenants who already use design tokens (Tailwind, Radix Colors, a custom system) typically write a one-liner mapping their token names onto --enfinitos-* and inherit their brand automatically.
Component catalogue
Rights Registry
| Component | What it renders |
|---|---|
<RightsRegistryPanel/> | Top-of-page panel: status-counts header, filter strip (status + substrate + search), paginated list (<DataTable/>), per-row click handler. Suspends to <RightDetailCard/> on selection. |
<RightDetailCard/> | Full right detail: status badge, scope label, basis link, full rules dump, provenance chain, action buttons (suspend / resume / revoke). |
<ComposeRuleForm/> | Rule-composition form covering every BehaviourRule kind (max-speed, exclusion zones, age-gate, consent-required, territory restrict, custom). Used to issue rights or counter-offer. |
<ChallengeInbox/> | Open challenges list, per-row resolve / withdraw, rationale textarea. |
Proof Centre
| Component | What it renders |
|---|---|
<ProofExplorer/> | Paginated proof-record list with filter by type + rightId, expandable per-row payload viewer, signature preview. |
<ProofExportButton/> | One-click signed-export trigger; opens the resulting archive URL when ready. |
Verification Centre
| Component | What it renders |
|---|---|
<ProofVerifier/> | Server-side verification result for a chain. If a auditorVerify callable is provided (typically @enfinitos/sdk-auditor), it runs a second-opinion verification client-side and displays both results. |
Compliance
| Component | What it renders |
|---|---|
<DsarRequestForm/> | GDPR Art. 15 (Data Subject Access Request) form: subject ref, note, submit + status polling. |
<ErasureRequestForm/> | GDPR Art. 17 (erasure) form. Surfaces BLOCKED reason when legal basis conflicts. |
<ConsentRecordsList/> | Paginated subject-consent list, revocation action inline. |
Pacing
| Component | What it renders |
|---|---|
<PacingDashboard/> | Grid of <DeliveryHealthCard/> per scope, auto-refreshes on a tenant-configurable interval. |
<DeliveryHealthCard/> | Single scope: pace index, render success rate, p95 resolve latency, pending impressions. Colour-coded by paceIndex. |
Billing
| Component | What it renders |
|---|---|
<UsageMeter/> | Bar chart for usage meters with used / included / cap. Shows percentage and remaining budget. |
<InvoiceList/> | Paginated invoice table, status badge, due-by, line items expandable. |
<SettlementBreakdown/> | Per-settlement split table: party, percentage, amount. |
Pilot
| Component | What it renders |
|---|---|
<PilotEnrolmentFlow/> | Multi-step enrolment form for first-time tenants: cohort selection, contract upload, primary-contact details. |
<PilotCohortBadge/> | Single pill conveying the current pilot cohort + tier. |
Shared building blocks (composable on their own)
| Component | What it renders |
|---|---|
<Card/> | The SDK's analogue of cp-glass-card — section label, title, subtitle, toolbar slot, body. |
<DataTable/> | Typed, accessible table: column renderers, sort indicators, keyboard navigation, empty state. |
<StatusBadge/> | Status pill with colour + dot. Includes a statusKindFor(status) helper to map domain enums. |
<SignedEvidenceCard/> | Surfaces a signed export: digest, signer, signature preview, download link. |
<AsyncBoundary/> | Loading / error / data renderer for the SDK's async hooks. |
<EmptyState/> | Title + body + CTA for zero-row lists. |
API client reference
Top-level construction:
const client = new EnfinitOSOperatorClient({
apiBaseUrl: "https://api.enfinitos.com",
orgId: "org_acme",
authToken: "ey…", // or () => Promise<string>
timeoutMs: 10_000,
retries: 3,
fetchImpl: customFetch, // optional override
userAgentTag: "acme-admin/1.2.3", // optional, appended to X-Operator-Web-SDK
});
Sub-API surface:
client.rights → list / get / issue / suspend / resume / revoke /
statusCounts / family / provenance / listBases
client.offers → listInbox / get / propose / accept / reject /
counter / withdraw
client.challenges → listOpen / get / open / resolve / withdraw
client.proof → list / get / chain / verify / exportSigned
client.consent → list / get / issue / revoke / check
client.compliance → listDsar / getDsar / fileDsar / listErasure /
fileErasure
client.billing → listInvoices / getInvoice / listSettlements /
getSettlement / listMeters
client.pacing → listHealth / getHealth
client.pilot → getEnrolment / enrol / listCohorts
Every method returns the unwrapped data from the platform's envelope; non-2xx responses throw an OperatorApiError carrying the platform's RFC-7807-ish code/message/details triple.
Retry policy
- 5xx and 429 responses retry up to
retriestimes (default 3). - 4xx responses (other than 429) are fatal — they throw immediately.
- Backoff is exponential with a small jitter to spread retry storms.
- A network/timeout error is treated as retryable.
Auth
authToken accepts either a static string or a callable returning a string | Promise<string>. The callable form is invoked on every request so a tenant can rotate tokens without reconstructing the client (and it slots cleanly into OAuth-refresh flows).
Cancellation
Every request races an AbortSignal against the configured timeoutMs. The hook layer (useAsync) also wires an abort signal so deps-changes cancel in-flight calls.
Hooks
For tenants that want the SDK's React-flavoured data layer rather than rolling their own:
useRightsList(filter) // → AsyncResult<Page<Right>>
useRight(rightId) // → AsyncResult<{ right, provenance } | null>
useRightFamily(rightId) // → AsyncResult<{ ancestors, descendants } | null>
useRightsStatusCounts() // → AsyncResult<Record<RightStatus, number>>
useProofList(opts) // → AsyncResult<Page<ProofRecord>>
useProofChain(headProofId) // → AsyncResult<ProofChain | null>
useConsentList(subjectRef) // → AsyncResult<Page<ConsentRecord>>
useConsentCheck(input) // → AsyncResult<{ granted, consentId }>
useInvoices(opts) // → AsyncResult<Page<Invoice>>
useSettlements(opts) // → AsyncResult<Page<Settlement>>
useUsageMeters() // → AsyncResult<Page<UsageMeter>>
usePacingHealth(opts) // → AsyncResult<DeliveryHealth[]>
Every hook returns { data, error, loading, refreshing, refetch }, shaped to plug into <AsyncBoundary state={…}> without ceremony.
Tenants who want raw control reach for useOperatorClient() and hit the typed client directly.
Sample integration — minimal viable operator dashboard
A complete, copy-pasteable 30-line dashboard:
import {
EnfinitOSOperatorClient,
OperatorProvider,
RightsRegistryPanel,
ProofExplorer,
PacingDashboard,
UsageMeter,
InvoiceList,
DsarRequestForm,
} from "@enfinitos/sdk-operator-web";
const client = new EnfinitOSOperatorClient({
apiBaseUrl: process.env.NEXT_PUBLIC_ENFINITOS_API!,
orgId: process.env.NEXT_PUBLIC_ENFINITOS_ORG!,
authToken: async () => (await fetch("/api/jwt")).text(),
});
export default function OperatorDashboard() {
return (
<OperatorProvider client={client} tokens={{ "--enfinitos-color-brand": "#0066cc" }}>
<div style={{ display: "grid", gap: 24, padding: 24 }}>
<RightsRegistryPanel />
<PacingDashboard refreshIntervalMs={30_000} />
<UsageMeter />
<InvoiceList />
<ProofExplorer />
<DsarRequestForm />
</div>
</OperatorProvider>
);
}
Verification gate
Components that render signed evidence (proof records, signed exports) accept an optional auditorVerify prop typed against @enfinitos/sdk-auditor. The SDK does NOT pull the auditor as a hard dep — tenants on edge-runtime hosts (Cloudflare Workers, Vercel Edge) opt-in only when they have the crypto primitives available.
import { verifyChain } from "@enfinitos/sdk-auditor";
<ProofVerifier headProofId="p_abc123" auditorVerify={verifyChain} />
What's SDK-side vs platform-side
| Concern | This SDK | Platform |
|---|---|---|
| REST contract types | mirrored verbatim in src/types.ts | source-of-truth in apps/api/src/modules/*/contracts |
| Auth — JWT issuance / SSO | NOT part of SDK | reuses the platform's /auth/* plane |
| Theming / branding | tenant-overridable CSS variables | n/a (platform's Workspace is a separate skin) |
| Pacing data | renders via client.pacing.listHealth() | apps/api/src/modules/pacing/routes.ts |
| Audit log persistence | the platform writes; the SDK reads | platform-owned |
| Offline proof verification | optional via auditorVerify prop + @enfinitos/sdk-auditor | platform also verifies on /v1/proof/:id/verify |
Accessibility
- Every interactive element has an explicit
aria-labelor visible label. - All clickable rows are keyboard-activatable (
Enter/Space) withtabIndex=0androle="button". - Status badges use
role="status"with a sensiblearia-label. - Focus rings honour
--enfinitos-focus-ringso tenants can keep brand contrast. - Async boundaries announce loading + error states via
role="status"androle="alert"so screen readers track state changes.
Versioning
- The SDK ships
SDK_VERSION(semver) andCONTRACT_VERSION(an ISO date matching the platform's accepted contract). Every REST call sends both as headers (X-Operator-Web-SDK,Accept-Version). - The platform pins responses to the negotiated contract version — a newer platform never returns shapes the SDK can't parse.
See also
packages/sdks/auditor-ts— independent signature verifier you can wire into<ProofVerifier auditorVerify={…}/>.packages/sdks/robotics-ts— the reference SDK for the ROBOTICS substrate (this Operator SDK speaks the sameBehaviourRuleunion).apps/web/app/_workspace/— first-party operator dashboard rendered against the same API surface; useful as a worked example.docs/launch/substrate-readiness-matrix.md— per-substrate readiness status.