@genation/bridge-server
v0.4.4
Published
Server-side grant toolkit for Genation Bridge
Readme
@genation/bridge-server
Framework-neutral helpers for customer backends that authorize and sign Genation Bridge peer grants. The toolkit does not authenticate customer users or register HTTP routes.
import {
allowRequestedCapabilities,
createServer,
parsePeerGrantRequest,
} from "@genation/bridge-server";
const bridge = await createServer({
projectId: Deno.env.get("BRIDGE_PROJECT_ID")!,
secretKey: Deno.env.get("BRIDGE_SERVER_SECRET_KEY")!,
});
// Authenticate the customer user and apply business rules before this point.
const request = parsePeerGrantRequest(await requestBody());
const result = await bridge.issue(
request,
allowRequestedCapabilities(request),
{
authorization: {
subject: authenticatedUser.id,
details: [{ type: "documents", actions: ["read"] }],
},
},
);The optional third argument is trusted server input and is signed into grant.v2. Never
copy a client-authored authorization object into it. Keep subjects opaque and do not
include JWTs, cookies, secrets, or personal data: grant metadata is signed but is not
part of the E2EE invocation payload.
Use allowAllCapabilities(), allowRequestedCapabilities(),
filterRequestedCapabilities(), or denyPeer() to make the authorization decision
explicit. createGrantIssuer() is the advanced boundary for an approved custom signer
and trusted clock. A custom signer must sign the exact UTF-8 bytes of
JSON.stringify(claims) supplied by the issuer; altered serialization is rejected.
An advanced createGrantId provider must return a fresh ID for every issued grant;
runtime renewal rejects reuse of any grant ID previously admitted on the same live
connection. Reconnect before exceeding 4,096 distinct grant IDs on one connection.
Static tool catalog and business authorization
The customer backend stores the approved static catalog. After it authenticates a user
and evaluates its own business rules, it uses the same invoke policy to filter tool
discovery and issue the capability grant:
import {
createToolCatalog,
filterRequestedCapabilities,
} from "@genation/bridge-server";
const catalog = createToolCatalog(await loadApprovedToolRecords());
// Customer authentication and role/resource policy happen before these calls.
const access = await accessFor(authenticatedCustomerUser);
const available = catalog.filterByPolicy(access);
const request = parsePeerGrantRequest(await requestBody());
const policy = filterRequestedCapabilities(request, access);
const grant = await bridge.issue(request, policy);
return {
tools: available.tools,
invokeCapabilities: available.invokeCapabilities,
grant,
};createToolCatalog() validates metadata but does not register a route, persist state,
or inspect whether a provider peer is online. filterByPolicy() only returns tools
whose bound capability appears in policy.invoke; it does not authenticate a user or
issue a grant. Non-tool capabilities in policy.invoke remain available to direct
peer.invoke() calls.
Portable agent registry
The reference registry validates metadata-only proposals and enforces immutable version transitions. Customer code must authenticate the creator/admin/consumer and make the business-policy decision before each operation:
import { createAgentRegistry } from "@genation/bridge-server";
const agents = createAgentRegistry({ projectId: bridge.projectId });
// Creator JWT and project membership were checked by customer code.
const pending = agents.submit(proposalFromPeer);
// Admin JWT and review authority were checked independently.
const approved = agents.review(pending.reference, "approve");
// Consumer agent ACL is separate from tool discovery and its grant.
const visible = agents.listApproved(agentIdsVisibleToCustomerUser);
const loaded = agents.loadApproved(
approved.reference,
agentIdsUsableByCustomerUser,
);createAgentRegistry() is an in-memory reference implementation for examples, tests,
and customer adapters. A production backend must persist records durably with equivalent
atomic version/transition semantics. The registry does not authenticate JWTs, infer
admin rights, grant any referenced capability, store a model run, or keep a creator peer
online. Published instructions are readable customer control-plane metadata; never put
credentials, grants, payloads, results, or runtime state in them.
Routing limitation
Capability permissions authorize actions; they do not bind a customer subject, target
peer allowlist, room, or routing scope. A peer naming convention such as
user-123.desktop remains customer application policy and is not a Bridge-enforced
same-user security boundary. Authorization details are also customer-defined
constraints; they never widen or narrow the SDK's capability permission checks.
Keep the server signing secret only in the customer backend. Never place it in a peer, browser bundle, desktop renderer, or mobile application.
