Hosting Several Protocols
A server hosts one primary protocol and any number of additional
application protocols, fixed when it is constructed (WIRE_PROTOCOL.md §3.1).
Every request names its protocol in vgi_rpc.protocol, so method names may
repeat across protocols without colliding.
import { Protocol, serveTcp, str, VgiRpcServer } from "@query-farm/vgi-rpc";
const app = new Protocol("acme.App.v1", { protocolVersion: "1.0.0" });const reports = new Protocol("acme.Reports.v1").unary("render", { params: { id: str }, result: { result: str }, handler: ({ id }) => ({ result: `report ${id}` }),});
const server = new VgiRpcServer(app, { protocols: [reports] });
// The same host object serves every transport:await server.run(); // stdio// await serveTcp(server, { port: 0 }); // raw TCP// await serveUnix(server, { unixPath }); // AF_UNIX// await serveStream(server, { readable }); // any byte-stream pair// createHttpHandler(server, { ... }); // HTTP / Cloudflare WorkersRules:
- Fixed for the server’s life. Register everything in the constructor
(
protocols,identity). Once a transport starts serving,addProtocolthrows, so reflection output and everyprotocol_hashstay stable. - The same set on every transport. Hand the one
VgiRpcServerto each launcher; a launcher given a built server refuses server-level options (serverId,dispatchHook, …) — they belong on the server. - Order is registration order, primary first.
list_protocolsreports application protocols in that order; framework protocols (vgi_rpc.Reflection.v1,vgi_rpc.Identity.v1) follow. - No reserved names. A name starting
vgi_rpc.is refused, whether it is the binding name or the protocol’s own name. Names must be unique. - The protocol is the unit of optionality. There is no way to host a subset of a protocol’s methods; make an optional capability its own protocol.
- Versions are per binding. Each protocol’s
protocolVersiongates only its own methods.
Identity
Section titled “Identity”vgi_rpc.Identity.v1 is framework-owned and opted into with the identity
option:
import { IdentityImpl, VgiRpcServer } from "@query-farm/vgi-rpc";
const server = new VgiRpcServer(app, { identity: new IdentityImpl({ resolveToken: async (token) => lookup(token), // null = unknown credential introspectPrincipals: ["edge-proxy"], // required with resolveToken }),});For “I could not find out” (a store outage), throw AuthUnavailableError from
the hook: it is translated to identity_unavailable with its retry hint
(vgi_rpc.RetryInfo). Host identity only where the transport authenticates
callers (HTTP).
Grants and bearer credentials
Section titled “Grants and bearer credentials”issue_grant mints a credential meant to be presented later as an ordinary
bearer, and resolveToken resolves opaque bearers — so both feed back into HTTP
authentication (WIRE_PROTOCOL.md §16):
- Sealed grants, opt-in. Set
VGI_RPC_GRANT_KEYS(comma-separated standard base64, exactly 32 bytes each; the first mints, all verify), or passnew VgiRpcServer(app, { grantKeys: GrantKeys.parse([...]) }). The framework then hostsissue_grant(unless yourIdentityImplsuppliesmintGrant) and accepts its ownvgig1.tokens as bearers. OptionalVGI_RPC_GRANT_AUDIENCEandVGI_RPC_GRANT_MAX_TTL_SECONDS(default 7 days). No key, no change; a malformed key fails construction. resolveTokenas a bearer authenticator. A hosted identity withresolveTokenhas bearers your ownauthenticatedid not accept resolved through it: an identity authenticates (domaintoken),nullis 401, andAuthUnavailableError/IdentityUnavailableErroris 503 withRetry-After.- Order. Your
authenticate→ sealed grants →resolveToken. A badvgig1.token is 401 and never reachesresolveToken. - A grant-authenticated caller has no
auth_time, so it cannot mint another grant (stale_auth). - Sealed grants are not individually revocable: keep the maximum lifetime short; removing a key revokes all its grants.
- If your authentication depends on proxy-injected evidence (
proxyProofRequired,proxyAuthHeaders), the handler refuses to OR these in: composegrantAuthenticate/resolveTokenAuthenticateyourself and passidentityBearer: false.
Conformance fixtures
Section titled “Conformance fixtures”@query-farm/vgi-rpc/conformance exports buildSecondaryProtocol() (the
cross-port conformance.Secondary.v1) and the identity fixture policy, for
conformance workers and SDK fixture workers. Not for production servers.
