@bevingh/inlet-guard
v0.2.0
Published
Verifies inlet product-scope impersonation grants (offline, fail-closed) and reports session activity back to inlet. A third package, deliberately separate from @bevingh/sdk-node — this one carries a public key, never a secret event-ingest key, in the sam
Readme
@bevingh/inlet-guard
Verifies inlet product-scope impersonation grants —
offline, fail-closed — and reports session activity back to inlet. A
third package, deliberately separate from @bevingh/sdk-node:
sdk-node carries a secret key and sends events; this package carries a
public key and verifies grants, and those two credentials must never sit
in the same client. See INLET_API_CONTRACT.md
§5.10 for the full contract this package implements against, and
../../docs/GUARD_INTEGRATION.md for
the complete integration guide (including OAuth-based products) and a
real, runnable reference server
(docs/examples/reference-product-server.ts).
The architectural fact this is built around
inlet does not own your users, and it never mints a session in your auth system. An admin approves a request inside inlet's own dashboard; inlet signs a short-lived, single-audience grant and hands it to the approver; your backend verifies that grant here, offline, and mints your own session. inlet authorizes — your product mints.
Install
npm install @bevingh/inlet-guardThree entry points: the core (@bevingh/inlet-guard), an Express adapter
(@bevingh/inlet-guard/express), and a test kit
(@bevingh/inlet-guard/testing, dev-only).
Usage
import { createGuard } from "@bevingh/inlet-guard";
const guard = createGuard({
productId: "prd_your_product_id",
secretKey: process.env.INLET_SECRET_KEY!, // used only to report activity back
apiBaseUrl: "https://inlet-api.bevingh.com",
});
const context = await guard.verifyGrant(grantToken);
// { targetUserRef, impersonatorId, sessionId, scopes, expiresAt }Call verifyGrant exactly once per grant. Replay protection makes a
grant's jti genuinely single-use — treat it like an OAuth authorization
code (exchanged once, for your own session), not a bearer token you
re-present on every request. Full pattern, worked example, and the
Express-specific version of this in docs/GUARD_INTEGRATION.md.
await guard.reportSessionStart(context); // strict — throws if it can't be recorded
guard.reportAction(context, { // best-effort, retried locally
action: "invoice.refund_issued",
targetType: "invoice",
targetId: "inv_123",
after: { refundedAmount: 4200 },
});Express
import { expressMiddleware } from "@bevingh/inlet-guard/express";
app.post("/admin/impersonation/start", expressMiddleware(guard), (req, res) => {
// req.impersonation, already verified — mint your own session here.
});Testing your integration
import { generateTestKeyPair, createTestGrant } from "@bevingh/inlet-guard/testing";
import { verifyGrant } from "@bevingh/inlet-guard";
const keyPair = await generateTestKeyPair();
const token = await createTestGrant({
keyPair,
productId: "prd_your_product_id",
externalUserRef: "cus_test_user",
impersonatorId: "mem_test",
sessionId: "ips_test",
scope: ["support.read"],
});
const context = await verifyGrant(token, { productId: "prd_your_product_id", keys: keyPair.keySource });A real, fresh Ed25519 keypair is generated per call — never inlet's own signing key.
Scope
verifyGrant/createGuard never mint a session, never decide what a
verified grant is allowed to do inside your product, and never wrap an
OAuth server — see docs/GUARD_INTEGRATION.md's own sections on both.
See VERSIONING.md for this package's semver and
deprecation policy.
Development (in this monorepo)
pnpm --filter @bevingh/inlet-guard run generate # regenerate src/generated/openapi.ts
pnpm --filter @bevingh/inlet-guard run typecheck
pnpm --filter @bevingh/inlet-guard run test
pnpm --filter @bevingh/inlet-guard run build # what actually gets published (dist/)See .github/workflows/publish-inlet-guard.yml for the publish
workflow — gated behind a GitHub Environment requiring manual approval;
nothing publishes on its own.
