@b1-road/types
v0.1.0-alpha.17
Published
Shared types and constants for every Eduzz Plat SDK — entities, permission algebra, hosted API URLs.
Downloads
822
Readme
@b1-road/types
Shared types and constants for every Road SDK — the single source of truth
for the Road wire contract. Entities, the permission algebra, input shapes,
hosted API URLs, and the API contract version live here; @b1-road/react,
@b1-road/nestjs, and every other binding re-export from this package and never
redefine.
Alpha. Road is pre-1.0. Install it plainly (
npm i @b1-road/types) — thelatestdist-tag tracks the newest release. The surface may change between minor versions until the API graduates its contract fromalphatov1.
New to Eduzz Plat?
Plat is Eduzz's platform layer — login, roles and permissions, and the business units your app builds on. This package is one piece of it; start here.
The fastest way in is to let your AI coding agent drive the integration:
claude mcp add road --scope user -- npx -y -p @b1-road/mcp road-mcpThat gives the agent the full integration guide plus tools to register a platform and issue its credentials.
Prefer to click through it? Create a platform in the
Dev Portal. Prefer a scaffold in your
repo? Run npx @b1-road/integrate.
Install
npm install @b1-road/typesShips dual ESM/CJS builds with type declarations. Zero runtime dependencies.
What's inside
| Export | What it is |
| --- | --- |
| ROAD_API_CONTRACT | The API contract version ("alpha"). SDKs build their base path as `${host}/api/${ROAD_API_CONTRACT}`. |
| ROAD_ENVIRONMENTS | Every hosted Eduzz Plat surface (api, authServer, devPortal) for sandbox and production. The source of truth every Road SDK reads from. |
| ROAD_API_URLS | Just the API base URLs, derived from ROAD_ENVIRONMENTS. |
| hostedEnvironment(env) | The whole surface record for an environment, or undefined. |
| hostedApiUrl(env) | Shorthand for that record's api. |
| hostedMatch(url) | Which environment and which surface a URL is, by origin, or undefined. Reporting the surface is what lets a BFF refuse an API URL pasted into the issuer slot instead of failing later at OIDC discovery. |
| environmentOf(url, surface?) | Just the environment from hostedMatch, optionally requiring a given surface. The BFF SDKs use it to refuse a config that mixes the two instances. |
| Entities | The resource shapes returned on the wire (business units, members, roles, permissions, …). |
| Inputs | The request-body shapes the API accepts. |
| Permissions | The action:subject permission algebra and its constants. |
| Webhooks | Webhook event types, per-event payloads, and the delivery envelope (RoadWebhookEvent, RoadWebhookPayloads, ROAD_WEBHOOK_EVENT_TYPES). |
| @b1-road/types/iam | IAM control-plane types (scopes, assignments, authorization, sessions). |
| @b1-road/types/bridge | Platform Bridge — grants, contracts, the RFC 8693 token exchange, and the audit query. |
| @b1-road/types/extensions | Platform Extensions — the catalogue, installs, targets, and the data-leg exchange. |
| hostedApiUrl | The hosted API base URL for an environment, or undefined if there isn't one. |
| Webhook signing | The canonical HMAC contract both sides agree on — buildWebhookSignedMessage, the four WEBHOOK_* headers, WEBHOOK_SIGNATURE_PREFIX and WEBHOOK_SIGNING_VECTOR. See below. |
import { ROAD_API_CONTRACT, ROAD_API_URLS } from "@b1-road/types";
const env = process.env.ROAD_ENV === "production" ? "production" : "sandbox";
const baseUrl = `${ROAD_API_URLS[env]}/api/${ROAD_API_CONTRACT}`;
// sandbox → https://api.road-sandbox.b1.app/api/alpha
// production → https://api.plat.eduzz.com/api/alphaWebhook signing — one contract, both sides
Road signs every delivery with HMAC-SHA256 over the timestamp and the exact
request body bytes, joined by a literal .:
import {
buildWebhookSignedMessage,
WEBHOOK_SIGNATURE_HEADER,
WEBHOOK_TIMESTAMP_HEADER,
WEBHOOK_SIGNATURE_PREFIX,
} from "@b1-road/types";
const message = buildWebhookSignedMessage(timestamp, rawBody);
// signature = "sha256=" + HMAC_SHA256(secret, message), lowercase hex| Export | What it is |
| --- | --- |
| buildWebhookSignedMessage(timestamp, rawBody) | Builds the exact string that gets HMAC'd. Both producer and consumer must construct it this way. |
| WEBHOOK_SIGNATURE_HEADER | x-road-signature — carries sha256=<hex>. |
| WEBHOOK_TIMESTAMP_HEADER | x-road-timestamp — unix seconds, as a string, not milliseconds. |
| WEBHOOK_EVENT_HEADER | x-road-event — mirrors the envelope's event. |
| WEBHOOK_DELIVERY_ID_HEADER | x-road-delivery-id — mirrors the envelope's id; deterministic, so it doubles as the dedupe key. |
| WEBHOOK_SIGNATURE_PREFIX | sha256=, the algorithm prefix on the header value. |
| WEBHOOK_SIGNING_VECTOR | A frozen (rawBody, secret, timestamp) → signature triple. Both the API's signer and every SDK's verifier assert against it, so neither can drift. |
⚠️ Verify over the raw bytes you received, never over a re-serialization of
the parsed body. Re-stringifying can reorder keys or alter whitespace and
unicode, which breaks the HMAC for reasons that look like a wrong secret. In
NestJS that means rawBody: true.
You usually do not need any of this. @b1-road/nestjs ships
RoadWebhookController, which verifies in constant time for you. These exports
exist for a binding that has no SDK yet, and for the two sides to be checkable
against one definition.
A contract package — the API is a consumer too
Every wire type, permission constant, and hosted URL has exactly one definition.
SDKs import them; they never re-declare. The Road API depends on this package
as well — not as "an SDK the API consumes," but as the contract both sides
agree on: the API (the producer) declares the wire shapes here and conforms its
DTOs to them, while the SDKs (the consumers) read the same shapes. The dependency
direction API → @b1-road/types is correct and intended. If a binding or the API
needs a shape this package doesn't yet expose, it contributes the shape back here
in the same change — so the API, React, Nest, and Laravel definitions can never
drift. This is the load-bearing principle of the ecosystem.
Versioning
@b1-road/types carries its own semantic version, independent of the Road API's
release cadence. It tracks the API on the contract axis: while
ROAD_API_CONTRACT is "alpha", this package stays pre-stable 0.x. The bump
to 1.0.0 happens when the API cuts its first stable contract (v1).
License
MIT
