--- url: /en/api/@connectum/auth.md --- [Connectum API Reference](../../index.md) / @connectum/auth # @connectum/auth ## Modules * [proto](proto/index.md) * [testing](testing/index.md) ## Classes * [AuthzDeniedError](classes/AuthzDeniedError.md) * [LruCache](classes/LruCache.md) ## Interfaces * [AuthContext](interfaces/AuthContext.md) * [AuthInterceptorOptions](interfaces/AuthInterceptorOptions.md) * [AuthzDeniedDetails](interfaces/AuthzDeniedDetails.md) * [AuthzInterceptorOptions](interfaces/AuthzInterceptorOptions.md) * [AuthzRule](interfaces/AuthzRule.md) * [CacheOptions](interfaces/CacheOptions.md) * [GatewayAuthInterceptorOptions](interfaces/GatewayAuthInterceptorOptions.md) * [GatewayHeaderMapping](interfaces/GatewayHeaderMapping.md) * [JwtAuthInterceptorOptions](interfaces/JwtAuthInterceptorOptions.md) * [ProtoAuthzInterceptorOptions](interfaces/ProtoAuthzInterceptorOptions.md) * [ResolvedMethodAuth](interfaces/ResolvedMethodAuth.md) * [SessionAuthInterceptorOptions](interfaces/SessionAuthInterceptorOptions.md) ## Type Aliases * [AuthzEffect](type-aliases/AuthzEffect.md) * [InterceptorFactory](type-aliases/InterceptorFactory.md) ## Variables * [AUTH\_HEADERS](variables/AUTH_HEADERS.md) * [authContextStorage](variables/authContextStorage.md) * [AuthzEffect](variables/AuthzEffect.md) ## Functions * [createAuthInterceptor](functions/createAuthInterceptor.md) * [createAuthzInterceptor](functions/createAuthzInterceptor.md) * [createGatewayAuthInterceptor](functions/createGatewayAuthInterceptor.md) * [createJwtAuthInterceptor](functions/createJwtAuthInterceptor.md) * [createProtoAuthzInterceptor](functions/createProtoAuthzInterceptor.md) * [createSessionAuthInterceptor](functions/createSessionAuthInterceptor.md) * [getAuthContext](functions/getAuthContext.md) * [getPublicMethods](functions/getPublicMethods.md) * [matchesMethodPattern](functions/matchesMethodPattern.md) * [parseAuthHeaders](functions/parseAuthHeaders.md) * [requireAuthContext](functions/requireAuthContext.md) * [resolveMethodAuth](functions/resolveMethodAuth.md) * [setAuthHeaders](functions/setAuthHeaders.md) --- --- url: /en/packages/auth.md description: Authentication and authorization interceptors for Connectum --- # @connectum/auth Authentication and authorization interceptors for ConnectRPC services. Provides interceptor factories covering the most common auth patterns: generic pluggable auth, JWT (via jose), gateway-injected headers, session-based auth, and declarative authorization rules. All interceptors propagate `AuthContext` through `AsyncLocalStorage` so handlers can access the authenticated identity without explicit parameter passing. **Layer**: 1 (Protocol) ::: tip Related Guides * [Auth & Authz Overview](/en/guide/auth) -- when and why to use authentication * [JWT](/en/guide/auth/jwt) -- JWKS, HMAC, public key configuration * [Gateway](/en/guide/auth/gateway) -- pre-authenticated gateway headers * [Session](/en/guide/auth/session) -- session-based auth with better-auth/lucia * [Authorization (RBAC)](/en/guide/auth/authorization) -- declarative rules and proto-based authz * [Context & Testing](/en/guide/auth/context) -- AsyncLocalStorage context propagation ::: ::: tip Full API Reference Complete TypeScript API documentation: [API Reference](/en/api/@connectum/auth/) ::: ## Installation ```bash pnpm add @connectum/auth ``` **Requires**: Node.js 18+ **Dependencies**: `@connectrpc/connect`, `@connectum/core`, `jose` ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { createJwtAuthInterceptor, createAuthzInterceptor } from '@connectum/auth'; const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', issuer: 'https://auth.example.com/', audience: 'my-api', }); const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'public', methods: ['public.v1.PublicService/*'], effect: 'allow' }, { name: 'admin', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, ], }); const server = createServer({ services: [routes], interceptors: [jwtAuth, authz], }); await server.start(); ``` Access the authenticated identity in handlers: ```typescript import { requireAuthContext } from '@connectum/auth'; const handler = { async getUser(req) { const auth = requireAuthContext(); // throws Unauthenticated if missing return { user: await db.getUser(auth.subject) }; }, }; ``` ## Auth Context All authentication interceptors produce an `AuthContext` and store it in `AsyncLocalStorage`. Downstream interceptors and handlers access it via `getAuthContext()` or `requireAuthContext()`. ```typescript interface AuthContext { readonly subject: string; // User/service identifier readonly name?: string; // Human-readable display name readonly roles: ReadonlyArray; // Assigned roles readonly scopes: ReadonlyArray; // Granted scopes readonly claims: Readonly>; // Raw credential claims readonly type: string; // Credential type ("jwt", "api-key", etc.) readonly expiresAt?: Date; // Credential expiration } ``` ## API Reference ### `createAuthInterceptor(options)` Generic, pluggable authentication interceptor. Extracts credentials from the request, verifies them via a user-provided callback, and stores the resulting `AuthContext` in `AsyncLocalStorage`. ```typescript function createAuthInterceptor(options: AuthInterceptorOptions): Interceptor; ``` By default, extracts a Bearer token from the `Authorization` header. Override `extractCredentials` for custom extraction (API keys, custom headers, etc.). ```typescript import { createAuthInterceptor } from '@connectum/auth'; const auth = createAuthInterceptor({ verifyCredentials: async (token) => { const user = await db.findByToken(token); if (!user) throw new Error('Invalid token'); return { subject: user.id, roles: user.roles, scopes: [], claims: {}, type: 'api-key', }; }, cache: { ttl: 60_000, maxSize: 500 }, }); ``` #### `AuthInterceptorOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `verifyCredentials` | `(credentials: string) => AuthContext \| Promise` | **(required)** | Verify credentials and return auth context. Must throw on invalid credentials. | | `extractCredentials` | `(req: { header: Headers }) => string \| null \| Promise` | Bearer token extractor | Extract credentials from request headers | | `skipMethods` | `string[]` | `[]` | Method patterns to skip authentication for | | `propagateHeaders` | `boolean` | `false` | Propagate auth context as headers for downstream services | | `cache` | `CacheOptions` | `undefined` | LRU cache for verification results | | `propagatedClaims` | `string[]` | `undefined` | Filter which claim keys are propagated in headers (all if undefined) | *** ### `createJwtAuthInterceptor(options)` JWT convenience wrapper built on [jose](https://github.com/panva/jose). Supports JWKS remote key sets, HMAC secrets, and asymmetric public keys. Delegates to `createAuthInterceptor` internally. ```typescript function createJwtAuthInterceptor(options: JwtAuthInterceptorOptions): Interceptor; ``` Key resolution priority: `jwksUri` > `publicKey` > `secret`. At least one must be provided. ```typescript import { createJwtAuthInterceptor } from '@connectum/auth'; // JWKS-based (Auth0, Keycloak, etc.) const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', issuer: 'https://auth.example.com/', audience: 'my-api', maxTokenAge: '1h', claimsMapping: { roles: 'realm_access.roles', scopes: 'scope', }, }); // HMAC secret (testing / simple setups) const jwtAuth = createJwtAuthInterceptor({ secret: process.env.JWT_SECRET, issuer: 'my-service', }); ``` **SEC-002**: Throws `ConnectError(Code.Unauthenticated)` when the JWT is missing a subject claim. No silent fallback. #### `JwtAuthInterceptorOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `jwksUri` | `string` | -- | JWKS endpoint URL for remote key set | | `secret` | `string` | -- | HMAC symmetric secret (HS256/HS384/HS512). Minimum key size enforced per RFC 7518. | | `publicKey` | `CryptoKey` | -- | Asymmetric public key (RSA, RSA-PSS, EC, EdDSA). Import via `crypto.subtle.importKey()`. | | `issuer` | `string \| string[]` | -- | Expected token issuer(s) | | `audience` | `string \| string[]` | -- | Expected token audience(s) | | `algorithms` | `string[]` | -- | Allowed algorithms | | `maxTokenAge` | `number \| string` | -- | Maximum token age (seconds or duration string, e.g., `"2h"`) | | `claimsMapping` | `{ subject?, name?, roles?, scopes? }` | `{}` | Maps JWT claims to AuthContext fields. Supports dot-notation paths (e.g., `"realm_access.roles"`). | | `skipMethods` | `string[]` | `[]` | Method patterns to skip authentication for | | `propagateHeaders` | `boolean` | `false` | Propagate auth context as headers for downstream services | *** ### `createGatewayAuthInterceptor(options)` For services behind an API gateway (Kong, Envoy, Traefik, etc.) that has already performed authentication. Reads pre-authenticated identity from gateway-injected headers after verifying the request source. Trust is established via a header value check (shared secret or CIDR-based IP matching), not via `peerAddress`. ```typescript function createGatewayAuthInterceptor(options: GatewayAuthInterceptorOptions): Interceptor; ``` ```typescript import { createGatewayAuthInterceptor } from '@connectum/auth'; const gatewayAuth = createGatewayAuthInterceptor({ headerMapping: { subject: 'x-user-id', name: 'x-user-name', roles: 'x-user-roles', }, trustSource: { header: 'x-gateway-secret', expectedValues: [process.env.GATEWAY_SECRET], }, }); ``` Mapped headers and the trust header are **always stripped** -- including for methods listed in `skipMethods` -- to prevent downstream spoofing even on public endpoints. #### `GatewayAuthInterceptorOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `headerMapping` | `GatewayHeaderMapping` | **(required)** | Maps gateway headers to AuthContext fields. `subject` is required. | | `trustSource` | `{ header: string; expectedValues: string[] }` | **(required)** | Trust verification. Supports exact match and CIDR notation (e.g., `"10.0.0.0/8"`). | | `stripHeaders` | `string[]` | `[]` | Additional headers to strip after extraction | | `skipMethods` | `string[]` | `[]` | Method patterns to skip authentication for | | `propagateHeaders` | `boolean` | `false` | Propagate auth context as headers for downstream services | | `defaultType` | `string` | `"gateway"` | Default credential type when not provided by gateway | #### `GatewayHeaderMapping` | Field | Type | Required | Description | |-------|------|----------|-------------| | `subject` | `string` | Yes | Header containing the authenticated subject | | `name` | `string` | No | Header containing the display name | | `roles` | `string` | No | Header containing roles (JSON array or comma-separated) | | `scopes` | `string` | No | Header containing scopes (space-separated) | | `type` | `string` | No | Header containing credential type | | `claims` | `string` | No | Header containing JSON-encoded claims | *** ### `createSessionAuthInterceptor(options)` Session-based authentication for frameworks like better-auth, lucia, etc. Two-step process: verify the session token, then map raw session data to `AuthContext`. Unlike `createAuthInterceptor`, this interceptor passes the **full request `Headers`** to `verifySession`, enabling cookie-based auth flows. ```typescript function createSessionAuthInterceptor(options: SessionAuthInterceptorOptions): Interceptor; ``` ```typescript import { createSessionAuthInterceptor } from '@connectum/auth'; const sessionAuth = createSessionAuthInterceptor({ verifySession: (token, headers) => auth.api.getSession({ headers }), mapSession: (session) => ({ subject: session.user.id, name: session.user.name, roles: [], scopes: [], claims: session.user, type: 'session', }), cache: { ttl: 60_000 }, }); ``` #### `SessionAuthInterceptorOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `verifySession` | `(token: string, headers: Headers) => unknown \| Promise` | **(required)** | Verify session token and return raw session data. Must throw on invalid sessions. | | `mapSession` | `(session: unknown) => AuthContext \| Promise` | **(required)** | Map raw session data to AuthContext | | `extractToken` | `(req: { header: Headers }) => string \| null \| Promise` | Bearer token extractor | Custom token extraction | | `cache` | `CacheOptions` | `undefined` | LRU cache for session verification results | | `skipMethods` | `string[]` | `[]` | Method patterns to skip authentication for | | `propagateHeaders` | `boolean` | `false` | Propagate auth context as headers for downstream services | | `propagatedClaims` | `string[]` | `undefined` | Filter which claim keys are propagated in headers (all if undefined) | *** ### `createAuthzInterceptor(options?)` Declarative rules-based authorization interceptor. Evaluates rules against `AuthContext` from the authentication interceptor. Must be placed **after** an auth interceptor in the chain. ```typescript function createAuthzInterceptor(options?: AuthzInterceptorOptions): Interceptor; ``` ```typescript import { createAuthzInterceptor } from '@connectum/auth'; const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'public', methods: ['public.v1.PublicService/*'], effect: 'allow' }, { name: 'admin-only', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, { name: 'write-scope', methods: ['data.v1.DataService/Write*'], requires: { scopes: ['write'] }, effect: 'allow' }, ], }); ``` Rules are evaluated in order; the first matching rule wins. If no rule matches, the optional `authorize` callback is invoked. If neither rules nor callback produce a decision, `defaultPolicy` applies. * **Roles**: user must have **at least one** of the required roles * **Scopes**: user must have **all** required scopes #### `AuthzInterceptorOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `defaultPolicy` | `AuthzEffect` | `"deny"` | Policy when no rule matches and no callback is defined | | `rules` | `AuthzRule[]` | `[]` | Declarative authorization rules, evaluated in order | | `authorize` | `(context: AuthContext, req: { service, method }) => boolean \| Promise` | -- | Programmatic authorization callback (fallback after rules) | | `skipMethods` | `string[]` | `[]` | Method patterns to skip authorization for | #### `AuthzRule` | Field | Type | Description | |-------|------|-------------| | `name` | `string` | Rule name for logging and debugging | | `methods` | `string[]` | Method patterns to match | | `effect` | `AuthzEffect` | `"allow"` or `"deny"` | | `requires` | `{ roles?: string[]; scopes?: string[] }` | Required roles/scopes for the rule to apply | ## Proto-Based Authorization Define authorization rules directly in `.proto` files using custom method and service options. The `createProtoAuthzInterceptor` reads these options at runtime via `@bufbuild/protobuf` reflection. ### Proto Definitions Add `connectum/auth/v1/options.proto` to your proto directory: ```protobuf syntax = "proto2"; package connectum.auth.v1; import "google/protobuf/descriptor.proto"; message AuthRequirements { repeated string roles = 1; // any-of semantics repeated string scopes = 2; // all-of semantics } message MethodAuth { optional bool public = 1; optional AuthRequirements requires = 2; optional string policy = 3; // "allow" or "deny" } message ServiceAuth { optional string default_policy = 1; optional AuthRequirements default_requires = 2; optional bool public = 3; } extend google.protobuf.MethodOptions { optional MethodAuth method_auth = 50100; } extend google.protobuf.ServiceOptions { optional ServiceAuth service_auth = 50101; } ``` ### Usage in `.proto` Files ```protobuf import "connectum/auth/v1/options.proto"; service UserService { option (connectum.auth.v1.service_auth) = { default_policy: "deny" }; rpc GetProfile(GetProfileRequest) returns (GetProfileResponse) { option (connectum.auth.v1.method_auth) = { public: true }; } rpc UpdateProfile(UpdateProfileRequest) returns (UpdateProfileResponse) { option (connectum.auth.v1.method_auth) = { requires: { roles: ["admin"] } }; } } ``` > See [examples/auth](https://github.com/Connectum-Framework/examples/tree/main/auth) for a complete demo showing both proto-based and code-based authorization side by side. ### `createProtoAuthzInterceptor(options?)` Creates an interceptor that reads authorization configuration from protobuf custom options. When proto options do not resolve the decision, falls back to programmatic rules and callbacks. ```typescript function createProtoAuthzInterceptor(options?: ProtoAuthzInterceptorOptions): Interceptor; ``` **Decision flow**: 1. Read proto options via `resolveMethodAuth(req.method)` 2. `public = true` → skip (allow without authentication) 3. No auth context → throw `Unauthenticated` 4. `requires` defined → check roles/scopes → allow or deny 5. `policy = "allow"` → allow 6. `policy = "deny"` → deny 7. Fallback: evaluate programmatic `rules` 8. Fallback: call `authorize` callback 9. Apply `defaultPolicy` ```typescript import { createProtoAuthzInterceptor } from '@connectum/auth'; // Proto options only const authz = createProtoAuthzInterceptor(); // With fallback rules const authz = createProtoAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'admin', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, ], authorize: (ctx, req) => ctx.roles.includes('superadmin'), }); ``` #### `ProtoAuthzInterceptorOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `defaultPolicy` | `AuthzEffect` | `"deny"` | Policy when no proto option and no rule match | | `rules` | `AuthzRule[]` | `[]` | Programmatic fallback rules (evaluated after proto options) | | `authorize` | `(context: AuthContext, req: { service, method }) => boolean \| Promise` | -- | Programmatic authorization callback (fallback after rules) | ### `resolveMethodAuth(method)` Resolves the effective authorization configuration for an RPC method by merging service-level defaults with method-level overrides. ```typescript function resolveMethodAuth(method: DescMethod): ResolvedMethodAuth; ``` Results are cached in a `WeakMap` keyed by `DescMethod`. Priority: method → service → default. ```typescript interface ResolvedMethodAuth { readonly public: boolean; readonly policy: "allow" | "deny" | undefined; readonly requires: { readonly roles: readonly string[]; readonly scopes: readonly string[] } | undefined; } ``` ### `getPublicMethods(services)` Extracts public method patterns from service descriptors. Use with `skipMethods` in auth interceptors. ```typescript function getPublicMethods(services: readonly DescService[]): string[]; ``` ```typescript import { getPublicMethods } from '@connectum/auth/proto'; const publicMethods = getPublicMethods([UserService, HealthService]); // ["user.v1.UserService/GetProfile", "grpc.health.v1.Health/Check"] const authn = createJwtAuthInterceptor({ jwksUri: '...', skipMethods: publicMethods, }); ``` ## Context Utilities ### `authContextStorage` The `AsyncLocalStorage` instance used by all auth interceptors. Automatically isolated per async context (request). ### `getAuthContext()` Returns the current `AuthContext` or `undefined` if no auth interceptor is active. ```typescript import { getAuthContext } from '@connectum/auth'; const auth = getAuthContext(); if (auth) { console.log(`Authenticated as ${auth.subject}`); } ``` ### `requireAuthContext()` Returns the current `AuthContext` or throws `ConnectError(Code.Unauthenticated)`. Use when authentication is mandatory. ```typescript import { requireAuthContext } from '@connectum/auth'; const auth = requireAuthContext(); // throws if not authenticated ``` ## Header Utilities Serialization and deserialization of `AuthContext` to/from HTTP headers for cross-service context propagation. ### `setAuthHeaders(headers, context, propagatedClaims?)` Serialize `AuthContext` to request headers. Used internally by auth interceptors when `propagateHeaders` is enabled. Headers for `roles`, `scopes`, and `claims` are **silently dropped** if the serialized value exceeds 8192 bytes. This prevents oversized headers from causing transport-level failures. ```typescript import { setAuthHeaders } from '@connectum/auth'; setAuthHeaders(req.header, authContext); // With claims filter: setAuthHeaders(req.header, authContext, ['email', 'org_id']); ``` ### `parseAuthHeaders(headers)` Deserialize `AuthContext` from request headers. Returns `undefined` if the required `x-auth-subject` header is missing. Only use in trusted environments (behind mTLS, service mesh, etc.). ```typescript import { parseAuthHeaders } from '@connectum/auth'; const context = parseAuthHeaders(req.header); ``` ### `AUTH_HEADERS` Standard header names used for auth context propagation: | Constant | Header Name | Description | |----------|-------------|-------------| | `SUBJECT` | `x-auth-subject` | Authenticated subject identifier | | `TYPE` | `x-auth-type` | Credential type | | `NAME` | `x-auth-name` | Display name | | `ROLES` | `x-auth-roles` | JSON-encoded roles array | | `SCOPES` | `x-auth-scopes` | Space-separated scopes | | `CLAIMS` | `x-auth-claims` | JSON-encoded claims object | ## Cache ### `LruCache` Minimal in-memory LRU cache with TTL expiration. Uses `Map` insertion order for LRU eviction. No external dependencies. The constructor throws `RangeError("ttl must be a positive number")` if `ttl` is zero or negative. ```typescript import { LruCache } from '@connectum/auth'; const cache = new LruCache({ ttl: 60_000, maxSize: 500 }); cache.set('key', 'value'); const value = cache.get('key'); // undefined after TTL cache.clear(); cache.size; // number of entries ``` ### `CacheOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `ttl` | `number` | **(required)** | Entry time-to-live in milliseconds. Must be a positive number. | | `maxSize` | `number` | `1000` | Maximum number of cached entries | ## Errors ### `AuthzDeniedError` Extends `ConnectError` with `Code.PermissionDenied`. Carries server-side details (rule name, required roles/scopes) while exposing only "Access denied" to the client via the `SanitizableError` protocol from `@connectum/core`. ```typescript import { AuthzDeniedError } from '@connectum/auth'; ``` | Property | Type | Description | |----------|------|-------------| | `clientMessage` | `string` | Always `"Access denied"` (safe for clients) | | `ruleName` | `string` | Name of the rule that denied access | | `authzDetails` | `AuthzDeniedDetails` | Full details including required roles/scopes | | `serverDetails` | `Record` | Structured details for server-side logging | ## Method Pattern Matching ### `matchesMethodPattern(serviceName, methodName, patterns)` Shared utility for matching gRPC methods against skip/rule patterns. Used internally by all interceptors. ```typescript import { matchesMethodPattern } from '@connectum/auth'; matchesMethodPattern('user.v1.UserService', 'GetUser', ['user.v1.UserService/*']); // true matchesMethodPattern('user.v1.UserService', 'GetUser', ['user.v1.UserService/GetUser']); // true matchesMethodPattern('user.v1.UserService', 'GetUser', ['*']); // true matchesMethodPattern('user.v1.UserService', 'GetUser', ['admin.v1.AdminService/*']); // false ``` Pattern types: * `"*"` -- matches all methods * `"Service/*"` -- matches all methods of a service * `"Service/Method"` -- matches an exact method ## Testing Utilities The `@connectum/auth/testing` subpath export provides helpers for testing authenticated handlers. ```typescript import { createMockAuthContext, createTestJwt, withAuthContext, TEST_JWT_SECRET } from '@connectum/auth/testing'; ``` | Export | Description | |--------|-------------| | `createMockAuthContext` | Create a mock `AuthContext` with sensible defaults | | `createTestJwt` | Generate a signed JWT for testing | | `TEST_JWT_SECRET` | Pre-shared HMAC secret for test JWTs | | `withAuthContext` | Run a function within a given `AuthContext` (wraps `AsyncLocalStorage.run`) | ## Security Considerations * **Header stripping**: Auth interceptors strip all `x-auth-*` headers from incoming requests before processing to prevent spoofing from external clients. * **SEC-001**: `propagatedClaims` option filters which claims are included in propagated headers to prevent leaking sensitive data. * **SEC-002**: `createJwtAuthInterceptor` throws on missing JWT subject claim instead of silently falling back. * **HMAC key validation**: Minimum key size enforced per RFC 7518 (32 bytes for HS256, 48 for HS384, 64 for HS512). * **Header size limits**: Both `setAuthHeaders` and `parseAuthHeaders` enforce 8192-byte limits on roles, scopes, and claims headers to prevent abuse. `setAuthHeaders` silently drops oversized headers; `parseAuthHeaders` ignores them. * **Gateway header stripping**: `createGatewayAuthInterceptor` strips all mapped headers and the trust header on **every** request -- including skipped methods -- to prevent downstream spoofing. ## Exports Summary | Export | Subpath | Description | |--------|---------|-------------| | `createAuthInterceptor` | `.` | Generic pluggable authentication interceptor | | `createJwtAuthInterceptor` | `.` | JWT authentication interceptor (jose) | | `createGatewayAuthInterceptor` | `.` | Gateway-injected headers authentication interceptor | | `createSessionAuthInterceptor` | `.` | Session-based authentication interceptor | | `createAuthzInterceptor` | `.` | Declarative rules-based authorization interceptor | | `authContextStorage` | `.` | AsyncLocalStorage instance for auth context | | `getAuthContext` | `.` | Get current auth context (or undefined) | | `requireAuthContext` | `.` | Get current auth context or throw Unauthenticated | | `setAuthHeaders` | `.` | Serialize AuthContext to headers | | `parseAuthHeaders` | `.` | Deserialize AuthContext from headers | | `matchesMethodPattern` | `.` | Method pattern matching utility | | `LruCache` | `.` | In-memory LRU cache with TTL | | `AuthzDeniedError` | `.` | Authorization denied error class | | `AUTH_HEADERS` | `.` | Standard auth header name constants | | `AuthzEffect` | `.` | Authorization effect constants (ALLOW, DENY) | | `createProtoAuthzInterceptor` | `.` | Proto-based authorization interceptor | | `AuthContext`, `AuthInterceptorOptions`, `JwtAuthInterceptorOptions`, `GatewayAuthInterceptorOptions`, `GatewayHeaderMapping`, `SessionAuthInterceptorOptions`, `AuthzInterceptorOptions`, `AuthzRule`, `ProtoAuthzInterceptorOptions`, `CacheOptions`, `InterceptorFactory`, `AuthzDeniedDetails` | `.` | TypeScript types | | `createProtoAuthzInterceptor` | `./proto` | Proto-based authorization interceptor | | `resolveMethodAuth` | `./proto` | Resolve proto auth config for a method | | `getPublicMethods` | `./proto` | Extract public method patterns from services | | `AuthRequirements`, `MethodAuth`, `ServiceAuth` | `./proto` | Generated proto message types | | `AuthRequirementsSchema`, `MethodAuthSchema`, `ServiceAuthSchema` | `./proto` | Generated proto schemas | | `method_auth`, `service_auth` | `./proto` | Proto extension descriptors | | `createMockAuthContext` | `./testing` | Create mock AuthContext for tests | | `createTestJwt` | `./testing` | Generate signed test JWTs | | `TEST_JWT_SECRET` | `./testing` | Pre-shared HMAC secret for tests | | `withAuthContext` | `./testing` | Run function within AuthContext | ## Related Packages * **[@connectum/core](./core.md)** -- Server that accepts interceptors (peer dependency) * **[@connectum/interceptors](./interceptors.md)** -- Resilience interceptors (complementary, typically placed before auth in the chain) * **[@connectum/otel](./otel.md)** -- OpenTelemetry instrumentation (complementary) --- --- url: /en/api/@connectum/cli.md --- [Connectum API Reference](../../index.md) / @connectum/cli # @connectum/cli ## Modules * [commands/proto-sync](commands/proto-sync/index.md) * [utils/reflection](utils/reflection/index.md) --- --- url: /en/packages/cli.md description: CLI tools for Connectum -- proto sync via gRPC Server Reflection --- # @connectum/cli Command-line tools for the Connectum framework. Currently provides the `proto sync` command -- a pipeline that connects to a running Connectum server via gRPC Server Reflection, discovers all services and proto definitions, and generates TypeScript client types using `buf generate`. **Layer**: 3 (Development Tools) ::: tip Related Guides * [Quickstart](/en/guide/quickstart) -- CLI proto sync in the tutorial * [Server Reflection](/en/guide/protocols/reflection) -- reflection protocol used by proto sync ::: ::: tip Full API Reference Complete TypeScript API documentation: [API Reference](/en/api/@connectum/cli/) ::: ## Installation ```bash pnpm add -D @connectum/cli ``` **Requires**: Node.js 18+, `buf` CLI available on PATH **Built with**: [citty](https://github.com/unjs/citty) for CLI framework ## Quick Start ```bash # Dry run: list discovered services and files npx connectum proto sync --from localhost:5000 --out ./gen --dry-run # Full sync: generate TypeScript types from a running server npx connectum proto sync --from localhost:5000 --out ./gen ``` ## Commands ### `connectum proto sync` Syncs proto type definitions from a running Connectum server via gRPC Server Reflection. #### Pipeline 1. Connects to the server via `ServerReflectionClient` 2. Discovers all services and builds a `FileRegistry` 3. Serializes the file descriptors as a `FileDescriptorSet` binary (`.binpb`) 4. Runs `buf generate` with the `.binpb` as input 5. Cleans up temporary files #### Arguments | Argument | Type | Required | Description | |----------|------|----------|-------------| | `--from` | `string` | Yes | Server address (e.g., `localhost:5000` or `http://localhost:5000`) | | `--out` | `string` | Yes | Output directory for generated types | | `--template` | `string` | No | Path to custom `buf.gen.yaml` template | | `--dry-run` | `boolean` | No | Show what would be synced without generating code | #### Examples ```bash # Basic sync npx connectum proto sync --from localhost:5000 --out ./src/gen # With custom buf template npx connectum proto sync \ --from localhost:5000 \ --out ./src/gen \ --template ./buf.gen.custom.yaml # Dry run to inspect services npx connectum proto sync --from http://localhost:5000 --out ./gen --dry-run ``` #### Dry Run Output ``` Connecting to http://localhost:5000... Connected to http://localhost:5000 Services: - grpc.health.v1.Health - my.service.v1.MyService - grpc.reflection.v1.ServerReflection Files: - google/protobuf/descriptor.proto - grpc/health/v1/health.proto - my/service/v1/service.proto Would generate to: ./gen ``` ## API Reference The CLI exports its internals for programmatic usage: ### `executeProtoSync(options)` Execute the proto sync pipeline programmatically. ```typescript import { executeProtoSync } from '@connectum/cli/commands/proto-sync'; await executeProtoSync({ from: 'http://localhost:5000', out: './src/gen', template: './buf.gen.yaml', dryRun: false, }); ``` ```typescript interface ProtoSyncOptions { from: string; // Server URL out: string; // Output directory template?: string; // Custom buf.gen.yaml path dryRun?: boolean; // Preview mode } ``` ### Reflection Utilities ```typescript import { fetchReflectionData, fetchFileDescriptorSetBinary, } from '@connectum/cli/utils/reflection'; ``` #### `fetchReflectionData(url)` Fetches service and file descriptor information from a running server. ```typescript const result = await fetchReflectionData('http://localhost:5000'); console.log(result.services); // ['grpc.health.v1.Health', ...] console.log(result.fileNames); // ['grpc/health/v1/health.proto', ...] console.log(result.registry); // FileRegistry instance ``` ```typescript interface ReflectionResult { services: string[]; registry: FileRegistry; fileNames: string[]; } ``` #### `fetchFileDescriptorSetBinary(url)` Fetches `FileDescriptorSet` as binary (`.binpb`) suitable for `buf generate` input. ```typescript const binpb = await fetchFileDescriptorSetBinary('http://localhost:5000'); writeFileSync('/tmp/descriptors.binpb', binpb); // Then: buf generate /tmp/descriptors.binpb --output ./gen ``` ## Prerequisites The `proto sync` command requires: 1. **A running Connectum server** with the `Reflection()` protocol enabled 2. **buf CLI** installed and available on PATH (`pnpm add -D @bufbuild/buf`) 3. **A `buf.gen.yaml`** in the working directory (or specified via `--template`) Example `buf.gen.yaml`: ```yaml version: v2 plugins: - local: protoc-gen-es out: . opt: - target=ts - import_extension=.js ``` ## Package Exports ```json { ".": "./src/index.ts", "./commands/proto-sync": "./src/commands/proto-sync.ts", "./utils/reflection": "./src/utils/reflection.ts" } ``` ## Related Packages * **[@connectum/reflection](./reflection.md)** -- Server-side reflection protocol (required for `proto sync`) * **[@connectum/core](./core.md)** -- Server framework --- --- url: /en/api/@connectum/core.md --- [Connectum API Reference](../../index.md) / @connectum/core # @connectum/core ## Modules * [config](config/index.md) * [types](types/index.md) ## Interfaces * [SanitizableError](interfaces/SanitizableError.md) ## Type Aliases * [ConnectumEnv](type-aliases/ConnectumEnv.md) ## Variables * [BooleanFromStringSchema](variables/BooleanFromStringSchema.md) * [ConnectumEnvSchema](variables/ConnectumEnvSchema.md) * [LogFormatSchema](variables/LogFormatSchema.md) * [LoggerBackendSchema](variables/LoggerBackendSchema.md) * [LogLevelSchema](variables/LogLevelSchema.md) * [NodeEnvSchema](variables/NodeEnvSchema.md) * [tlsPath](variables/tlsPath.md) ## Functions * [createServer](functions/createServer.md) * [getTLSPath](functions/getTLSPath.md) * [isSanitizableError](functions/isSanitizableError.md) * [parseEnvConfig](functions/parseEnvConfig.md) * [readTLSCertificates](functions/readTLSCertificates.md) * [safeParseEnvConfig](functions/safeParseEnvConfig.md) ## References ### CreateServerOptions Re-exports [CreateServerOptions](types/interfaces/CreateServerOptions.md) *** ### HttpHandler Re-exports [HttpHandler](types/type-aliases/HttpHandler.md) *** ### LifecycleEvent Re-exports [LifecycleEvent](types/variables/LifecycleEvent.md) *** ### NodeRequest Re-exports [NodeRequest](types/type-aliases/NodeRequest.md) *** ### NodeResponse Re-exports [NodeResponse](types/type-aliases/NodeResponse.md) *** ### ProtocolContext Re-exports [ProtocolContext](types/interfaces/ProtocolContext.md) *** ### ProtocolRegistration Re-exports [ProtocolRegistration](types/interfaces/ProtocolRegistration.md) *** ### Server Re-exports [Server](types/interfaces/Server.md) *** ### ServerState Re-exports [ServerState](types/variables/ServerState.md) *** ### ServiceRoute Re-exports [ServiceRoute](types/type-aliases/ServiceRoute.md) *** ### ShutdownHook Re-exports [ShutdownHook](types/type-aliases/ShutdownHook.md) *** ### ShutdownOptions Re-exports [ShutdownOptions](types/interfaces/ShutdownOptions.md) *** ### TLSOptions Re-exports [TLSOptions](types/interfaces/TLSOptions.md) *** ### TransportServer Re-exports [TransportServer](types/type-aliases/TransportServer.md) --- --- url: /en/packages/core.md description: Main server factory with protocol plugin system for Connectum --- # @connectum/core The central package of the Connectum framework. Provides `createServer()` -- a factory function that creates a production-ready gRPC/ConnectRPC server with explicit lifecycle control, protocol plugin system, graceful shutdown, and TLS support. **Layer**: 0 (Server Foundation) -- zero internal dependencies ::: tip Related Guides * [Server Overview](/en/guide/server) -- lifecycle, events, `createServer()` API * [Lifecycle & Events](/en/guide/server/lifecycle) -- server states and event handlers * [Configuration](/en/guide/server/configuration) -- environment variables and 12-Factor App * [Graceful Shutdown](/en/guide/server/graceful-shutdown) -- shutdown hooks, Kubernetes integration * [Security (TLS)](/en/guide/security) -- TLS and mTLS configuration ::: ::: tip Full API Reference Complete TypeScript API documentation: [API Reference](/en/api/@connectum/core/) ::: ## Installation ```bash pnpm add @connectum/core ``` **Requires**: Node.js >= 18.0.0 (packages ship compiled `.js` + `.d.ts` + source maps) ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); console.log(`Server ready on port ${server.address?.port}`); }); server.on('error', (err) => console.error(err)); await server.start(); ``` ## API Reference ### `createServer(options)` Factory function that creates an unstarted `Server` instance. ```typescript function createServer(options: CreateServerOptions): Server; ``` The server is created in `CREATED` state. Call `server.start()` to begin accepting connections. ### `CreateServerOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `services` | `ServiceRoute[]` | **(required)** | Service routes to register on the ConnectRouter | | `port` | `number` | `5000` | Server port | | `host` | `string` | `"0.0.0.0"` | Server host to bind | | `tls` | `TLSOptions` | `undefined` | TLS configuration for secure connections | | `protocols` | `ProtocolRegistration[]` | `[]` | Protocol plugins (healthcheck, reflection, custom) | | `shutdown` | `ShutdownOptions` | `{}` | Graceful shutdown configuration | | `interceptors` | `Interceptor[]` | `[]` | ConnectRPC interceptors. When omitted or `[]`, no interceptors are applied. Use `createDefaultInterceptors()` from `@connectum/interceptors` for the production-ready chain. | | `allowHTTP1` | `boolean` | `true` | Allow HTTP/1.1 connections | | `handshakeTimeout` | `number` | `30000` | Handshake timeout in milliseconds | | `http2Options` | `SecureServerOptions` | `undefined` | Additional HTTP/2 server options | ### `Server` Interface Extends `EventEmitter`. Provides explicit lifecycle control. #### Lifecycle Methods ```typescript interface Server extends EventEmitter { /** Start the server. Throws if not in CREATED state. */ start(): Promise; /** Stop the server gracefully. Throws if not in RUNNING state. */ stop(): Promise; } ``` #### State Properties ```typescript interface Server { /** Current server address (null until started) */ readonly address: AddressInfo | null; /** Whether server is currently running */ readonly isRunning: boolean; /** Current server state */ readonly state: ServerState; /** Underlying HTTP/2 transport (null until started) */ readonly transport: Http2SecureServer | Http2Server | null; /** Registered service routes */ readonly routes: ReadonlyArray; /** Registered interceptors */ readonly interceptors: ReadonlyArray; /** Registered protocols */ readonly protocols: ReadonlyArray; } ``` #### Runtime Operations (before `start()`) ```typescript interface Server { /** Add a service route. Throws if server is already started. */ addService(service: ServiceRoute): void; /** Add an interceptor. Throws if server is already started. */ addInterceptor(interceptor: Interceptor): void; /** Add a protocol. Throws if server is already started. */ addProtocol(protocol: ProtocolRegistration): void; } ``` #### Shutdown Hooks ```typescript interface Server { /** Register an anonymous shutdown hook */ onShutdown(handler: ShutdownHook): void; /** Register a named shutdown hook */ onShutdown(name: string, handler: ShutdownHook): void; /** Register a named shutdown hook with dependencies */ onShutdown(name: string, dependencies: string[], handler: ShutdownHook): void; /** AbortSignal that is aborted when server begins shutdown */ readonly shutdownSignal: AbortSignal; } ``` ### `ServerState` ```typescript const ServerState = { CREATED: 'created', STARTING: 'starting', RUNNING: 'running', STOPPING: 'stopping', STOPPED: 'stopped', } as const; ``` ### `LifecycleEvent` ```typescript const LifecycleEvent = { START: 'start', // Emitted when server starts (before ready) READY: 'ready', // Emitted when server is ready to accept connections STOPPING: 'stopping', // Emitted when server begins graceful shutdown STOP: 'stop', // Emitted when server stops ERROR: 'error', // Emitted on error } as const; ``` ### `ShutdownOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `timeout` | `number` | `30000` | Timeout in ms for graceful shutdown | | `signals` | `NodeJS.Signals[]` | `["SIGTERM", "SIGINT"]` | Signals to listen for | | `autoShutdown` | `boolean` | `false` | Enable automatic graceful shutdown on signals | | `forceCloseOnTimeout` | `boolean` | `true` | Force close HTTP/2 sessions when timeout exceeded | ### Protocol Plugin System Protocols implement the `ProtocolRegistration` interface to register themselves on the server. ```typescript interface ProtocolRegistration { readonly name: string; register(router: ConnectRouter, context: ProtocolContext): void; httpHandler?: HttpHandler; } interface ProtocolContext { readonly registry: ReadonlyArray; } ``` ### TLS Configuration ```typescript interface TLSOptions { keyPath?: string; // Path to TLS key file certPath?: string; // Path to TLS certificate file dirPath?: string; // Directory with server.key and server.crt } ``` Utility functions: ```typescript function getTLSPath(): string; function readTLSCertificates(options?: TLSOptions): { key: Buffer; cert: Buffer }; ``` ### Environment Configuration (`@connectum/core/config`) Type-safe environment configuration using Zod schemas (12-Factor App). ```typescript import { parseEnvConfig, safeParseEnvConfig } from '@connectum/core/config'; const config = parseEnvConfig(); // throws on invalid config const result = safeParseEnvConfig(); // returns { success, data/error } ``` | Environment Variable | Type | Default | Description | |---------------------|------|---------|-------------| | `PORT` | `number` | `5000` | Server port | | `LISTEN` | `string` | `"0.0.0.0"` | Listen address | | `LOG_LEVEL` | `"debug" \| "info" \| "warn" \| "error"` | `"info"` | Log level | | `LOG_FORMAT` | `"json" \| "pretty"` | `"json"` | Log format | | `LOG_BACKEND` | `"otel" \| "pino" \| "console"` | `"otel"` | Logger backend | | `NODE_ENV` | `"development" \| "production" \| "test"` | `"development"` | Node environment | | `HTTP_HEALTH_ENABLED` | `boolean` | `false` | Enable HTTP health endpoints | | `HTTP_HEALTH_PATH` | `string` | `"/healthz"` | HTTP health endpoint path | | `OTEL_SERVICE_NAME` | `string` | -- | OpenTelemetry service name | | `OTEL_EXPORTER_OTLP_ENDPOINT` | `string (URL)` | -- | OTLP exporter endpoint | | `GRACEFUL_SHUTDOWN_ENABLED` | `boolean` | `true` | Enable graceful shutdown | | `GRACEFUL_SHUTDOWN_TIMEOUT_MS` | `number` | `30000` | Shutdown timeout in ms | ## Server Lifecycle ``` CREATED ──start()──> STARTING ──> RUNNING ──stop()──> STOPPING ──> STOPPED │ │ └──(error)──> STOPPED └──(error)──> STOPPED ``` Events are emitted in this order: `start` -> `ready` (success) or `error` (failure), then `stopping` -> `stop` (or `error`). ## Error Protocol ### `SanitizableError` (interface) Interface for errors that carry rich server-side details while exposing only a safe message to clients. The `ErrorHandler` interceptor from `@connectum/interceptors` recognizes this interface and sanitizes errors automatically. ```typescript interface SanitizableError { readonly clientMessage: string; readonly serverDetails: Readonly>; } ``` | Property | Type | Description | |----------|------|-------------| | `clientMessage` | `string` | Safe message returned to the client | | `serverDetails` | `Readonly>` | Structured details for server-side logging | ### `isSanitizableError(err)` Type guard that checks whether a value implements the `SanitizableError` protocol. Returns `true` only if the value is an `Error` instance with a `clientMessage` string, a non-null `serverDetails` object, and a numeric `code` property. ```typescript function isSanitizableError(err: unknown): err is Error & SanitizableError & { code: number }; ``` ```typescript import { isSanitizableError } from '@connectum/core'; if (isSanitizableError(err)) { // err.clientMessage -- safe for the client // err.serverDetails -- rich details for logging // err.code -- numeric gRPC status code } ``` ## Published Package Format All `@connectum/*` packages are built with [tsup](https://tsup.egoist.dev/) and ship: * **Compiled `.js` files** (ESM) -- ready to run on any ES module-capable runtime (Node.js 18+, Bun, tsx) * **TypeScript declarations** (`.d.ts`) -- full type information for IDE support and type checking * **Source maps** (`.js.map`) -- accurate stack traces pointing to the original TypeScript source No special loader or register hook is needed. All runtimes can import `@connectum/*` packages directly. See [Runtime Support: Node.js vs Bun vs tsx](/en/guide/typescript#runtime-support-node-js-vs-bun) for details. ## Exports Summary | Export | Subpath | Description | |--------|---------|-------------| | `createServer` | `.` | Server factory function | | `ServerState` | `.` | Server state constants | | `LifecycleEvent` | `.` | Lifecycle event name constants | | `isSanitizableError` | `.` | Type guard for `SanitizableError` protocol | | `getTLSPath`, `readTLSCertificates`, `tlsPath` | `.` | TLS utilities | | `SanitizableError`, `Server`, `CreateServerOptions`, `ShutdownOptions`, etc. | `.` | TypeScript types | | `parseEnvConfig`, `safeParseEnvConfig`, schemas | `./config` | Env configuration | ## Related Packages * **[@connectum/interceptors](./interceptors.md)** -- Resilience interceptor chain (Layer 1, optional) * **[@connectum/healthcheck](./healthcheck.md)** -- Healthcheck protocol plugin (Layer 1, optional) * **[@connectum/reflection](./reflection.md)** -- Server Reflection protocol plugin (Layer 1, optional) * **[@connectum/otel](./otel.md)** -- OpenTelemetry instrumentation (Layer 2, optional) --- --- url: /en/api/@connectum/healthcheck.md --- [Connectum API Reference](../../index.md) / @connectum/healthcheck # @connectum/healthcheck ## Classes * [HealthcheckManager](classes/HealthcheckManager.md) ## Interfaces * [HealthcheckOptions](interfaces/HealthcheckOptions.md) * [ServiceStatus](interfaces/ServiceStatus.md) ## Type Aliases * [ServingStatus](type-aliases/ServingStatus.md) ## Variables * [healthcheckManager](variables/healthcheckManager.md) * [ServingStatus](variables/ServingStatus.md) ## Functions * [createHealthcheckManager](functions/createHealthcheckManager.md) * [createHttpHealthHandler](functions/createHttpHealthHandler.md) * [Healthcheck](functions/Healthcheck.md) * [parseServiceFromUrl](functions/parseServiceFromUrl.md) --- --- url: /en/packages/healthcheck.md description: gRPC Health Check protocol and HTTP health endpoints for Connectum --- # @connectum/healthcheck Implements the [gRPC Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) as a Connectum protocol plugin. Provides gRPC `Check`, `Watch`, and `List` RPCs, plus optional HTTP endpoints (`/healthz`, `/health`, `/readyz`) for Kubernetes probes and load balancers. **Layer**: 1 (Protocol) ::: tip Related Guides * [Health Checks Overview](/en/guide/health-checks) -- gRPC and HTTP health endpoints * [gRPC & HTTP Protocol](/en/guide/health-checks/protocol) -- detailed protocol configuration * [Kubernetes Integration](/en/guide/health-checks/kubernetes) -- probes, shutdown, zero-downtime ::: ::: tip Full API Reference Complete TypeScript API documentation: [API Reference](/en/api/@connectum/healthcheck/) ::: ## Installation ```bash pnpm add @connectum/healthcheck ``` **Peer dependency**: `@connectum/core` ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true })], shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); server.on('stopping', () => { healthcheckManager.update(ServingStatus.NOT_SERVING); }); await server.start(); ``` ## API Reference ### `Healthcheck(options?)` Factory function that creates a `ProtocolRegistration` for the healthcheck protocol. ```typescript function Healthcheck(options?: HealthcheckOptions): ProtocolRegistration; ``` Pass the result to `createServer({ protocols: [...] })`. ### `HealthcheckOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `httpEnabled` | `boolean` | `false` | Enable HTTP health endpoints | | `httpPaths` | `string[]` | `["/healthz", "/health", "/readyz"]` | HTTP endpoint paths | | `watchInterval` | `number` | `500` | Polling interval (ms) for streaming Watch RPC | | `manager` | `HealthcheckManager` | `healthcheckManager` (singleton) | Custom manager instance | ### `healthcheckManager` (Singleton) Module-level singleton for controlling health status. Import it from any file in your application. ```typescript import { healthcheckManager, ServingStatus } from '@connectum/healthcheck'; // Set all services to SERVING healthcheckManager.update(ServingStatus.SERVING); // Set a specific service healthcheckManager.update(ServingStatus.NOT_SERVING, 'my.service.v1.MyService'); // Check overall health const allHealthy = healthcheckManager.areAllHealthy(); // Get specific service status const status = healthcheckManager.getStatus('my.service.v1.MyService'); // Get all statuses const allStatuses = healthcheckManager.getAllStatuses(); ``` ### `HealthcheckManager` Class ```typescript class HealthcheckManager { /** Update status for all services (no service arg) or a specific one */ update(status: ServingStatus, service?: string): void; /** Get status of a specific service */ getStatus(service: string): ServiceStatus | undefined; /** Get all service statuses */ getAllStatuses(): Map; /** Check if all services are SERVING */ areAllHealthy(): boolean; /** Initialize with service names (called automatically by protocol) */ initialize(serviceNames: string[]): void; /** Clear all services */ clear(): void; } ``` ### `createHealthcheckManager()` Creates an isolated `HealthcheckManager` instance. Useful for testing or running multiple servers in one process. ```typescript import { Healthcheck, createHealthcheckManager, ServingStatus } from '@connectum/healthcheck'; const manager = createHealthcheckManager(); const server = createServer({ protocols: [Healthcheck({ manager })], }); manager.update(ServingStatus.SERVING); ``` ### `ServingStatus` Re-exported from generated gRPC health proto. Values: | Constant | Value | Description | |----------|-------|-------------| | `ServingStatus.UNKNOWN` | `0` | Status unknown (initial state) | | `ServingStatus.SERVING` | `1` | Service is healthy | | `ServingStatus.NOT_SERVING` | `2` | Service is unhealthy | | `ServingStatus.SERVICE_UNKNOWN` | `3` | Service not recognized | ## gRPC Health Check Protocol The protocol registers the standard `grpc.health.v1.Health` service: | RPC | Type | Description | |-----|------|-------------| | `Check` | Unary | Returns serving status for a service. Empty service name checks overall health. Returns `NOT_FOUND` for unknown services. | | `Watch` | Server-streaming | Sends initial status immediately, then sends updates only when status changes. | | `List` | Unary | Returns all registered services with their statuses. | ### Testing with grpcurl ```bash # Check overall health grpcurl -plaintext localhost:5000 grpc.health.v1.Health/Check # Check specific service grpcurl -plaintext -d '{"service": "my.service.v1.MyService"}' \ localhost:5000 grpc.health.v1.Health/Check # List all services grpcurl -plaintext localhost:5000 grpc.health.v1.Health/List ``` ## HTTP Health Endpoints When `httpEnabled: true`, HTTP endpoints respond with JSON: ``` GET /healthz -> overall health GET /healthz?service=my.v1.MyService -> specific service GET /health -> same as /healthz GET /readyz -> same as /healthz ``` ### Response Format ```json { "status": "SERVING", "service": "overall", "timestamp": "2025-01-15T10:30:00.000Z" } ``` ### HTTP Status Code Mapping | ServingStatus | HTTP Status | |---------------|-------------| | `SERVING` | 200 | | `NOT_SERVING` | 503 | | `SERVICE_UNKNOWN` | 404 | | `UNKNOWN` | 503 | ## Exports Summary | Export | Description | |--------|-------------| | `Healthcheck` | Protocol registration factory | | `healthcheckManager` | Default singleton manager | | `HealthcheckManager` | Manager class | | `createHealthcheckManager` | Factory for isolated manager instances | | `createHttpHealthHandler` | HTTP handler factory (advanced) | | `parseServiceFromUrl` | URL query parser utility | | `ServingStatus` | Serving status constants | | `ServiceStatus` | Service status type | | `HealthcheckOptions` | Options type | ## Related Packages * **[@connectum/core](./core.md)** -- Server that hosts this protocol (peer dependency) * **[@connectum/reflection](./reflection.md)** -- Companion protocol for service discovery --- --- url: /en/api/@connectum/interceptors.md --- [Connectum API Reference](../../index.md) / @connectum/interceptors # @connectum/interceptors ## Modules * [bulkhead](bulkhead/index.md) * [circuit-breaker](circuit-breaker/index.md) * [defaults](defaults/index.md) * [errorHandler](errorHandler/index.md) * [fallback](fallback/index.md) * [logger](logger/index.md) * [method-filter](method-filter/index.md) * [retry](retry/index.md) * [serializer](serializer/index.md) * [timeout](timeout/index.md) ## Interfaces * [BulkheadOptions](interfaces/BulkheadOptions.md) * [CircuitBreakerOptions](interfaces/CircuitBreakerOptions.md) * [ErrorHandlerOptions](interfaces/ErrorHandlerOptions.md) * [FallbackOptions](interfaces/FallbackOptions.md) * [LoggerOptions](interfaces/LoggerOptions.md) * [RetryOptions](interfaces/RetryOptions.md) * [SerializerOptions](interfaces/SerializerOptions.md) * [TimeoutOptions](interfaces/TimeoutOptions.md) ## Type Aliases * [InterceptorFactory](type-aliases/InterceptorFactory.md) * [MethodFilterMap](type-aliases/MethodFilterMap.md) ## References ### createBulkheadInterceptor Re-exports [createBulkheadInterceptor](bulkhead/functions/createBulkheadInterceptor.md) *** ### createCircuitBreakerInterceptor Re-exports [createCircuitBreakerInterceptor](circuit-breaker/functions/createCircuitBreakerInterceptor.md) *** ### createDefaultInterceptors Re-exports [createDefaultInterceptors](defaults/functions/createDefaultInterceptors.md) *** ### createErrorHandlerInterceptor Re-exports [createErrorHandlerInterceptor](errorHandler/functions/createErrorHandlerInterceptor.md) *** ### createFallbackInterceptor Re-exports [createFallbackInterceptor](fallback/functions/createFallbackInterceptor.md) *** ### createLoggerInterceptor Re-exports [createLoggerInterceptor](logger/functions/createLoggerInterceptor.md) *** ### createMethodFilterInterceptor Re-exports [createMethodFilterInterceptor](method-filter/functions/createMethodFilterInterceptor.md) *** ### createRetryInterceptor Re-exports [createRetryInterceptor](retry/functions/createRetryInterceptor.md) *** ### createSerializerInterceptor Re-exports [createSerializerInterceptor](serializer/functions/createSerializerInterceptor.md) *** ### createTimeoutInterceptor Re-exports [createTimeoutInterceptor](timeout/functions/createTimeoutInterceptor.md) *** ### DefaultInterceptorOptions Re-exports [DefaultInterceptorOptions](defaults/interfaces/DefaultInterceptorOptions.md) --- --- url: /en/packages/interceptors.md description: Production-ready ConnectRPC interceptors with resilience patterns --- # @connectum/interceptors Production-ready ConnectRPC interceptors providing resilience patterns, error handling, validation, and serialization. Includes a fixed-order default interceptor chain and a method-filter interceptor for per-method configuration. **Layer**: 1 (Protocol) ::: tip Related Guides * [Interceptors Overview](/en/guide/interceptors) -- the interceptor chain explained * [Built-in Chain](/en/guide/interceptors/built-in) -- 8 production-ready interceptors * [Custom Interceptors](/en/guide/interceptors/custom) -- creating your own interceptors * [Method Filtering](/en/guide/interceptors/method-filtering) -- per-service/per-method routing ::: ::: tip Full API Reference Complete TypeScript API documentation: [API Reference](/en/api/@connectum/interceptors/) ::: ## Installation ```bash pnpm add @connectum/interceptors ``` **Requires**: Node.js 18+ ## Quick Start ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; // All defaults (fallback disabled) const interceptors = createDefaultInterceptors(); // Custom configuration const interceptors = createDefaultInterceptors({ retry: false, timeout: { duration: 10_000 }, circuitBreaker: { threshold: 3 }, }); ``` When using `@connectum/core`, pass the result of `createDefaultInterceptors()` to the `interceptors` option: ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; const server = createServer({ services: [routes], interceptors: createDefaultInterceptors({ errorHandler: { logErrors: true }, timeout: { duration: 15_000 }, retry: false, }), }); ``` ## Default Interceptor Chain The interceptor order is fixed and intentional: ``` errorHandler -> timeout -> bulkhead -> circuitBreaker -> retry -> fallback -> validation -> serializer ``` | # | Interceptor | Purpose | Default | |---|-------------|---------|---------| | 1 | **errorHandler** | Catch-all error normalization (outermost) | Enabled | | 2 | **timeout** | Enforce request deadline | Enabled (30s) | | 3 | **bulkhead** | Limit concurrent requests | Enabled (10/10) | | 4 | **circuitBreaker** | Prevent cascading failures | Enabled (5 failures) | | 5 | **retry** | Retry transient failures with backoff | Enabled (3 retries) | | 6 | **fallback** | Graceful degradation | **Disabled** | | 7 | **validation** | Validate request messages (@connectrpc/validate) | Enabled | | 8 | **serializer** | JSON serialization (innermost) | Enabled | ## API Reference ### `createDefaultInterceptors(options?)` Creates the full interceptor chain with configurable options. ```typescript function createDefaultInterceptors(options?: DefaultInterceptorOptions): Interceptor[]; ``` Each interceptor can be set to `true` (defaults), `false` (disabled), or an options object. ### `DefaultInterceptorOptions` ```typescript interface DefaultInterceptorOptions { errorHandler?: boolean | ErrorHandlerOptions; // default: true timeout?: boolean | TimeoutOptions; // default: true bulkhead?: boolean | BulkheadOptions; // default: true circuitBreaker?: boolean | CircuitBreakerOptions; // default: true retry?: boolean | RetryOptions; // default: true fallback?: boolean | FallbackOptions; // default: false validation?: boolean; // default: true serializer?: boolean | SerializerOptions; // default: true } ``` ## Individual Interceptors ### Error Handler Transforms errors into `ConnectError` with proper gRPC status codes. Recognizes the `SanitizableError` protocol from `@connectum/core`: preserves server-side details for logging while exposing only a safe `clientMessage` to the client. ```typescript import { createErrorHandlerInterceptor } from '@connectum/interceptors'; const interceptor = createErrorHandlerInterceptor({ logErrors: true, // default: NODE_ENV !== "production" includeStackTrace: false, // default: NODE_ENV !== "production" }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `logErrors` | `boolean` | `NODE_ENV !== "production"` | Log errors to console. **Deprecated**: use `onError` instead. | | `includeStackTrace` | `boolean` | `NODE_ENV !== "production"` | Include stack trace in logs | | `onError` | `(info: { error: Error; code: number; serverDetails?: Record; stack?: string }) => void` | -- | Error callback. Replaces console.error when provided. Receives rich error info including `serverDetails` from `SanitizableError`. | ### Timeout Enforces a request deadline before any processing begins. ```typescript import { createTimeoutInterceptor } from '@connectum/interceptors'; const interceptor = createTimeoutInterceptor({ duration: 15_000, // 15 seconds skipStreaming: true, // skip for streaming calls }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `duration` | `number` | `30000` | Request timeout in milliseconds | | `skipStreaming` | `boolean` | `true` | Skip for streaming calls | ### Bulkhead Limits concurrent requests to prevent resource exhaustion. ```typescript import { createBulkheadInterceptor } from '@connectum/interceptors'; const interceptor = createBulkheadInterceptor({ capacity: 20, // max concurrent requests queueSize: 50, // max queued requests skipStreaming: true, }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `capacity` | `number` | `10` | Maximum concurrent requests | | `queueSize` | `number` | `10` | Maximum queued requests | | `skipStreaming` | `boolean` | `true` | Skip for streaming calls | ### Circuit Breaker Prevents cascading failures by breaking the circuit on consecutive errors. ```typescript import { createCircuitBreakerInterceptor } from '@connectum/interceptors'; const interceptor = createCircuitBreakerInterceptor({ threshold: 5, // failures before opening halfOpenAfter: 30_000, // wait before retrying skipStreaming: true, }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `threshold` | `number` | `5` | Consecutive failures before opening | | `halfOpenAfter` | `number` | `30000` | Milliseconds before half-open attempt | | `skipStreaming` | `boolean` | `true` | Skip for streaming calls | ### Retry Retries transient failures with exponential backoff. Uses the `cockatiel` library. ```typescript import { createRetryInterceptor } from '@connectum/interceptors'; const interceptor = createRetryInterceptor({ maxRetries: 3, initialDelay: 200, maxDelay: 5_000, retryableCodes: [Code.Unavailable, Code.ResourceExhausted], skipStreaming: true, }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `maxRetries` | `number` | `3` | Maximum retry attempts | | `initialDelay` | `number` | `200` | Initial backoff delay (ms) | | `maxDelay` | `number` | `5000` | Maximum backoff delay (ms) | | `retryableCodes` | `Code[]` | `[Unavailable, ResourceExhausted]` | Error codes that trigger retry | | `skipStreaming` | `boolean` | `true` | Skip for streaming calls | ### Fallback Provides graceful degradation when the service fails. Disabled by default because it requires a handler function. ```typescript import { createFallbackInterceptor } from '@connectum/interceptors'; const interceptor = createFallbackInterceptor({ handler: (error) => ({ items: [], error: error.message }), skipStreaming: true, }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `handler` | `(error: Error) => T \| Promise` | **(required)** | Fallback function | | `skipStreaming` | `boolean` | `true` | Skip for streaming calls | ### Validation Validates request messages using `@connectrpc/validate` (protovalidate). No configuration options; toggled on/off. ```typescript // Enabled by default in createDefaultInterceptors() // To use standalone: import { createValidateInterceptor } from '@connectrpc/validate'; const interceptor = createValidateInterceptor(); ``` ### Serializer Auto-serializes ConnectRPC responses to JSON format. ```typescript import { createSerializerInterceptor } from '@connectum/interceptors'; const interceptor = createSerializerInterceptor({ skipGrpcServices: true, alwaysEmitImplicit: true, ignoreUnknownFields: true, }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `skipGrpcServices` | `boolean` | `true` | Skip for gRPC services | | `alwaysEmitImplicit` | `boolean` | `true` | Always emit implicit fields in JSON | | `ignoreUnknownFields` | `boolean` | `true` | Ignore unknown fields when deserializing | ## Method Filter Interceptor Routes interceptors to specific methods based on wildcard pattern matching. ```typescript import { createMethodFilterInterceptor } from '@connectum/interceptors'; const perMethodInterceptor = createMethodFilterInterceptor({ '*': [logRequest], // all methods 'admin.v1.AdminService/*': [requireAdmin], // all service methods 'user.v1.UserService/DeleteUser': [requireAdmin, auditLog], // exact method }); ``` ### Pattern Resolution Order All matching patterns execute in order: 1. Global wildcard `"*"` (first) 2. Service wildcard `"Service/*"` (second) 3. Exact match `"Service/Method"` (last) Within each pattern, interceptors execute in array order. ### `MethodFilterMap` ```typescript type MethodFilterMap = Record; ``` ## Exports Summary | Export | Description | |--------|-------------| | `createDefaultInterceptors` | Factory for the default interceptor chain | | `createErrorHandlerInterceptor` | Error normalization | | `createLoggerInterceptor` | Request logging | | `createSerializerInterceptor` | JSON serialization | | `createRetryInterceptor` | Retry with backoff | | `createCircuitBreakerInterceptor` | Circuit breaker | | `createTimeoutInterceptor` | Request timeout | | `createBulkheadInterceptor` | Concurrency limiter | | `createFallbackInterceptor` | Graceful degradation | | `createMethodFilterInterceptor` | Per-method interceptor routing | ## Related Packages * **[@connectum/core](./core.md)** -- Uses this package for built-in interceptor chain (dependent) * **[@connectum/otel](./otel.md)** -- OpenTelemetry interceptor (complementary) --- --- url: /en/api/@connectum/otel.md --- [Connectum API Reference](../../index.md) / @connectum/otel # @connectum/otel ## Modules * [attributes](attributes/index.md) * [client-interceptor](client-interceptor/index.md) * [interceptor](interceptor/index.md) * [logger](logger/index.md) * [meter](meter/index.md) * [metrics](metrics/index.md) * [provider](provider/index.md) * [shared](shared/index.md) * [traceAll](traceAll/index.md) * [traced](traced/index.md) * [tracer](tracer/index.md) ## Interfaces * [BatchSpanProcessorOptions](interfaces/BatchSpanProcessorOptions.md) * [CollectorOptions](interfaces/CollectorOptions.md) * [Meter](interfaces/Meter.md) * [OtelBaseOptions](interfaces/OtelBaseOptions.md) * [OtelClientInterceptorOptions](interfaces/OtelClientInterceptorOptions.md) * [OtelInterceptorOptions](interfaces/OtelInterceptorOptions.md) * [OTLPSettings](interfaces/OTLPSettings.md) * [TraceAllOptions](interfaces/TraceAllOptions.md) * [TracedOptions](interfaces/TracedOptions.md) * [Tracer](interfaces/Tracer.md) ## Type Aliases * [ArgsFilter](type-aliases/ArgsFilter.md) * [ExporterType](type-aliases/ExporterType.md) * [MethodArgsFilter](type-aliases/MethodArgsFilter.md) * [OtelAttributeFilter](type-aliases/OtelAttributeFilter.md) * [OtelFilter](type-aliases/OtelFilter.md) ## Variables * [ExporterType](variables/ExporterType.md) ## Functions * [getBatchSpanProcessorOptions](functions/getBatchSpanProcessorOptions.md) * [getCollectorOptions](functions/getCollectorOptions.md) * [getOTLPSettings](functions/getOTLPSettings.md) * [getServiceMetadata](functions/getServiceMetadata.md) ## References ### ATTR\_ERROR\_TYPE Re-exports [ATTR\_ERROR\_TYPE](attributes/variables/ATTR_ERROR_TYPE.md) *** ### ATTR\_NETWORK\_PEER\_ADDRESS Re-exports [ATTR\_NETWORK\_PEER\_ADDRESS](attributes/variables/ATTR_NETWORK_PEER_ADDRESS.md) *** ### ATTR\_NETWORK\_PEER\_PORT Re-exports [ATTR\_NETWORK\_PEER\_PORT](attributes/variables/ATTR_NETWORK_PEER_PORT.md) *** ### ATTR\_NETWORK\_PROTOCOL\_NAME Re-exports [ATTR\_NETWORK\_PROTOCOL\_NAME](attributes/variables/ATTR_NETWORK_PROTOCOL_NAME.md) *** ### ATTR\_NETWORK\_TRANSPORT Re-exports [ATTR\_NETWORK\_TRANSPORT](attributes/variables/ATTR_NETWORK_TRANSPORT.md) *** ### ATTR\_RPC\_CONNECT\_RPC\_STATUS\_CODE Re-exports [ATTR\_RPC\_CONNECT\_RPC\_STATUS\_CODE](attributes/variables/ATTR_RPC_CONNECT_RPC_STATUS_CODE.md) *** ### ATTR\_RPC\_METHOD Re-exports [ATTR\_RPC\_METHOD](attributes/variables/ATTR_RPC_METHOD.md) *** ### ATTR\_RPC\_SERVICE Re-exports [ATTR\_RPC\_SERVICE](attributes/variables/ATTR_RPC_SERVICE.md) *** ### ATTR\_RPC\_SYSTEM Re-exports [ATTR\_RPC\_SYSTEM](attributes/variables/ATTR_RPC_SYSTEM.md) *** ### ATTR\_SERVER\_ADDRESS Re-exports [ATTR\_SERVER\_ADDRESS](attributes/variables/ATTR_SERVER_ADDRESS.md) *** ### ATTR\_SERVER\_PORT Re-exports [ATTR\_SERVER\_PORT](attributes/variables/ATTR_SERVER_PORT.md) *** ### buildErrorAttributes Re-exports [buildErrorAttributes](shared/functions/buildErrorAttributes.md) *** ### ConnectErrorCode Re-exports [ConnectErrorCode](attributes/variables/ConnectErrorCode.md) *** ### ConnectErrorCodeName Re-exports [ConnectErrorCodeName](attributes/variables/ConnectErrorCodeName.md) *** ### createOtelClientInterceptor Re-exports [createOtelClientInterceptor](client-interceptor/functions/createOtelClientInterceptor.md) *** ### createOtelInterceptor Re-exports [createOtelInterceptor](interceptor/functions/createOtelInterceptor.md) *** ### createRpcClientMetrics Re-exports [createRpcClientMetrics](metrics/functions/createRpcClientMetrics.md) *** ### createRpcServerMetrics Re-exports [createRpcServerMetrics](metrics/functions/createRpcServerMetrics.md) *** ### estimateMessageSize Re-exports [estimateMessageSize](shared/functions/estimateMessageSize.md) *** ### getLogger Re-exports [getLogger](logger/functions/getLogger.md) *** ### getMeter Re-exports [getMeter](meter/functions/getMeter.md) *** ### getProvider Re-exports [getProvider](provider/functions/getProvider.md) *** ### getTracer Re-exports [getTracer](tracer/functions/getTracer.md) *** ### initProvider Re-exports [initProvider](provider/functions/initProvider.md) *** ### Logger Re-exports [Logger](logger/interfaces/Logger.md) *** ### LoggerOptions Re-exports [LoggerOptions](logger/interfaces/LoggerOptions.md) *** ### ProviderOptions Re-exports [ProviderOptions](provider/interfaces/ProviderOptions.md) *** ### RPC\_SYSTEM\_CONNECT\_RPC Re-exports [RPC\_SYSTEM\_CONNECT\_RPC](attributes/variables/RPC_SYSTEM_CONNECT_RPC.md) *** ### RpcClientMetrics Re-exports [RpcClientMetrics](metrics/interfaces/RpcClientMetrics.md) *** ### RpcServerMetrics Re-exports [RpcServerMetrics](metrics/interfaces/RpcServerMetrics.md) *** ### shutdownProvider Re-exports [shutdownProvider](provider/functions/shutdownProvider.md) *** ### traceAll Re-exports [traceAll](traceAll/functions/traceAll.md) *** ### traced Re-exports [traced](traced/functions/traced.md) --- --- url: /en/packages/otel.md description: 'OpenTelemetry instrumentation for Connectum -- tracing, metrics, and logging' --- # @connectum/otel Full OpenTelemetry instrumentation for Connectum services. Provides ConnectRPC interceptors for server and client tracing/metrics, a correlated logger, deep tracing helpers (`traced`, `traceAll`), and OTLP exporter management. Follows [OpenTelemetry semantic conventions for RPC](https://opentelemetry.io/docs/specs/semconv/rpc/connect-rpc/). **Layer**: 0 (Independent Core) ::: tip Related Guides * [Observability Overview](/en/guide/observability) -- tracing, metrics, and logging * [Service Communication](/en/guide/service-communication) -- inter-service calls with OTel instrumentation * [Client Interceptors](/en/guide/service-communication/client-interceptors) -- OTel client interceptor, resilience * [Distributed Tracing](/en/guide/observability/tracing) -- server/client interceptors, traced(), traceAll() * [Metrics](/en/guide/observability/metrics) -- counters, histograms, RPC metrics * [Logging](/en/guide/observability/logging) -- structured logging with OTel correlation * [Backends & Config](/en/guide/observability/backends) -- environment variables, Jaeger, Grafana ::: ::: tip Full API Reference Complete TypeScript API documentation: [API Reference](/en/api/@connectum/otel/) ::: ## Installation ```bash pnpm add @connectum/otel ``` **Requires**: Node.js 18+ ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { createOtelInterceptor, initProvider } from '@connectum/otel'; // Initialize provider (optional -- lazy-initialized from env by default) initProvider({ serviceName: 'my-service' }); const server = createServer({ services: [routes], interceptors: [ createOtelInterceptor({ serverPort: 5000 }), ], }); await server.start(); ``` ## API Reference ### Server Interceptor #### `createOtelInterceptor(options?)` Creates a ConnectRPC interceptor that instruments incoming RPC calls with OpenTelemetry tracing and metrics. ```typescript function createOtelInterceptor(options?: OtelInterceptorOptions): Interceptor; ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `withoutTracing` | `boolean` | `false` | Disable span creation (metrics only) | | `withoutMetrics` | `boolean` | `false` | Disable metric recording (tracing only) | | `filter` | `OtelFilter` | -- | Filter callback to skip specific RPCs | | `attributeFilter` | `OtelAttributeFilter` | -- | Filter to exclude specific attributes | | `recordMessages` | `boolean` | `false` | Include message content in span events (may contain sensitive data) | | `trustRemote` | `boolean` | `false` | Use extracted remote context as parent span | | `serverAddress` | `string` | `os.hostname()` | Override `server.address` attribute | | `serverPort` | `number` | -- | Opt-in `server.port` attribute | ```typescript const interceptor = createOtelInterceptor({ serverPort: 5000, filter: ({ service }) => !service.includes('Health'), trustRemote: false, // creates root spans with links to remote context }); ``` ### Client Interceptor #### `createOtelClientInterceptor(options)` Creates a ConnectRPC interceptor for outgoing client RPC calls. ```typescript function createOtelClientInterceptor(options: OtelClientInterceptorOptions): Interceptor; ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `serverAddress` | `string` | **(required)** | Target server address | | `serverPort` | `number` | -- | Target server port | | `withoutTracing` | `boolean` | `false` | Disable span creation | | `withoutMetrics` | `boolean` | `false` | Disable metric recording | | `filter` | `OtelFilter` | -- | Filter callback | | `attributeFilter` | `OtelAttributeFilter` | -- | Attribute filter | | `recordMessages` | `boolean` | `false` | Record message content | ### Streaming RPC Instrumentation Both server and client interceptors automatically instrument streaming RPCs (client streaming, server streaming, and bidirectional streaming). **Span lifecycle**: For streaming calls, the span is **not** closed when the handler returns. Instead, the span lifecycle is deferred to stream completion: * Span starts when the RPC begins * Individual `rpc.message` events are recorded for each sent/received message (when `recordMessages` is enabled) * Span ends when the stream is fully consumed, errors, or is broken This ensures accurate duration measurement and complete message tracking for long-lived streams. #### Streaming Attributes Additional semantic convention attributes for streaming messages: | Attribute | Value | Description | |-----------|-------|-------------| | `rpc.message.id` | sequential number | Message sequence number within the stream | | `rpc.message.type` | `"SENT"` / `"RECEIVED"` | Message direction | | `rpc.message.uncompressed_size` | bytes (estimated) | Estimated message size | | `network.transport` | `"tcp"` | Network transport protocol | ### Deep Tracing Helpers #### `traced(fn, options?)` Wraps a single function in an OpenTelemetry span. Preserves the original type signature. Supports sync and async functions. ```typescript function traced any>(fn: T, options?: TracedOptions): T; ``` ```typescript import { traced } from '@connectum/otel'; const findUser = traced(async (id: string) => { return await db.users.findById(id); }, { name: 'UserService.findUser', recordArgs: true, argsFilter: (args) => args.map(a => typeof a === 'string' ? a : '[redacted]'), }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `name` | `string` | `fn.name` or `"anonymous"` | Span name | | `recordArgs` | `boolean \| string[]` | `false` | Record function arguments | | `argsFilter` | `ArgsFilter` | -- | Transform/mask recorded args | | `attributes` | `Record` | -- | Custom span attributes | #### `traceAll(obj, options?)` Proxy-based wrapper that instruments all methods of an object. Returns a proxy that creates spans for each method call. ```typescript function traceAll(obj: T, options?: TraceAllOptions): T; ``` ```typescript import { traceAll } from '@connectum/otel'; const userRepo = traceAll(new UserRepository(), { prefix: 'UserRepository', exclude: ['toString'], recordArgs: false, }); ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `prefix` | `string` | `constructor.name` or `"Object"` | Span name prefix | | `include` | `string[]` | -- | Whitelist of methods to wrap | | `exclude` | `string[]` | -- | Blacklist of methods to exclude | | `recordArgs` | `boolean \| string[]` | `false` | Record method arguments | | `argsFilter` | `MethodArgsFilter` | -- | Transform/mask recorded args | ### Logger #### `getLogger(name?, options?)` Returns a correlated logger that automatically enriches log records with the active `trace_id` and `span_id`. ```typescript function getLogger(name?: string, options?: LoggerOptions): Logger; ``` ```typescript import { getLogger } from '@connectum/otel'; const log = getLogger('my-service'); log.info('User created', { userId: '123' }); log.warn('Rate limit approaching'); log.error('Database connection failed', { db: 'primary' }); log.debug('Cache hit'); ``` ```typescript interface Logger { info(message: string, attributes?: AnyValueMap): void; warn(message: string, attributes?: AnyValueMap): void; error(message: string, attributes?: AnyValueMap): void; debug(message: string, attributes?: AnyValueMap): void; emit(record: LogRecord): void; } ``` ### Provider Management #### `initProvider(options?)` Explicitly initialize the OpenTelemetry provider. Must be called before any telemetry is emitted if custom configuration is needed. ```typescript function initProvider(options?: ProviderOptions): void; ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `serviceName` | `string` | `OTEL_SERVICE_NAME` or `npm_package_name` | Override service name | | `serviceVersion` | `string` | `npm_package_version` | Override service version | | `settings` | `Partial` | env-based | Override OTLP settings | #### `getProvider()` Returns the current provider instance. Lazily creates one with default (environment-based) configuration if not yet initialized. ```typescript function getProvider(): OtelProvider; ``` #### `shutdownProvider()` Gracefully shuts down all OTLP providers and releases resources. ```typescript async function shutdownProvider(): Promise; ``` ### Standalone Instances ```typescript import { getTracer, getMeter } from '@connectum/otel'; const tracer = getTracer(); // Lazy-initialized Tracer const meter = getMeter(); // Lazy-initialized Meter ``` ### RPC Metrics ```typescript import { createRpcServerMetrics, createRpcClientMetrics } from '@connectum/otel'; const serverMetrics = createRpcServerMetrics(meter); // serverMetrics.callDuration -- rpc.server.call.duration (seconds) // serverMetrics.requestSize -- rpc.server.request.size (bytes) // serverMetrics.responseSize -- rpc.server.response.size (bytes) const clientMetrics = createRpcClientMetrics(meter); // clientMetrics.callDuration -- rpc.client.call.duration (seconds) // clientMetrics.requestSize -- rpc.client.request.size (bytes) // clientMetrics.responseSize -- rpc.client.response.size (bytes) ``` ## Configuration ### Environment Variables | Variable | Values | Default | Description | |----------|--------|---------|-------------| | `OTEL_SERVICE_NAME` | string | `npm_package_name` | Service name for all telemetry | | `OTEL_TRACES_EXPORTER` | `console \| otlp/http \| otlp/grpc \| none` | -- | Trace exporter type | | `OTEL_METRICS_EXPORTER` | `console \| otlp/http \| otlp/grpc \| none` | -- | Metric exporter type | | `OTEL_LOGS_EXPORTER` | `console \| otlp/http \| otlp/grpc \| none` | -- | Log exporter type | | `OTEL_EXPORTER_OTLP_ENDPOINT` | URL | -- | Collector endpoint (e.g. `http://localhost:4318`) | | `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` | number | `100` | Max spans per export batch | | `OTEL_BSP_MAX_QUEUE_SIZE` | number | `1000` | Max queue size (drops when full) | | `OTEL_BSP_SCHEDULE_DELAY` | number (ms) | `1000` | Auto-export interval | | `OTEL_BSP_EXPORT_TIMEOUT` | number (ms) | `10000` | Export operation timeout | ### `ExporterType` ```typescript const ExporterType = { CONSOLE: 'console', OTLP_HTTP: 'otlp/http', OTLP_GRPC: 'otlp/grpc', NONE: 'none', } as const; ``` ## Semantic Conventions Exported attribute constants for manual instrumentation: ```typescript import { ATTR_RPC_SYSTEM, ATTR_RPC_SERVICE, ATTR_RPC_METHOD, ATTR_RPC_CONNECT_RPC_STATUS_CODE, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, ATTR_ERROR_TYPE, ATTR_RPC_MESSAGE_ID, ATTR_RPC_MESSAGE_TYPE, ATTR_RPC_MESSAGE_UNCOMPRESSED_SIZE, ATTR_NETWORK_TRANSPORT, RPC_SYSTEM_CONNECT_RPC, RPC_MESSAGE_EVENT, } from '@connectum/otel'; ``` ## Exports Summary | Export | Subpath | Description | |--------|---------|-------------| | `createOtelInterceptor` | `.` / `./interceptor` | Server-side RPC interceptor | | `createOtelClientInterceptor` | `.` / `./client-interceptor` | Client-side RPC interceptor | | `traced` | `.` / `./traced` | Function-level tracing wrapper | | `traceAll` | `.` / `./traceAll` | Object-level tracing proxy | | `getLogger` | `.` / `./logger` | Correlated logger | | `getTracer`, `getMeter` | `.` / `./tracer`, `./meter` | Standalone OTel instances | | `initProvider`, `getProvider`, `shutdownProvider` | `.` / `./provider` | Provider lifecycle | | `createRpcServerMetrics`, `createRpcClientMetrics` | `.` / `./metrics` | RPC metric factories | | `ATTR_*`, `ConnectErrorCode`, etc. | `.` / `./attributes` | Semantic conventions | | `ExporterType`, `getOTLPSettings`, etc. | `.` / `./config` | Configuration utilities | ## Related Packages * **[@connectum/core](./core.md)** -- Server where the interceptor runs * **[@connectum/interceptors](./interceptors.md)** -- Complementary resilience interceptors --- --- url: /en/api/@connectum/reflection.md --- [Connectum API Reference](../../index.md) / @connectum/reflection # @connectum/reflection ## Functions * [collectFileProtos](functions/collectFileProtos.md) * [Reflection](functions/Reflection.md) --- --- url: /en/packages/reflection.md description: gRPC Server Reflection protocol plugin for Connectum --- # @connectum/reflection Implements the [gRPC Server Reflection Protocol](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) (v1 + v1alpha) as a Connectum protocol plugin. Allows clients like `grpcurl`, Postman, and `buf curl` to discover services, methods, and message types at runtime without requiring proto files. **Layer**: 1 (Protocol) ::: tip Related Guides * [Protocols Overview](/en/guide/protocols) -- the protocol plugin system * [Server Reflection](/en/guide/protocols/reflection) -- grpcurl, buf curl, Postman usage ::: ::: tip Full API Reference Complete TypeScript API documentation: [API Reference](/en/api/@connectum/reflection/) ::: ## Installation ```bash pnpm add @connectum/reflection ``` **Peer dependency**: `@connectum/core` **Internal dependency**: Uses `@lambdalisue/connectrpc-grpcreflect` for the reflection implementation. ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [routes], protocols: [Reflection()], }); await server.start(); // Clients can now discover services via gRPC Server Reflection ``` ## API Reference ### `Reflection()` Factory function that creates a `ProtocolRegistration` for the gRPC Server Reflection protocol. ```typescript function Reflection(): ProtocolRegistration; ``` The function takes no arguments. It automatically collects all registered service file descriptors from the `ProtocolContext` and builds a `FileDescriptorSet` for the reflection service. Pass the result to `createServer({ protocols: [...] })`. ### `collectFileProtos(files)` Utility function that recursively collects `FileDescriptorProto` objects from `DescFile` entries, including transitive dependencies. Deduplicates by file name using depth-first traversal. ```typescript function collectFileProtos(files: ReadonlyArray): DescFile['proto'][]; ``` This is primarily used internally by `Reflection()` but is exported for advanced use cases. ## How It Works When the server starts, the `Reflection` protocol: 1. Receives all registered service file descriptors via `ProtocolContext.registry` 2. Recursively collects all proto file descriptors and their dependencies using `collectFileProtos()` 3. Builds a `FileDescriptorSet` from the collected protos 4. Registers the `ServerReflection` service (v1 and v1alpha) on the ConnectRouter ## Usage with grpcurl `grpcurl` is the most common client for gRPC Server Reflection: ```bash # List all services grpcurl -plaintext localhost:5000 list # Describe a service grpcurl -plaintext localhost:5000 describe my.service.v1.MyService # Describe a method grpcurl -plaintext localhost:5000 describe my.service.v1.MyService.GetUser # Call a method (reflection provides the schema) grpcurl -plaintext -d '{"id": "123"}' \ localhost:5000 my.service.v1.MyService/GetUser ``` ## Usage with buf curl ```bash # List services buf curl --protocol grpc --http2-prior-knowledge http://localhost:5000 --list-methods # Call a method buf curl --protocol grpc --http2-prior-knowledge \ -d '{"id": "123"}' \ http://localhost:5000/my.service.v1.MyService/GetUser ``` ## Combined with Healthcheck Reflection and Healthcheck are typically used together: ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [routes], protocols: [ Healthcheck({ httpEnabled: true }), Reflection(), ], shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ## Exports Summary | Export | Description | |--------|-------------| | `Reflection` | Protocol registration factory | | `collectFileProtos` | Utility to collect file descriptors with dependencies | ## Related Packages * **[@connectum/core](./core.md)** -- Server that hosts this protocol (peer dependency) * **[@connectum/healthcheck](./healthcheck.md)** -- Companion protocol for health monitoring * **[@connectum/cli](./cli.md)** -- CLI tool that uses reflection for `proto sync` --- --- url: /en/contributing/adr/001-native-typescript-migration.md --- # ADR-001: Compile-Before-Publish TypeScript Strategy ## Status **Accepted** -- 2026-02-16 (supersedes original ADR-001 from 2025-12-22) ## Context ### Original Decision The original ADR-001 (2025-12-22) chose to publish `@connectum/*` packages as raw `.ts` source files to npm, relying on Node.js 25.2.0+ stable type stripping at runtime. The rationale was zero build step, instant startup, and simplified CI/CD. After real-world feedback and deeper analysis, this decision has been **revised**. ### Node.js Maintainer Feedback A Node.js core maintainer provided the following critical feedback on the "publish .ts source" approach: 1. **Node.js actively blocks type stripping in `node_modules`** -- this is an intentional design decision, not a temporary limitation. See the [official documentation](https://nodejs.org/api/typescript.html#type-stripping-in-dependencies). 2. **TypeScript is not backward-compatible** -- TypeScript regularly introduces breaking changes in minor versions. Real-world examples include `noble/hashes` and `uint8array` breakage, as well as legacy decorators vs. TC39 Stage 3 decorators incompatibilities. 3. **Each package must control its own TypeScript version** -- a package should compile with the TypeScript version it was tested against and publish the resulting JavaScript. Forcing consumers to strip types at runtime couples them to the publisher's TypeScript version. 4. **JavaScript is permanently backward-compatible** -- once valid JS is published, it works forever. TypeScript source does not have this guarantee. 5. **Official position** -- Node.js documentation explicitly states that type stripping should not be used for dependencies in `node_modules`. 6. **Practical breakage patterns** -- decorator semantics, enum compilation changes, and import resolution differences across TypeScript versions create silent failures that are difficult to diagnose. ### Loader Propagation Issues The raw `.ts` publishing approach required consumers to register a custom loader (`@connectum/core/register`) or use `--import` flags. This created several problems: * **Worker threads** do not inherit `--import` hooks * **`fork()` / `spawn()`** do not propagate loader configuration * **APM instrumentation tools** (OpenTelemetry, Datadog, New Relic) may not propagate hooks correctly * **Test runners and build tools** may strip or ignore custom loaders These issues made the raw `.ts` approach unreliable in production environments with complex process hierarchies. ### Industry-Standard Practice Compile-before-publish is the established pattern used by virtually all major TypeScript packages in the ecosystem. Frameworks and libraries such as tRPC, Fastify, Effect, Drizzle ORM, and Hono all develop in TypeScript but publish compiled `.js` + `.d.ts` + source maps. Common tooling includes: * **tsup** (esbuild-powered) or **unbuild** (rollup-powered) for fast compilation * **ESM** as the primary output format * **`declarationMap: true`** for IDE jump-to-source navigation * **Turborepo** or **Nx** for monorepo build orchestration This pattern is well-proven at scale across monorepos with dozens of packages. ## Decision **Compile-before-publish with tsup**: develop in `.ts`, publish `.js` + `.d.ts` + source maps to npm. ### Build Pipeline | Tool | Purpose | |------|---------| | **tsup** | Compile TS to JS (esbuild under the hood) | | **tsc** | Type checking only (`--noEmit`) | | **Turborepo** | Orchestrate build tasks across monorepo | Output characteristics: * **ESM only** (`type: "module"`) * **Declaration files** (`.d.ts`) for consumer type checking * **Declaration maps** (`declarationMap: true`) for IDE jump-to-source * **Source maps** (`.js.map`) for debugging * **No minification** -- framework code should be readable ### tsup Configuration ```typescript // tsup.config.ts import { defineConfig } from 'tsup' export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], dts: true, sourcemap: true, clean: true, minify: false, }) ``` ### Package.json Template ```json { "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, "files": ["dist"], "scripts": { "build": "tsup", "dev": "node --watch src/index.ts", "typecheck": "tsc --noEmit", "test": "node --test tests/**/*.test.ts" } } ``` ### TypeScript Configuration The `tsconfig.json` remains largely unchanged from the original ADR: ```json { "compilerOptions": { "noEmit": true, "target": "esnext", "module": "nodenext", "allowImportingTsExtensions": true, "rewriteRelativeImportExtensions": true, "erasableSyntaxOnly": true, "verbatimModuleSyntax": true, "declarationMap": true, "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true } } ``` ### What Is Preserved from the Original ADR The following conventions remain unchanged: * **`erasableSyntaxOnly: true`** -- no `enum`, no `namespace` with runtime code, no parameter properties * **`verbatimModuleSyntax: true`** -- explicit `import type` required * **`.ts` extensions in import paths** -- `rewriteRelativeImportExtensions` rewrites them to `.js` during build * **`node src/index.ts`** for local development -- type stripping works outside `node_modules` * **`node --watch src/index.ts`** for hot reload during development * **`tsc --noEmit`** for type checking * **Node.js >= 25.2.0** for the development environment * All syntax restrictions (no enum, no namespace, no parameter properties, no decorators, explicit `import type`, `package.json#imports` for path aliases) ### What Changes | Aspect | Before (Original ADR-001) | After (This ADR) | |--------|---------------------------|-------------------| | npm artifact | `src/*.ts` (raw source) | `dist/*.js` + `dist/*.d.ts` + `dist/*.js.map` | | package.json exports | `./src/index.ts` | `./dist/index.js` | | Build step | None | `tsup` before publish | | `@connectum/core/register` | Required for consumers | **DEPRECATED** (no longer needed) | | Consumer Node.js requirement | `>=25.2.0` | `>=18.0.0` (any modern Node.js) | | Consumer TypeScript coupling | Must match publisher's TS version | Decoupled via `.d.ts` | | Development Node.js requirement | `>=25.2.0` | `>=25.2.0` (unchanged) | ## Consequences ### Positive 1. **Broad Consumer Compatibility** -- published JavaScript works on any Node.js >=18.0.0. Consumers are no longer forced to use Node.js 25.2.0+ at runtime. 2. **No Loader Issues** -- compiled JavaScript requires no custom loaders, hooks, or `--import` flags. Worker threads, `fork()`, and APM tools work without special configuration. 3. **TypeScript Version Decoupling** -- the framework controls which TypeScript version it compiles with. Consumers receive stable `.d.ts` declarations that work with any compatible TypeScript version. 4. **Ecosystem Standard** -- compile-before-publish is the established pattern used by virtually all major TypeScript packages (tRPC, Fastify, Effect, Drizzle ORM, Hono, etc.). This reduces surprise for consumers. 5. **Permanent Backward Compatibility** -- published JavaScript does not break across TypeScript or Node.js upgrades. Once published, it works forever. 6. **IDE Experience Preserved** -- `declarationMap: true` enables jump-to-source navigation in IDEs, providing the same developer experience as raw `.ts` source. 7. **Development Workflow Unchanged** -- developers still write `.ts`, run `node src/index.ts` locally, and use `node --watch` for hot reload. The build step only runs before publish. ### Negative 1. **Added Build Step** -- `tsup` must run before publishing. This adds ~2-5 seconds per package to the CI/CD pipeline. Mitigated by Turborepo caching and parallel builds. 2. **`dist/` Directory** -- each package now has a `dist/` folder that must be gitignored and managed. Mitigated by `.gitignore` and `files` field in `package.json`. 3. **Build Dependency** -- tsup (and transitively esbuild) is added as a dev dependency. Mitigated by the fact that tsup is a well-maintained, widely-used build tool with minimal dependencies. 4. **Source Not Directly Readable in `node_modules`** -- consumers see compiled JS in `node_modules` instead of TypeScript source. Mitigated by source maps and declaration maps for debugging and navigation. ### Risks 1. **tsup/esbuild compatibility** -- if tsup introduces a breaking change, it could affect the build pipeline. Mitigated by pinning versions and using Turborepo's deterministic builds. 2. **Declaration file accuracy** -- `.d.ts` generation can occasionally produce incorrect types for complex TypeScript patterns. Mitigated by `tsc --noEmit` type checking and integration tests. ## Migration Plan ### Phase 1: Add Build Tooling * Add `tsup` as a dev dependency to each `@connectum/*` package * Create `tsup.config.ts` in each package * Add `build` script to each `package.json` * Add `dist/` to `.gitignore` * Update Turborepo pipeline to include `build` task ### Phase 2: Update Package Exports * Change `package.json` exports from `./src/index.ts` to `./dist/index.js` * Add `types` field pointing to `./dist/index.d.ts` * Update `files` field to include only `dist` * Add `declarationMap: true` to `tsconfig.json` ### Phase 3: Deprecate Register Hook * Mark `@connectum/core/register` as deprecated with a console warning * Update documentation to remove loader registration instructions * Remove register entrypoint in the next major version ### Phase 4: Update CI/CD and Documentation * Update GitHub Actions workflows to run `pnpm build` before publish * Update Changesets publish workflow to include build step * Update all documentation, guides, and examples * Update `engines` field: keep `>=25.2.0` for development, document `>=18.0.0` for consumers ## Alternatives Considered ### Alternative 1: Keep Raw .ts Publishing (Original ADR-001) **Rejected.** While appealing in theory (zero build step), this approach is explicitly blocked by Node.js in `node_modules`, couples consumers to a specific TypeScript version, and creates unreliable behavior with worker threads and process forking. ### Alternative 2: tsc Compilation **Considered but not chosen.** Standard `tsc` compilation works but is significantly slower than tsup/esbuild for the compilation step. It also does not support bundling or tree-shaking if needed in the future. tsup provides a faster, more flexible build pipeline while still using `tsc` for type checking. ### Alternative 3: Dual ESM + CJS Publishing **Deferred.** Publishing both ESM and CJS formats increases package size and complexity. Since Connectum targets modern Node.js environments, ESM-only is sufficient. CJS support can be added later via tsup's `format: ['esm', 'cjs']` if consumer demand justifies it. ### Alternative 4: SWC-based Compilation **Considered but not chosen.** SWC is faster than esbuild for some workloads but has less mature `.d.ts` generation. tsup's esbuild backend is fast enough for Connectum's package sizes, and tsup's built-in dts support simplifies the pipeline. ### Alternative 5: Bun / Deno Runtime **Rejected.** Both runtimes have native TypeScript support but would abandon the Node.js ecosystem and ConnectRPC compatibility. The Node.js ecosystem is a core requirement for Connectum. ## References 1. [Node.js -- Type Stripping in Dependencies](https://nodejs.org/api/typescript.html#type-stripping-in-dependencies) -- official documentation on why type stripping is blocked in `node_modules` 2. [Node.js TypeScript Documentation](https://nodejs.org/api/typescript.html) -- full TypeScript support documentation 3. [tsup Documentation](https://tsup.egoist.dev/) -- build tool used for compilation 4. [TypeScript 5.8 -- `rewriteRelativeImportExtensions`](https://www.typescriptlang.org/tsconfig#rewriteRelativeImportExtensions) -- compiler option for `.ts` to `.js` import rewriting 5. [Turborepo Documentation](https://turbo.build/repo/docs) -- monorepo build orchestration ## Changelog | Date | Author | Change | |------|--------|--------| | 2025-12-22 | Claude | Original ADR: Native TypeScript (raw .ts publishing) | | 2026-02-16 | Claude | Revised: Compile-before-publish with tsup (this version) | --- --- url: /en/contributing/adr/003-package-decomposition.md --- # ADR-003: Package Decomposition Strategy ## Status **Accepted** - 2025-12-22 > **Update (v0.2.0-beta.2, 2026-02-12)**: Package `@connectum/utilities` removed. All utilities (~800 lines) had better alternatives as Node.js built-ins or npm packages: `retry()` replaced by `cockatiel`, `sleep()` by `node:timers/promises`, `withTimeout()` by `AbortSignal.timeout()`, `LRUCache` by `lru-cache` npm. Configuration module (`ConnectumEnvSchema`, `parseEnvConfig`) moved to `@connectum/core/config`. > > **Update (v0.2.0-beta.2, 2026-02-12)**: Package `@connectum/proto` removed. It contained third-party proto definitions (Google APIs, buf/validate, OpenAPI v3) but had zero internal consumers. Proto distribution solved via `@connectum/reflection` + `@connectum/cli proto sync` (see [ADR-020](./020-reflection-proto-sync.md)). Third-party proto definitions available through BSR deps in `buf.yaml`. The monorepo now contains modular packages in dependency layers. Layer 0 contains only `@connectum/core`. ## Context Connectum is built as a universal framework for gRPC/ConnectRPC microservices. The predecessor was a monolithic package containing ~15-20 modules with mixed infrastructure and domain concerns. ### Problems with the Monolithic Approach 1. **Coupling**: Everything depends on everything 2. **Bundle Size**: Users pull the entire package even when they need a single utility 3. **Mixed Concerns**: Infrastructure + domain logic in one package 4. **Versioning**: A breaking change in one module forces a major bump for the whole package 5. **Reusability**: Difficult to use parts in other projects ### Target Audience Connectum is a **universal** framework for ANY gRPC/ConnectRPC services: * Must NOT contain domain-specific logic * Must be modular -- use only what you need * Must have clear separation of responsibilities ### Decomposition Principles 1. **Single Responsibility**: Each package handles one task 2. **Layered Architecture**: Strict dependency hierarchy 3. **Low Coupling**: Minimal dependencies between packages 4. **High Cohesion**: Related components in the same package 5. **Independent Versioning**: Each package can be versioned independently (future) ## Decision **We decompose into modular packages organized in dependency layers (originally defined in 4 layers, refined and expanded over time).** ### Current Package Structure ``` @connectum/ ├── core/ # Layer 0: Server Foundation (zero internal deps) ├── auth/ # Layer 1: Authentication & authorization interceptors ├── interceptors/ # Layer 1: ConnectRPC interceptors ├── healthcheck/ # Layer 1: gRPC Health Check protocol ├── reflection/ # Layer 1: gRPC Server Reflection protocol ├── cli/ # Layer 2: CLI tooling ├── otel/ # Layer 2: OpenTelemetry instrumentation └── testing/ # Layer 2: Testing utilities ``` ### Layer 0: Server Foundation #### @connectum/core **Purpose**: Main server factory (`createServer()`) with HTTP/2 server creation, TLS configuration, protocol plugin system, and explicit lifecycle control. Also contains the configuration module (`ConnectumEnvSchema`, `parseEnvConfig`) moved from the removed `@connectum/utilities`. **Why Layer 0**: Core is the foundation that all other packages extend. It has **zero internal dependencies** -- only external npm packages. Interceptors and protocols are passed explicitly by the user. **Key API**: ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; const server = createServer({ services: [routes], port: 5000, interceptors: createDefaultInterceptors(), protocols: [Healthcheck({ httpEnabled: true }), Reflection()], shutdown: { autoShutdown: true, timeout: 30000 }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` **Internal dependencies**: None **External dependencies**: `@connectrpc/connect`, `@connectrpc/connect-node`, `@bufbuild/protobuf`, `env-var`, `zod` *** ### Layer 1: Extensions #### @connectum/interceptors **Purpose**: ConnectRPC interceptors for cross-cutting concerns. **Contains**: errorHandler, timeout, bulkhead, circuitBreaker, retry, fallback, validation, serializer interceptors. Exports `createDefaultInterceptors()` factory for standard interceptor chain assembly (see [ADR-023](./023-uniform-registration-api.md)). **Why separate**: Interceptors are a distinct architectural pattern. Users choose which interceptors to use, can add custom ones, and can test them independently. **Internal dependencies**: `@connectum/otel` **External dependencies**: `@connectrpc/connect` #### @connectum/auth **Purpose**: Authentication and authorization interceptors for ConnectRPC services. **Contains**: Interceptor factories -- `createJwtAuthInterceptor` (JWT Bearer token verification via jose + JWKS), `createGatewayAuthInterceptor` (trusted gateway header forwarding), `createSessionAuthInterceptor` (session-based authentication), `createAuthzInterceptor` (declarative rule-based authorization), and `createProtoAuthzInterceptor` (proto-driven authorization via `@connectum/auth/proto` subpath). Auth context propagation via `AsyncLocalStorage` and cross-service headers. **Why separate**: Authentication and authorization are distinct cross-cutting concerns with their own dependency footprint (JWT libraries, session stores). Keeping them separate from `@connectum/interceptors` allows users to opt in only when needed, and avoids pulling auth-related dependencies into projects that handle auth at the gateway level. **Internal dependencies**: `@connectum/core` **External dependencies**: `@connectrpc/connect`, `jose`, `@bufbuild/protobuf` #### @connectum/healthcheck **Purpose**: gRPC Health Check protocol implementation (gRPC + HTTP endpoints). **Why separate**: Extracted from core per [ADR-022](./022-protocol-extraction.md) to follow Single Responsibility Principle. Can be used independently or omitted. **External dependencies**: `@connectrpc/connect`, `@bufbuild/protobuf` #### @connectum/reflection **Purpose**: gRPC Server Reflection protocol (v1 + v1alpha). **Why separate**: Extracted from core per [ADR-022](./022-protocol-extraction.md). Optional capability -- not all deployments need reflection. **External dependencies**: `@connectrpc/connect`, `@lambdalisue/connectrpc-grpcreflect` *** ### Layer 2: Tools #### @connectum/cli **Purpose**: CLI tooling for Connectum projects. **Contains**: Developer-facing commands for proto synchronization (`proto sync`), project scaffolding, and other workflow automation tasks. **Why separate**: CLI tools are a development-time concern with their own dependency footprint (argument parsing, file system operations). Production services do not need CLI utilities at runtime. **Internal dependencies**: None **External dependencies**: TBD #### @connectum/otel **Purpose**: OpenTelemetry instrumentation (traces, metrics, logs). **Contains**: OTLPProvider, tracer, meter, logger, wrapAll auto-instrumentation, env-based configuration. **Why separate**: Observability is a distinct concern with heavy `@opentelemetry/*` dependencies. Users can opt out if not needed. Easier to swap the observability provider in the future. **Dependencies**: `@opentelemetry/*` (7-8 packages) #### @connectum/testing **Purpose**: Mock factories, assertion helpers, and test server utility that eliminate test boilerplate across Connectum packages. **Contains**: * **Mock factories (P0)**: `createMockRequest`, `createMockNext`, `createMockNextError`, `createMockNextSlow` — eliminate 85+ duplicated mock objects across interceptor tests * **Assertion helpers (P0)**: `assertConnectError` — type-safe ConnectError assertion with `asserts` narrowing (replaces 50+ boilerplate patterns) * **Descriptor mocks (P1)**: `createMockDescMessage`, `createMockDescField`, `createMockDescMethod` — structurally valid protobuf descriptor mocks * **Streaming helpers (P1)**: `createMockStream` — AsyncIterable from array * **Test server (P2)**: `createTestServer`, `withTestServer` — real ConnectRPC server on random port with automatic lifecycle management **Why separate**: Testing is a devDependency concern. Production code should not pull test utilities. **Internal dependencies**: `@connectum/core` (and all transitive) **External dependencies**: `@connectrpc/connect`, `@bufbuild/protobuf` *** ### examples/ (directory, NOT a package) **Purpose**: Usage examples (basic-service, custom interceptors, production-ready). **Location**: Separate `examples` repository, outside the monorepo workspace. **Why not a package**: Examples are not published, they use packages as external dependencies, serve as E2E tests and onboarding material, and do not add complexity to the dependency graph. ## Consequences ### Positive 1. **Modularity** -- users install only what they need: ```json // Minimal setup { "dependencies": { "@connectum/core": "^0.2.0" } } // Full stack with observability and auth { "dependencies": { "@connectum/core": "^0.2.0", "@connectum/auth": "^0.2.0", "@connectum/otel": "^0.2.0", "@connectum/interceptors": "^0.2.0", "@connectum/healthcheck": "^0.2.0", "@connectum/reflection": "^0.2.0" } } ``` 2. **Clear Separation of Concerns** -- universal infrastructure packages only. No domain-specific logic in Connectum. 3. **Independent Evolution** -- a breaking change in one package does not force a major bump for all. Only affected packages are versioned. 4. **Testability** -- each package can be tested in isolation without pulling the full framework. 5. **Reusability** -- components like `@connectum/otel` or `@connectum/interceptors` can be used in non-Connectum Node.js projects. ### Negative 1. **Dependency Management Complexity** -- multiple packages to install instead of one. Mitigated by documentation with recommended package sets and future possibility of a meta-package (`@connectum/all`). 2. **Version Compatibility** -- risk of incompatible versions between packages. Mitigated by synchronized versioning strategy (all packages bump together) via changesets. 3. **Documentation Fragmentation** -- Each package needs its own README. Mitigated by centralized docs site, cross-package examples, and a single getting-started entry point. ### Trade-off Analysis | Aspect | Monolith | Modular (layered packages) | |--------|----------|---------------------| | **Bundle Size** | Large | Small | | **Setup Complexity** | Simple (1 pkg) | Medium (several pkgs) | | **Reusability** | Low | High | | **Testability** | Medium | High | | **Separation of Concerns** | Poor | Excellent | | **Independent Evolution** | Blocked | Possible | Modular approach wins on most criteria. Setup complexity is compensated by documentation and tooling. ## Alternatives Considered ### Alternative 1: Monolith (single package) **Rating**: 3/10. Simplest setup, but does not solve fundamental problems -- large bundle, mixed concerns, poor reusability. ### Alternative 2: Two Packages (Core + Extensions) **Rating**: 5/10. Some modularity, but "core" is still too large (~400KB), mixed concerns remain, cannot opt out of observability. ### Alternative 3: Micro-packages (15-20 packages) **Rating**: 4/10. Maximum granularity, but extreme dependency management complexity and poor developer experience. Diminishing returns. ### Alternative 4: Domain-Driven Packages **Rating**: 6/10. Packages aligned to domain areas (server, telemetry, data, middleware). Unclear boundaries -- layer-based approach provides cleaner separation. ### Alternative 5: Current Decision (layered packages) -- ACCEPTED **Rating**: 9/10. Clear separation of concerns, layered dependency graph, optimal granularity, each package has a clear purpose, manageable complexity. ## Implementation Guidelines ### Package.json Structure ```json { "name": "@connectum/", "version": "0.2.0", "type": "module", "main": "./src/index.ts", "types": "./src/index.ts", "exports": { ".": { "types": "./src/index.ts", "default": "./src/index.ts" } }, "engines": { "node": ">=25.2.0" } } ``` ### Directory Structure ``` packages// ├── src/ │ ├── index.ts # Main export │ └── ... ├── tests/ │ ├── unit/ │ └── integration/ ├── package.json ├── tsconfig.json └── README.md ``` ### Requirements per Package * **README**: Brief description, installation, usage examples, API reference, links to main docs * **Tests**: >80% unit test coverage, key integration scenarios * **Documentation**: TypeDoc comments, working examples in the `examples/` repository ## References 1. [Turborepo](https://turbo.build/) -- monorepo build orchestration 2. [pnpm workspaces](https://pnpm.io/workspaces) -- workspace management 3. [pnpm catalog](https://pnpm.io/catalogs) -- dependency catalog 4. Clean Architecture (Uncle Bob), Hexagonal Architecture (Ports & Adapters) ## Changelog | Date | Author | Change | |------|--------|--------| | 2025-12-22 | Claude | Initial ADR -- 8 packages in 4 layers | | 2026-02-12 | Claude | @connectum/utilities removed (8 -> 7 packages) | | 2026-02-12 | Claude | @connectum/proto removed (7 -> 6 packages, 4 -> 3 layers) | | 2026-02-14 | Claude | @connectum/testing description refined with detailed API surface (mock factories, assertions, test server) | | 2026-02-17 | Claude | Added @connectum/auth (Layer 1): 5 interceptor factories for JWT, gateway headers, session-based auth, and declarative authorization | | 2026-02-17 | Claude | Added @connectum/cli (Layer 2): CLI tooling | | 2026-02-17 | Claude | Updated package count: 6 -> 8 | --- --- url: /en/contributing/adr/005-input-validation-strategy.md --- # ADR-005: Input Validation Strategy ## Status **Accepted** - 2025-12-24 > **Update (2026-02-14)**: Replaced custom `createValidationInterceptor` with the official `@connectrpc/validate` package (`createValidateInterceptor()`). Custom implementation removed. Interceptor chain order revised — validation is now 7th (before serializer), not 1st. See [Implementation](#implementation) section. *** ## Context ### Target Environment **Embedded devices in production**: * Critical industrial/medical systems (high reliability requirements) * Long-running processes (months/years without restart) * No remote debugging (isolated networks) * Expensive physical access (embedded devices in the field) **Quality requirements**: * Target uptime: 99.9%+ (mission-critical systems) * Zero tolerance for crashes — failures can have serious consequences * High confidence in releases — no ability to quick-fix in production * Must catch bugs before deployment ### Why Input Validation is Critical (P0) In this environment, **input validation** is the **primary defense mechanism**: 1. **No Auth/Authz Layer** — no traditional authentication protection 2. **Direct Service Access** — clients have direct access to services 3. **Malformed Data Risk** — invalid input can cause crashes or undefined behavior 4. **Data Integrity** — critical for embedded systems (robotics, industrial) | Mechanism | Priority | Status | |-----------|----------|--------| | Input Validation | **P0 (Critical)** | This ADR | | TLS Encryption | Optional | ADR-004 (internal) | | Rate Limiting | Not Required | Controlled environment | | Authentication | Not Required | Isolated network | | Authorization | Not Required | Trusted devices | ### Requirements 1. **Schema-Based Validation** — rules must be part of proto schemas 2. **Automatic Enforcement** — validation happens automatically for all requests 3. **Fail Fast** — invalid requests rejected before business logic 4. **Clear Error Messages** — clients receive understandable validation errors 5. **Performance** — validation overhead < 1ms per request 6. **Extensibility** — custom validation rules possible ## Decision **Use `@connectrpc/validate` (official ConnectRPC validation package backed by `@bufbuild/protovalidate`) for schema-based input validation, integrated into the default interceptor chain.** ### Solution Architecture ```mermaid graph TB client["Client"] errorHandler["Error Handler
(outermost)"] resilience["Resilience
(timeout, bulkhead,
circuit breaker, retry)"] validation["Validation
(@connectrpc/validate)"] serializer["Serializer
(innermost)"] handler["Service Handler"] client -->|"Request"| errorHandler errorHandler --> resilience resilience --> validation validation -->|"Valid"| serializer validation -->|"Invalid
INVALID_ARGUMENT"| errorHandler serializer --> handler handler -->|"Response"| serializer serializer --> validation validation --> resilience resilience --> errorHandler errorHandler --> client style validation fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px style handler fill:#90ee90,stroke:#2b8a3e ``` ### Proto Schema with Validation Constraints ```protobuf syntax = "proto3"; import "buf/validate/validate.proto"; message CreateOrderRequest { string customer_id = 1 [(buf.validate.field).string.min_len = 1]; repeated OrderItem items = 2 [(buf.validate.field).repeated.min_items = 1]; ShippingAddress shipping_address = 3 [(buf.validate.field).required = true]; string currency = 4 [(buf.validate.field).string = {min_len: 3, max_len: 3}]; } message OrderItem { string product_id = 1 [(buf.validate.field).string.min_len = 1]; string name = 2 [(buf.validate.field).string.min_len = 1]; int32 quantity = 3 [(buf.validate.field).int32.gt = 0]; int64 price_cents = 4 [(buf.validate.field).int64.gt = 0]; } message GetOrderRequest { string order_id = 1 [(buf.validate.field).string.uuid = true]; } message ListOrdersRequest { int32 page_size = 1 [(buf.validate.field).int32 = {gte: 1, lte: 100}]; string page_token = 2; } ``` Available constraints: `min_len`, `max_len`, `pattern`, `email`, `uri`, `uuid` (string); `lt`, `lte`, `gt`, `gte`, `in`, `not_in` (numeric); `min_items`, `max_items`, `unique` (repeated); `required`, `skip` (message); `defined_only` (enum). ### buf.yaml Configuration Proto files that use validation constraints must declare the dependency: ```yaml # buf.yaml version: v2 modules: - path: proto deps: - buf.build/bufbuild/protovalidate lint: use: - STANDARD breaking: use: - FILE ``` ## Implementation ### Package: `@connectrpc/validate` Validation is delegated to the official ConnectRPC package. No custom interceptor implementation exists in Connectum. **Dependencies** (in `@connectum/interceptors`): ```json { "dependencies": { "@connectrpc/validate": "catalog:" } } ``` **Catalog** (in `pnpm-workspace.yaml`): ```yaml catalog: '@connectrpc/validate': ^0.2.0 '@bufbuild/protovalidate': ^1.1.1 ``` `@connectrpc/validate` has peer dependencies on `@bufbuild/protobuf` (^2.9.0), `@bufbuild/protovalidate` (^1.0.0), and `@connectrpc/connect` (^2.0.3). ### Integration with `createDefaultInterceptors()` Location: `packages/interceptors/src/defaults.ts` ```typescript import { createValidateInterceptor } from "@connectrpc/validate"; // Inside createDefaultInterceptors(): if (options.validation !== false) { interceptors.push(createValidateInterceptor()); } ``` Configuration accepts only `boolean`: ```typescript export interface DefaultInterceptorOptions { /** Validates request messages using @connectrpc/validate. @default true */ validation?: boolean; // ... } ``` For custom validation configuration, use `createValidateInterceptor()` directly: ```typescript import { createValidateInterceptor } from "@connectrpc/validate"; const server = createServer({ services: [routes], interceptors: [ createValidateInterceptor(/* custom options */), // ... other interceptors ], }); ``` ### Interceptor Chain Order The default chain order is fixed (see [ADR-023](./023-uniform-registration-api.md)): ``` 1. errorHandler — Catch-all error normalization (outermost, must be first) 2. timeout — Enforce deadline before any processing 3. bulkhead — Limit concurrency 4. circuitBreaker — Prevent cascading failures 5. retry — Retry transient failures (exponential backoff) 6. fallback — Graceful degradation (DISABLED by default) 7. validation — @connectrpc/validate (createValidateInterceptor) 8. serializer — JSON serialization (innermost) ``` **Rationale for validation position (7th, not 1st):** The original ADR proposed validation as the first interceptor ("reject invalid data immediately"). The current implementation places it after resilience interceptors because: 1. **Error handler must be outermost** — validation errors (ConnectError with `INVALID_ARGUMENT`) need consistent error formatting, which errorHandler provides as the outermost wrapper 2. **Timeout protects validation** — if validation itself is slow (complex constraints), timeout will abort it 3. **Validation before serializer** — data is validated before JSON serialization, ensuring only valid data reaches the handler 4. **Resilience is infrastructure** — timeout, bulkhead, circuit breaker protect the system regardless of payload validity ### Usage ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; // Validation enabled by default const server = createServer({ services: [routes], interceptors: createDefaultInterceptors(), }); // Disable validation const server = createServer({ services: [routes], interceptors: createDefaultInterceptors({ validation: false }), }); ``` ### Error Response When validation fails, `@connectrpc/validate` throws a `ConnectError` with code `INVALID_ARGUMENT` containing structured violation details: ``` Code: INVALID_ARGUMENT Message: "customer_id: value length must be at least 1 characters [string.min_len]" ``` *** ## Consequences ### Positive 1. **Zero Custom Code** — validation logic is fully delegated to the official `@connectrpc/validate` package. No maintenance burden for custom interceptor. 2. **Consistent with Ecosystem** — uses the same validation library as the rest of the ConnectRPC/buf ecosystem. 3. **Schema-Based** — proto schemas are the single source of truth. Validation rules are co-located with message definitions. 4. **Automatic Enforcement** — enabled by default in `createDefaultInterceptors()`. All requests are validated without developer action. 5. **Clear Error Messages** — clients receive structured `INVALID_ARGUMENT` errors with per-field violation details. 6. **Performance** — validation overhead < 1ms per request for typical messages. ### Negative 1. **Proto File Complexity** — proto files become more verbose with constraint annotations. Mitigated: constraints are self-documenting and easier to read than manual validation code. 2. **Limited Custom Validation** — `buf.validate` provides a predefined constraint set. Business-level validation (e.g., "email must be unique") still requires service-layer code. 3. **Boolean-Only Config** — `createDefaultInterceptors()` accepts only `boolean` for validation. Custom configuration requires direct use of `createValidateInterceptor()`. 4. **Upstream Dependency** — relies on `@connectrpc/validate` and `@bufbuild/protovalidate`. Breaking changes upstream would affect Connectum. *** ## Alternatives Considered | # | Alternative | Rating | Why Rejected | |---|-------------|--------|--------------| | 1 | Manual validation in service handlers | 2/10 | Error-prone, inconsistent, boilerplate, does not scale | | 2 | Joi/Zod runtime validation | 6/10 | Duplicate schemas (proto + Zod), schema drift risk, proto should be single source of truth | | 3 | Custom `createValidationInterceptor` | 7/10 | Was the original decision. Replaced by official `@connectrpc/validate` — less maintenance, better ecosystem compatibility | | **4** | **`@connectrpc/validate` (chosen)** | **9/10** | **Official package, zero custom code, ecosystem standard, maintained by ConnectRPC team** | *** ## Migration The custom validation interceptor was removed in favor of `@connectrpc/validate`: ```typescript // BEFORE (custom, removed) import { createValidationInterceptor } from "@connectum/interceptors"; const interceptor = createValidationInterceptor({ skipStreaming: true }); // AFTER (official) import { createValidateInterceptor } from "@connectrpc/validate"; const interceptor = createValidateInterceptor(); // Or via default chain (recommended) import { createDefaultInterceptors } from "@connectum/interceptors"; const interceptors = createDefaultInterceptors(); // validation enabled by default ``` *** ## References * [@connectrpc/validate](https://www.npmjs.com/package/@connectrpc/validate) — official ConnectRPC validation interceptor * [Buf Validate](https://github.com/bufbuild/protovalidate) — constraint library and reference * [ConnectRPC Interceptors](https://connectrpc.com/docs/node/interceptors) * [OWASP Input Validation Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html) * [ADR-023: Uniform Registration API](./023-uniform-registration-api.md) — interceptor chain order ## Changelog | Date | Author | Change | |------|--------|--------| | 2025-12-24 | Claude | Initial ADR — custom createValidationInterceptor, validation-first chain order | | 2026-02-14 | Claude | Replaced custom interceptor with @connectrpc/validate; updated chain order (7th, not 1st); added migration guide; real proto examples from production-ready example | --- --- url: /en/contributing/adr/006-resilience-pattern-implementation.md --- # ADR-006: Resilience Pattern Implementation ## Status **Accepted** - 2025-12-24 > **Update (2026-02-06)**: Per ADR-010 (Framework vs Infrastructure), resilience interceptors (circuit breaker, timeout, bulkhead, fallback, retry) are now **optional (opt-in)**. They are not included in the default interceptor chain of createServer(). Users explicitly attach the interceptors they need. For standalone deployments without Envoy/proxy, they are still recommended. *** ## Context ### Target Environment **Embedded devices in isolated networks**: * Services run on embedded devices (edge computing) * Isolated networks with no access to cloud infrastructure * No managed cloud services, no external monitoring/alerting **High availability requirements**: * Target uptime: 99.9%+ (critical industrial/medical systems) * Failure isolation: one service failure must not cause cascading failures * Graceful degradation: system must continue in degraded mode * Self-healing: automatic recovery without human intervention ### Why Resilience Patterns are Critical 1. **No Cloud Fallback** -- no external services available in isolated networks 2. **Resource Constraints** -- embedded devices have limited CPU/memory 3. **Cascading Failure Risk** -- one slow/failing service can block the entire system 4. **Human Intervention Cost** -- physical access to embedded devices is expensive and slow 5. **Safety-Critical** -- industrial/medical systems require high availability | Pattern | Purpose | Priority | |---------|---------|----------| | Circuit Breaker | Prevent cascading failures | P0 | | Timeout | Prevent resource exhaustion | P0 | | Bulkhead | Isolate failures | P0 | | Fallback | Graceful degradation | P1 | | Retry | Transient failure recovery | P1 | *** ## Decision **Use [cockatiel](https://github.com/connor4312/cockatiel) library for resilience pattern implementation in Connectum.** ### Why cockatiel? 1. **Production-ready** -- battle-tested in Microsoft VSCode, Azure SDK 2. **TypeScript-first** -- native TypeScript support, excellent typing 3. **Comprehensive** -- all required patterns in one library 4. **Zero dependencies** -- standalone, no external deps 5. **Lightweight** -- ~10KB minified, suitable for embedded devices 6. **Well-documented** -- excellent documentation and examples ### Implemented Patterns #### 1. Circuit Breaker Prevents cascading failures by failing fast when a downstream service is unhealthy. * **States**: Closed (normal) -> Open (fail fast) -> Half-Open (probe recovery) * **Default config**: `threshold: 5` consecutive failures, `halfOpenAfter: 30000ms` * **Error mapping**: `BrokenCircuitError` -> `ConnectError(Code.Unavailable)` #### 2. Timeout Prevents resource exhaustion from slow/hanging requests. * **Strategy**: `TimeoutStrategy.Aggressive` -- cancel immediately on timeout * **Default config**: `duration: 30000ms` * **Error mapping**: `TaskCancelledError` -> `ConnectError(Code.DeadlineExceeded)` #### 3. Bulkhead Isolates failures by limiting concurrent requests. * **Default config**: `capacity: 10`, `queueSize: 10` * **Error mapping**: `BulkheadRejectedError` -> `ConnectError(Code.ResourceExhausted)` #### 4. Fallback Graceful degradation when primary service fails. User provides a handler function that returns cached/default data on error. #### 5. Retry Recovers from transient failures. **Only retries `ResourceExhausted` errors (Code 8)** -- all other error types are not retried because they are either permanent (InvalidArgument, NotFound) or handled by other patterns (Unavailable by circuit breaker, DeadlineExceeded by timeout). * **Default config**: `maxRetries: 5`, `timeout: 1000ms` (fixed delay) ### Interceptor Chain Order ``` Request | [Validation] --(invalid)--> Reject (400 Invalid Argument) | (valid) [Timeout] --(timeout)--> Reject (504 Deadline Exceeded) | (in time) [Circuit Breaker] --(open)--> Reject (503 Unavailable) | (closed) [Bulkhead] --(exhausted)--> Reject (503 Resource Exhausted) | (available) [Security/Redact] | [Error Handler] | [Observability] | [Retry] --(ResourceExhausted)--> Wait -> Retry | (success) Response ``` **Why this order?** 1. Validation first -- reject invalid data immediately 2. Timeout before Circuit Breaker -- catch slow requests before tracking failures 3. Circuit Breaker before Bulkhead -- fail fast if service is down, don't waste capacity slots 4. Retry last -- after all protections, retry only transient failures *** ## Consequences ### Positive 1. **Fault Isolation** -- circuit breaker prevents cascading failures; bulkhead isolates slow services 2. **Resource Protection** -- timeout prevents hanging requests; bulkhead prevents thread/memory exhaustion 3. **Graceful Degradation** -- fallback provides degraded service instead of complete failure 4. **Self-Healing** -- circuit breaker auto-recovers via half-open state; retry recovers from transient failures 5. **Production-Ready** -- battle-tested cockatiel library (Microsoft VSCode, Azure SDK) 6. **Embedded Device Friendly** -- lightweight (~10KB), minimal CPU/memory overhead, zero dependencies ### Negative 1. **Configuration Complexity** -- requires correct threshold tuning; incorrect config can reduce availability. Mitigated by sensible defaults. 2. **Debugging Complexity** -- circuit breaker errors can obscure root cause; fallback can hide production issues. Mitigated by comprehensive logging of state changes. 3. **Testing Complexity** -- requires chaos testing (fault injection, latency injection). Mitigated by comprehensive test suite. 4. **Latency Overhead** -- interceptor chain adds ~1-2ms per request. Acceptable for embedded devices (target p95 < 100ms). 5. **Retry Amplification Risk** -- retry can amplify load on failing services. Mitigated by only retrying ResourceExhausted errors. *** ## Alternatives Considered ### Seriously Evaluated | # | Alternative | Rating | Why Rejected | |---|-------------|--------|--------------| | 1 | Manual implementation | 2/5 | High dev/maintenance cost, missing advanced features (half-open, state listeners), reinventing the wheel | | 2 | opossum (Red Hat) | 3.5/5 | Only circuit breaker -- missing timeout, bulkhead, retry, fallback; requires combining multiple libraries; larger bundle (~20KB) | | **-** | **cockatiel (chosen)** | **5/5** | **All patterns in one library, TypeScript-first, lightweight, Microsoft-backed** | ### Also Evaluated (not viable) * **Hystrix (Netflix)** -- deprecated since 2018, no TypeScript implementation, too heavy for embedded devices * **Polly-js** -- unmaintained (last commit 2019), no TypeScript types, missing bulkhead pattern *** ## References * [cockatiel](https://github.com/connor4312/cockatiel) -- resilience library (used in Microsoft VSCode, Azure SDK) * [Circuit Breaker Pattern](https://martinfowler.com/bliki/CircuitBreaker.html) -- Martin Fowler * [Azure Resilience Patterns](https://learn.microsoft.com/en-us/azure/architecture/patterns/category/resiliency) * [ADR-005: Input Validation Strategy](./005-input-validation-strategy.md) * ADR-010: Framework vs Infrastructure (internal planning document) --- --- url: /en/contributing/adr/007-testing-strategy.md --- # ADR-007: Testing Strategy ## Status **Accepted** - 2025-12-24 > **Update (2026-02-14)**: Added `@connectum/testing` package specification — mock factories, assertion helpers, and test server utility to eliminate test boilerplate (135+ duplicates identified). Design influenced by [connect-es](https://github.com/connectrpc/connect-es) and [protobuf-es](https://github.com/bufbuild/protobuf-es) testing patterns. See [Testing Utilities](#testing-utilities-connectumtesting) section. *** ## Context ### Target Environment **Embedded devices in production**: * Critical industrial/medical systems (high reliability requirements) * Long-running processes (months/years without restart) * No remote debugging (isolated networks) * Expensive physical access (embedded devices in the field) **Quality requirements**: * Target uptime: 99.9%+ (mission-critical systems) * Zero tolerance for crashes -- failures can have serious consequences * High confidence in releases -- no ability to quick-fix in production * Must catch bugs before deployment ### Why Comprehensive Testing is Critical 1. **No Production Debugging** -- isolated networks mean no remote access for troubleshooting 2. **Expensive Failures** -- physical site visits are costly and slow 3. **Mission-Critical** -- industrial/medical systems require high reliability 4. **Long-Running Processes** -- bugs may only manifest after days/weeks 5. **Limited Rollback** -- can't easily rollback on embedded devices | Component | Target Coverage | Priority | |-----------|----------------|----------| | Core (server factory) | 95%+ | P0 | | Interceptors | 90%+ | P0 | | Otel (observability) | 85%+ | P1 | | Testing utilities | 90%+ | P2 | *** ## Decision **Use [node:test](https://nodejs.org/api/test.html) (native Node.js test runner) for comprehensive testing of all Connectum packages with a target coverage of 90%+.** ### Why node:test? 1. **Native Node.js** -- built-in to Node.js 25.2.0+ (zero dependencies) 2. **Zero Configuration** -- works out of the box 3. **TypeScript Support** -- works with native type stripping (no build step) 4. **Lightweight** -- minimal overhead 5. **Modern API** -- `describe`/`it`/`assert`, similar to Jest/Mocha 6. **Coverage Built-in** -- `--experimental-test-coverage` flag 7. **Parallel Execution** -- runs tests in parallel by default 8. **Stable** -- stable since Node.js 20 ### Test Structure ``` packages// src/ tests/ unit/ # Isolated unit tests .test.ts integration/ # Full-stack integration tests (when needed) .test.ts ``` * `tests/` directory at package root (sibling to `src/`) * Test file naming: `.test.ts` * Test files mirror source file organization ### Testing Philosophy **1. Mock Only External Dependencies** Mock HTTP requests, file system, external APIs, time-dependent code. Do NOT mock internal functions, internal modules, shared utilities, or pure functions from the same package. **2. Unit vs Integration Tests** * **Unit tests** (`tests/unit/`): single function/class in isolation, mock all external deps, fast (<100ms per test), 90%+ coverage target * **Integration tests** (`tests/integration/`): multiple components working together, minimal mocking, focus on critical paths **3. Descriptive Test Names** Format: `should [when ]` ```typescript describe('circuit breaker', () => { it('should pass request when circuit closed'); it('should open circuit after threshold failures'); it('should reject requests when circuit open'); }); ``` **4. Test Edge Cases** Always test: happy path, invalid input, empty/null/undefined values, boundary values, error conditions, concurrent access. **5. Strict Assertions** ```typescript import assert from 'node:assert'; assert.strictEqual(result, expected); // strict equality assert.deepStrictEqual(obj1, obj2); // deep strict equality assert.throws(() => fn(bad), /message/); // sync errors await assert.rejects(() => asyncFn(bad), /message/); // async errors ``` **6. Cleanup After Tests** Always close servers/connections, clear timers, reset mocks, delete temp files, and restore env variables in `afterEach`. ### Testing Utilities (@connectum/testing) Analysis of the existing test suite (216 tests across 3 packages) revealed significant boilerplate duplication: | Pattern | Duplicates | Example | |---------|-----------|---------| | Mock interceptor request | 50+ | `{ url, stream, message, service, method } as any` | | Mock next function | 35+ | `mock.fn(async () => ({ message: ... }))` | | ConnectError assertions | 50+ | `assert(err instanceof ConnectError); assert.strictEqual(err.code, ...)` | | DescMessage/Field/Method mocks | 10+ | 20-line objects with kind, typeName, fields, file, proto | | Streaming mock generators | 5+ | `async function* mockStream() { yield ... }` | **Decision**: Create `@connectum/testing` package (Layer 2) with the following API: **Phase 1 — Mock Factories & Assertions (P0):** * `createMockRequest(options?)` — mock interceptor request with sensible defaults * `createMockNext(options?)` — successful next function wrapped in `mock.fn()` for spy capabilities * `createMockNextError(code, message?)` — next that throws ConnectError * `createMockNextSlow(delay, options?)` — delayed next for timeout/retry testing * `assertConnectError(error, code, pattern?)` — type-safe assertion with `asserts` narrowing **Phase 2 — Protobuf Descriptor Mocks & Streaming (P1):** * `createMockDescMessage(typeName, options?)` — structurally valid DescMessage * `createMockDescField(localName, options?)` — DescField with isSensitive support * `createMockDescMethod(name, options?)` — DescMethod with input/output descriptors * `createMockStream(items, options?)` — AsyncIterable from array **Phase 3 — Test Server (P2):** * `createTestServer(options)` — real ConnectRPC server on random port * `withTestServer(options, testFn)` — lifecycle wrapper with automatic cleanup #### Design Decisions for @connectum/testing | Decision | Choice | Rationale | |----------|--------|-----------| | Mock objects vs runtime proto compilation | Mock objects | No protoc/buf dependency at test time; matches existing patterns; simpler setup | | `mock.fn()` in createMockNext | Yes (node:test) | Spy capabilities (call count, args) needed; node:test is the project standard | | Both createTestServer + withTestServer | Yes | beforeEach/afterEach vs single-test convenience | | No re-exports of Code/ConnectError | Correct | Users import directly from @connectrpc/connect; avoids coupling | #### Upstream Influence * **connect-es**: `useNodeServer()` pattern (start server before test, close after) → inspired `createTestServer` / `withTestServer` * **protobuf-es**: `node:test` + `node:assert`, descriptor-driven parameterized tests, `compileMessage()` for runtime proto compilation (rejected — too heavy for our use case) Full API specification: `connectum/packages/testing/README.md` ### Running Tests ```bash pnpm test # All tests (unit + integration) pnpm test:unit # Unit tests only pnpm test:integration # Integration tests only pnpm --filter @connectum/core test # Specific package pnpm test -- --experimental-test-coverage # With coverage pnpm test -- --watch # Watch mode pnpm test -- --test-concurrency=1 # Sequential (debugging) ``` *** ## Consequences ### Positive 1. **High Confidence in Releases** -- 90%+ coverage catches most bugs before production; regression tests prevent breakage 2. **Fast Development Velocity** -- tests provide fast feedback (15s execution), safe refactoring 3. **No External Dependencies** -- node:test is built-in, zero config, stable API 4. **Embedded Device Friendly** -- native execution, fast startup, minimal memory footprint 5. **CI/CD Ready** -- built-in coverage reporting, parallel execution, exit codes for validation ### Negative 1. **Initial Development Overhead** -- writing tests takes upfront time. Mitigated: tests pay off quickly via early bug detection and faster refactoring. 2. **Test Maintenance** -- tests must be updated with code changes; brittle tests can slow development. Mitigated: focus on behavior testing, not implementation. 3. **Coverage != Bug-Free** -- 90% coverage does not guarantee zero bugs. Mitigated: combine with manual testing and production monitoring. 4. **Limited node:test Features** -- no snapshot testing, no DOM testing, basic coverage reporting. Acceptable for server-side Node.js (no DOM needed). *** ## Alternatives Considered | # | Alternative | Rating | Why Rejected | |---|-------------|--------|--------------| | 1 | Jest | 4/5 | External dependency (~2MB), requires build step for TypeScript, slow startup, overkill for server-side Node.js | | 2 | Mocha + Chai | 3/5 | Multiple dependencies, requires build step, fragmented ecosystem | | 3 | AVA | 3/5 | External dependency, requires build step, different API, smaller community | | 4 | Vitest | 3.5/5 | Requires Vite, designed for frontend projects, additional complexity | | **-** | **node:test (chosen)** | **5/5** | **Native, zero-config, TypeScript support via type stripping, fast, stable** | *** ## Implementation Results **Total tests: 216** (198 unit + 18 integration), **92% overall coverage**. | Package | Unit | Integration | Total | Coverage | |---------|------|-------------|-------|----------| | interceptors | 77 | 18 | 95 | 92% | | core | 49 | 0 | 49 | 94% | | otel | 24 | 0 | 24 | 88% | | testing | 0 | 0 | 0 | Planned | | **Total** | **198** | **18** | **216** | **92%** | All tests passing (100% pass rate). Test execution time ~15s. *** ## References * [node:test Documentation](https://nodejs.org/api/test.html) -- official docs, coverage, mocking * [Test Pyramid](https://martinfowler.com/bliki/TestPyramid.html) -- Martin Fowler * [ADR-001: Native TypeScript Migration](./001-native-typescript-migration.md) * [ADR-003: Package Decomposition](./003-package-decomposition.md) -- @connectum/testing as Layer 2 * [ADR-006: Resilience Pattern Implementation](./006-resilience-pattern-implementation.md) * [connect-es](https://github.com/connectrpc/connect-es) -- upstream testing patterns (Jasmine, useNodeServer) * [protobuf-es](https://github.com/bufbuild/protobuf-es) -- upstream testing patterns (node:test, descriptor-driven tests) ## Changelog | Date | Author | Change | |------|--------|--------| | 2025-12-24 | Claude | Initial ADR -- testing strategy with node:test | | 2026-02-14 | Claude | Added @connectum/testing package specification (mock factories, assertions, test server) | --- --- url: /en/contributing/adr/008-performance-benchmarking.md --- # ADR-008: Performance Benchmarking ## Status **Accepted** - 2025-12-24 *** ## Context ### Target Environment **Embedded devices with resource constraints**: * Limited CPU (1-4 cores, embedded ARM/x86) * Limited memory (512MB - 2GB RAM) * Long-running processes (months/years without restart) * No cloud scaling (fixed hardware capacity) ### Why Performance Benchmarking is Critical 1. **No Cloud Scaling** -- embedded devices have fixed hardware; can't add more servers 2. **Resource Constraints** -- limited CPU/memory requires efficient code 3. **Long-Running Processes** -- performance issues compound over time (memory leaks, CPU spikes) 4. **Real-Time Requirements** -- industrial systems require low latency (< 100ms) 5. **Regression Detection** -- need a baseline for detecting performance degradation 6. **Capacity Planning** -- must understand limits for production deployment | Metric | Target | Priority | |--------|--------|----------| | **p95 Latency** | **< 100ms** | **P0 (Primary SLA)** | | p50 Latency | < 50ms | P0 | | p99 Latency | < 150ms | P0 | | Throughput | > 1000 req/sec | P0 | | Memory (RSS) | < 100MB | P1 | | CPU Usage | < 50% single core | P1 | | Interceptor Overhead | < 2ms/interceptor | P1 | *** ## Decision **Use [k6](https://k6.io/) load testing tool for comprehensive performance benchmarking of Connectum, with infrastructure for measuring baseline performance, interceptor overhead, and breaking points.** ### Why k6? 1. **Production-ready** -- industry standard (Grafana Labs) 2. **JavaScript syntax** -- familiar to the team 3. **Powerful scenarios** -- ramp-up, sustained load, spike tests 4. **Built-in thresholds** -- automated SLA pass/fail validation 5. **Rich metrics** -- p50, p95, p99, custom metrics 6. **Export options** -- JSON, Prometheus, InfluxDB, Grafana Cloud 7. **Lightweight** -- ~40MB binary (suitable for embedded device testing) 8. **CI/CD ready** -- exit codes for automated validation ### Performance SLA **Primary SLA: p95 Latency < 100ms** | Percentile | Target | Description | |------------|--------|-------------| | p50 | < 50ms | Fast majority | | p95 | < 100ms | **Primary SLA** | | p99 | < 150ms | Acceptable tail latency | | max | < 500ms | Worst case | **Secondary SLAs**: throughput > 1000 req/sec sustained, error rate < 1% under normal load (< 5% under stress), memory < 100MB, per-interceptor overhead < 2ms (full chain < 20ms). ### 5-Server Benchmarking Architecture Measures interceptor overhead by comparing different configurations: | Port | Configuration | Purpose | |------|--------------|---------| | 8081 | Baseline (no interceptors) | Minimum latency reference | | 8082 | Validation only | Validation overhead | | 8083 | Logger only | Logger overhead | | 8084 | Tracing only | Tracing overhead | | 8080 | Full chain (all interceptors) | Total overhead | **Overhead = Configuration latency - Baseline latency** ### Benchmark Scenarios #### 1. Basic Load Test (Primary SLA Validation) ```javascript export const options = { stages: [ { duration: '30s', target: 50 }, // Warm-up { duration: '1m', target: 100 }, // Ramp-up { duration: '5m', target: 100 }, // Sustained load { duration: '30s', target: 0 }, // Ramp-down ], thresholds: { http_req_duration: ['p(95)<100', 'p(99)<150'], http_reqs: ['rate>1000'], http_req_failed: ['rate<0.01'], }, }; ``` #### 2. Stress Test (Find Breaking Point) Ramps from 100 to 2000 VUs. Identifies maximum throughput before errors increase, latency degradation curve, and breaking point. ```javascript thresholds: { http_req_duration: ['p(95)<500'], http_req_failed: ['rate<0.05'], } ``` #### 3. Spike Test (Recovery Validation) Sudden 10x load spike (100 -> 1000 VUs). Validates the system handles spikes without crashing, circuit breaker doesn't trip incorrectly, and recovery time < 30s. #### 4. Interceptor Overhead Profiling Low VU count (10) for measurement accuracy, tests all 5 server configurations. Validates < 2ms per interceptor target. ### Running Benchmarks ```bash # 1. Start performance test server node examples/performance-test-server/src/index.ts # 2. Run scenarios k6 run tests/performance/scenarios/basic-load.js k6 run tests/performance/scenarios/stress-test.js k6 run tests/performance/scenarios/spike-test.js k6 run tests/performance/scenarios/interceptor-overhead.js # 3. Export results k6 run --out json=results/basic-load.json tests/performance/scenarios/basic-load.js ``` *** ## Consequences ### Positive 1. **SLA Validation** -- automated verification of p95 < 100ms with pass/fail criteria for CI/CD 2. **Performance Visibility** -- exact latency distribution, throughput limits, breaking points, per-interceptor costs 3. **Capacity Planning** -- understand system limits for production deployment on fixed hardware 4. **Optimization Targets** -- identify bottlenecks, measure optimization impact, prioritize work 5. **CI/CD Integration** -- automated benchmarks, block releases on SLA violations, track trends 6. **Embedded Device Validation** -- k6 runs on embedded devices (~40MB binary) ### Negative 1. **Initial Setup Overhead** -- creating scenarios and 5-server infrastructure takes time. Mitigated: infrastructure already created. 2. **Benchmark Maintenance** -- scenarios must be updated on API changes; baselines re-established on major changes. Mitigated: versioned baselines per release. 3. **False Positives** -- network jitter and system load can skew localhost results. Mitigated: multiple runs, warm-up periods, outlier detection. 4. **Limited Real-World Simulation** -- synthetic load differs from real user behavior. Mitigated: combine with production monitoring and real device testing. *** ## Alternatives Considered | # | Alternative | Rating | Why Rejected | |---|-------------|--------|--------------| | 1 | Apache Bench (ab) | 2/5 | No scenarios, no ramp-up, no p95/p99, no thresholds, HTTP/1.1 only | | 2 | wrk/wrk2 | 3/5 | Lua syntax (less familiar), no built-in scenarios or thresholds, limited export | | 3 | Gatling | 3/5 | JVM required (~200MB), Scala DSL, too heavy for embedded devices | | 4 | Locust | 3.5/5 | Python runtime required, slower than k6, no built-in thresholds | | **-** | **k6 (chosen)** | **5/5** | **JavaScript syntax, rich scenarios, built-in thresholds, lightweight, CI/CD ready** | *** ## Implementation Results **Infrastructure: complete**. 5-server architecture, 4 benchmark scenarios, documentation. **Baseline benchmarks: pending** (to be executed in v0.2.0-beta.1). | Scenario | Key Metric | Target | Status | |----------|-----------|--------|--------| | Basic Load | p95 latency | < 100ms | Pending | | Basic Load | Throughput | > 1000 req/sec | Pending | | Stress Test | Breaking point | > 2000 VUs | Pending | | Spike Test | Recovery time | < 30s | Pending | | Interceptor Overhead | Per-interceptor | < 2ms | Pending | *** ## References * [k6 Documentation](https://k6.io/docs/) -- load testing types, thresholds, metrics * [SRE Book: SLOs](https://sre.google/sre-book/service-level-objectives/) -- service level objectives * [ADR-006: Resilience Pattern Implementation](./006-resilience-pattern-implementation.md) * [ADR-007: Testing Strategy](./007-testing-strategy.md) --- --- url: /en/contributing/adr/009-buf-cli-migration.md --- # ADR-009: Migration to Buf CLI v2 for Proto Generation **Status:** Accepted - 2026-02-06 **Deciders:** Tech Lead, Platform Team **Tags:** `protobuf`, `buf`, `code-generation`, `lint`, `breaking-changes`, `tooling` *** ## Context ### Prior State (before ADR-009) The `@connectum/proto` package contained 15+ third-party proto files (googleapis, grpc health/reflection, buf validate, openapiv3) and used `protoc` for code generation. \[Update: @connectum/proto removed, see ADR-003] The pipeline was: ``` proto/*.proto -> protoc + protoc-gen-es -> gen-ts/*.ts -> tsc -> gen/*.js ``` **Problems with this approach:** 1. **No version pinning for protoc**: System-wide `protoc v3.21.12` installed globally. Different developers and CI environments may have different versions, breaking reproducible builds. 2. **Two-stage generation process**: `protoc-gen-es` generates `.ts` files with `enum` (non-erasable syntax) that Node.js 25.2.0+ cannot execute directly. An intermediate `tsc` step is required to compile to `.js` (see [ADR-001](./001-native-typescript-migration.md)). 3. **No proto file linting**: No style checks, naming conventions, or best practices enforcement for `.proto` files. 4. **No breaking change detection**: Proto contract changes could silently break compatibility between services. 5. **No declarative configuration**: The `protoc:generate` command contained a long `find ... -exec protoc ...` string in package.json, hard to read and maintain. 6. **Vendor protos mixed with user protos**: No clear separation between third-party protos (google, grpc, buf, openapiv3) and project-owned proto files. ### Requirements 1. **Reproducible builds**: Identical results on any machine and in CI 2. **Proto quality**: Linting and style checking for proto files 3. **Backward compatibility**: Breaking change detection in proto contracts 4. **Simplicity**: Declarative configuration instead of imperative scripts 5. **Fallback**: Ability to revert to protoc if problems arise with buf *** ## Decision **We adopt [Buf CLI v2](https://buf.build/docs/cli/) as the primary tool for proto code generation, linting, and breaking change detection, while keeping protoc as a fallback.** ### Configuration #### buf.yaml (module and rules) ```yaml version: v2 modules: - path: proto lint: use: - STANDARD ignore: - proto/google - proto/grpc - proto/buf - proto/openapiv3 breaking: use: - FILE ignore: - proto/google - proto/grpc - proto/buf - proto/openapiv3 ``` **Configuration rationale:** * **STANDARD lint rules**: Balance between strictness and practicality. Includes naming conventions, package structure, import checks. * **FILE level breaking detection**: Checks breaking changes at the file level (renaming, removing fields/services). Less strict than WIRE (allows renumbering reserved fields). * **Vendor protos in ignore**: Third-party files (google, grpc, buf, openapiv3) excluded from lint and breaking checks -- we don't control their format. #### buf.gen.yaml (code generation) ```yaml version: v2 clean: true inputs: - directory: proto plugins: - local: protoc-gen-es out: gen-ts opt: - target=ts - import_extension=.js include_imports: true ``` **Configuration rationale:** * **`clean: true`**: Automatically cleans the output directory before generation. Eliminates stale file issues. * **`local: protoc-gen-es`**: Uses a locally installed plugin (via npm). Buf invokes it the same way as protoc, but with managed dependency resolution. * **`include_imports: true`**: Generates code for imported proto files (google/protobuf, buf/validate, etc.). ### npm scripts ```json { "scripts": { "buf:generate": "buf generate", "buf:lint": "buf lint", "buf:breaking": "buf breaking --against '../../.git#branch=main,subdir=packages/proto'", "protoc:generate": "mkdir -p gen-ts && find proto -name '*.proto' -exec pnpm exec protoc -I proto --es_out=gen-ts --es_opt=target=ts,import_extension=.js {} +", "build:proto": "pnpm run buf:generate && pnpm run build:proto:compile", "build:proto:protoc": "pnpm run protoc:generate && pnpm run build:proto:compile", "build:proto:compile": "tsc --project tsconfig.build.json", "clean": "rm -rf gen gen-ts" } } ``` **Two generation paths:** | Command | Tool | When to use | |---------|------|-------------| | `build:proto` | **Buf CLI** (primary) | Default path: `buf generate` + `tsc` | | `build:proto:protoc` | **protoc** (fallback) | When Buf CLI has issues | ### Dependency Management ```json { "devDependencies": { "@bufbuild/buf": "catalog:", "@bufbuild/protoc-gen-es": "catalog:", "typescript": "catalog:" } } ``` * **`@bufbuild/buf`**: Buf CLI as npm devDependency. Version pinning via pnpm catalog. Reproducible builds without global installation. * **`@bufbuild/protoc-gen-es`**: Plugin for generating ES-compatible TypeScript code from proto files. *** ## Consequences ### Positive 1. **Version pinning via npm** -- `@bufbuild/buf` is pinned in pnpm catalog. All developers and CI use the same version. Reproducible builds guaranteed. 2. **Built-in proto linting** -- STANDARD rules cover naming conventions, structure, and best practices. Proto errors caught before code generation. `buf lint` integrates into CI. 3. **Breaking change detection** -- `buf breaking --against '../../.git#branch=main,subdir=packages/proto'` compares against previous version in git. FILE level detection catches removal/renaming of fields, services, and methods. 4. **Simplified CI/CD** -- One tool for lint, breaking check, and generation. Declarative config (buf.yaml, buf.gen.yaml) instead of long shell commands. `clean: true` eliminates stale file issues. 5. **Declarative configuration** -- buf.yaml describes the module, lint rules, and breaking rules. buf.gen.yaml describes plugins and output. Easy to read, understand, and maintain. 6. **Automatic output cleanup** -- `clean: true` in buf.gen.yaml deletes gen-ts/ before each generation, eliminating orphaned files when protos are removed. ### Negative 1. **Additional devDependency (~50MB)** -- `@bufbuild/buf` adds ~50MB to node\_modules. Increases `pnpm install` time. **Mitigation:** devDependency only, does not affect production bundle. 2. **tsc step still required** -- `protoc-gen-es` generates TypeScript with `enum` (non-erasable syntax). Node.js 25.2.0+ cannot execute enum directly. Two-step process remains: `buf generate` -> `tsc`. **Mitigation:** Awaiting native enum support in Node.js (see [ADR-001](./001-native-typescript-migration.md)). 3. **Two generation paths (complexity)** -- `build:proto` (Buf) and `build:proto:protoc` (protoc fallback) create two paths that must both be maintained. **Mitigation:** protoc fallback is for emergencies only. Primary path is buf. 4. **Buf ecosystem dependency** -- buf.yaml and buf.gen.yaml are Buf-specific formats. Reverting from Buf would require a reverse migration. **Mitigation:** protoc fallback is preserved; reverse migration is trivial. *** ## Alternatives Considered ### Alternative 1: Keep only protoc (status quo) **Rating:** 3/10 -- **REJECTED** **Pros:** No additional dependencies; simple and well-known tool; widely used in the industry. **Cons:** No version pinning (system protoc); no proto linting; no breaking change detection; imperative configuration (long shell commands); no automatic output cleanup. **Why rejected:** Lack of lint, breaking detection, and version pinning creates risks for proto contract quality and build reproducibility. ### Alternative 2: Full migration to Buf (no protoc fallback) **Rating:** 7/10 -- **REJECTED** **Pros:** Simpler maintenance (single path); no configuration duplication; fewer scripts in package.json. **Cons:** No fallback if Buf CLI has issues; blocks work on critical Buf bugs; single-tool dependency. **Why rejected:** For a production-grade framework, having a fallback is important. If Buf CLI breaks in a new version, protoc allows work to continue without blocking. ### Alternative 3: Buf BSR (Buf Schema Registry) **Rating:** 4/10 -- **REJECTED** **Pros:** Centralized proto storage; proto versioning via registry; dependency management for protos (like npm for JS); hosted documentation. **Cons:** Over-engineering for vendor protos; requires Buf BSR account; network dependency during generation; additional setup and maintenance complexity; vendor protos (google, grpc) are already available locally. **Why rejected:** The project uses vendor proto files that rarely change. BSR adds complexity without proportionate benefit. Worth reconsidering when shared proto between multiple projects is needed. *** ## Implementation ### Created Files | File | Purpose | |------|---------| | `packages/proto/buf.yaml` | Module, lint, and breaking rules configuration | | `packages/proto/buf.gen.yaml` | Code generation configuration | ### Updated Files | File | Change | |------|--------| | `packages/proto/package.json` | Added `buf:*` scripts, `@bufbuild/buf` devDependency | | `turbo.json` | Added `buf:lint` task | | `.gitignore` | Added patterns for Buf CLI | ### Commands > **Update (2026-02-12):** Commands below refer to the removed `@connectum/proto` package. Buf CLI is still used by other packages for lint and code generation. See [ADR-003](./003-package-decomposition.md) for removal details. ```bash # [Historical: @connectum/proto commands] # Primary path (Buf CLI) pnpm --filter @connectum/proto build:proto # Lint proto files pnpm --filter @connectum/proto buf:lint # Check breaking changes pnpm --filter @connectum/proto buf:breaking # Fallback (protoc) pnpm --filter @connectum/proto build:proto:protoc # Clean pnpm --filter @connectum/proto clean ``` *** ## References 1. **Buf CLI Documentation** * Official: https://buf.build/docs/cli/ * buf.yaml v2: https://buf.build/docs/configuration/v2/buf-yaml/ * buf.gen.yaml v2: https://buf.build/docs/configuration/v2/buf-gen-yaml/ 2. **Buf Lint Rules** * STANDARD rules: https://buf.build/docs/lint/rules/ * Style guide: https://buf.build/docs/best-practices/style-guide/ 3. **Buf Breaking Rules** * FILE level: https://buf.build/docs/breaking/rules/ * Categories: https://buf.build/docs/breaking/overview/ 4. **Implementation Files** \[Update: @connectum/proto removed, these files no longer exist] * buf.yaml: `packages/proto/buf.yaml` * buf.gen.yaml: `packages/proto/buf.gen.yaml` * package.json: `packages/proto/package.json` 5. **Related ADRs** * [ADR-001: Native TypeScript Migration](./001-native-typescript-migration.md) -- Enum workaround (tsc step) * [ADR-003: Package Decomposition](./003-package-decomposition.md) -- @connectum/proto package \[Update: @connectum/proto removed, see ADR-003] *** ## Changelog | Date | Author | Change | |------|--------|--------| | 2026-02-06 | Claude | Initial ADR -- Buf CLI v2 migration | *** ## Future Considerations ### Removing the tsc step (Node.js enum support) When Node.js adds stable `enum` support (via `--experimental-transform-types` -> stable), it will be possible to: 1. Remove `tsconfig.build.json` 2. Use `gen-ts/` directly as `gen/` 3. Simplify `build:proto` to a single step: `buf generate` ### Buf BSR for shared protos When multiple projects share common proto contracts: 1. Create shared protos in Buf BSR 2. Use `buf dep update` for dependency management 3. Version proto contracts via BSR ### Removing protoc fallback After stable Buf CLI operation across several releases: 1. Remove `protoc:generate` and `build:proto:protoc` scripts 2. Simplify documentation 3. Keep only the single generation path ### CI/CD integration 1. `buf lint` in pre-commit hook or CI pipeline 2. `buf breaking` in CI for pull requests (breaking change protection) 3. Automatic code generation on `.proto` file changes --- --- url: /en/contributing/adr/014-method-filter-interceptor.md --- # ADR-014: Per-Method Interceptor Routing (createMethodFilterInterceptor) **Status:** Accepted - 2026-02-07 **Deciders:** Tech Lead, Platform Team **Tags:** `interceptors`, `per-method`, `routing`, `filter`, `wildcard`, `moleculer-inspired` **Supersedes:** Original ADR-014 (Per-Method Action Hooks) -- rejected after Codex Debater review due to introducing a second execution model alongside interceptors. *** ## Context ### Problem: no per-method customization Connectum interceptors form a flat chain applied to **all** RPC methods uniformly. The current interceptor chain in `createServer()` (see [ADR-006](./006-resilience-pattern-implementation.md)): ``` Error Handler -> Validation -> Serializer -> Logger -> Tracing -> Redact -> [Custom Interceptors] -> Handler ``` **Limitation:** If `checkAuth` should only apply to `UserService/GetUser` and `rateLimit` only to `PaymentService/*`, the only option is writing a custom interceptor with manual filtering: ```typescript // Current approach: manual filtering inside the interceptor const authInterceptor: Interceptor = (next) => async (req) => { if (req.method.name === "GetUser" && req.service.typeName === "UserService") { await checkAuth(req); } return next(req); }; ``` This leads to: * **Boilerplate**: every custom interceptor contains if/else filtering logic * **No standard API**: each developer implements filtering differently * **Composition difficulty**: no declarative way to describe per-method behavior ### Rejected approach: Action Hooks An approach with hooks (before/after/error) inspired by Moleculer was initially considered. **Rejected** after critical analysis: 1. **Two execution models**: interceptors + hooks create confusion ("where do I add auth check?") 2. **Increased core surface area**: hooks added new types (BeforeHook, AfterHook, ErrorHook, HookContext) to `@connectum/core` 3. **Error handling anti-pattern**: error hooks with chain interruption (`return true`) contradicted standard ConnectRPC error handling via `ConnectError` **Codex Debater recommendation**: keep the single interceptor pattern, add a convenience helper for per-method routing. ### Current interceptors (12 total) | Category | Interceptors | |----------|-------------| | **Core (builtin)** | errorHandler, logger, serializer, tracing, validation, redact | | **Auth** | addToken (JWT) | | **Resilience (opt-in)** | retry, circuit-breaker, timeout, bulkhead, fallback | *** ## Decision **Implement `createMethodFilterInterceptor` in `@connectum/interceptors` -- a convenience helper for per-method interceptor routing within the single interceptor pattern.** ### API Design ```typescript import { createMethodFilterInterceptor } from "@connectum/interceptors"; const perMethodInterceptor = createMethodFilterInterceptor({ // Exact match: specific method "user.v1.UserService/GetUser": [checkAuth, enrichUser], // Service wildcard: all methods of a service "payment.v1.PaymentService/*": [validatePayment, auditLog], // Global wildcard: all methods "*": [logRequest], }); const server = createServer({ services: [routes], interceptors: [perMethodInterceptor], }); ``` ### createMethodFilterInterceptor Signature ```typescript import type { Interceptor } from "@connectrpc/connect"; /** * Method pattern -> array of interceptors mapping. * * Patterns: * - "*" -- matches all methods * - "package.Service/*" -- matches all methods of a service * - "package.Service/Method" -- matches exact method * * Key format: service.typeName + "/" + method.name (full protobuf path) */ type MethodFilterMap = Record; interface MethodFilterOptions { /** * Per-method interceptor routing map. */ methods: MethodFilterMap; /** * Skip streaming calls for all interceptors in this filter. * @default false */ skipStreaming?: boolean; } /** * Create a single interceptor that routes to per-method interceptors * based on wildcard pattern matching. * * Resolution order (all matching patterns execute): * 1. Global wildcard "*" (executed first) * 2. Service wildcard "Service/*" (executed second) * 3. Exact match "Service/Method" (executed last) * * Within each pattern, interceptors execute in array order. */ function createMethodFilterInterceptor( methods: MethodFilterMap ): Interceptor; // Overload with options function createMethodFilterInterceptor( options: MethodFilterOptions ): Interceptor; ``` ### Execution Order All matching patterns execute **sequentially** (from general to specific): ``` Request: user.v1.UserService/GetUser 1. "*": [logRequest] -- global (always runs) 2. "user.v1.UserService/*": [] -- service-level (if defined) 3. "user.v1.UserService/GetUser": [checkAuth, enrichUser] -- exact match Total chain: logRequest -> checkAuth -> enrichUser -> next(req) ``` This follows the ConnectRPC interceptor chain model -- each interceptor calls `next(req)`, forming a nested chain. ### Usage Examples ```typescript // === Example 1: Auth per service === createMethodFilterInterceptor({ "*": [logRequest], "admin.v1.AdminService/*": [requireAdmin], "user.v1.UserService/DeleteUser": [requireAdmin, auditLog], }); // === Example 2: Resilience per method === createMethodFilterInterceptor({ // 5s timeout for fast operations "catalog.v1.CatalogService/GetProduct": [ createTimeoutInterceptor({ duration: 5_000 }), ], // 30s timeout for heavy operations "report.v1.ReportService/*": [ createTimeoutInterceptor({ duration: 30_000 }), createCircuitBreakerInterceptor({ threshold: 3 }), ], }); // === Example 3: Combine with server interceptors === createServer({ services: [routes], interceptors: [ // Global interceptors (all methods) createDeadlineInterceptor({ defaultTimeout: 30_000 }), // Per-method interceptors createMethodFilterInterceptor({ "payment.v1.PaymentService/*": [ createCircuitBreakerInterceptor({ threshold: 5 }), createRetryInterceptor({ maxRetries: 3 }), ], }), ], }); ``` ### Pattern Matching Implementation ```typescript // Matching logic (simplified) function matchesPattern( pattern: string, serviceName: string, methodName: string, ): boolean { if (pattern === "*") return true; if (pattern.endsWith("/*")) { const servicePattern = pattern.slice(0, -2); return serviceName === servicePattern; } return pattern === `${serviceName}/${methodName}`; } // Pre-compiled at creation time for performance // No regex -- simple string comparison and endsWith check ``` ### Performance * **Pattern matching**: O(n) where n = number of patterns. Pre-compiled map lookup for exact matches, linear scan only for wildcards. * **Optimization**: At `createMethodFilterInterceptor()` call time, patterns are split into 3 groups (global, service, exact). Exact matches use Map lookup O(1). Service wildcards use Map lookup O(1). Global always executes. * **Overhead**: 1 Map lookup + 1 Map lookup per request. Negligible for real workloads. *** ## Consequences ### Positive * **Single execution model**: Interceptors only. No "interceptor vs hook" confusion. ConnectRPC interceptor pattern is the only pattern for cross-cutting concerns. * **Per-method routing without boilerplate**: Declarative configuration instead of if/else in every interceptor. * **Composable**: createMethodFilterInterceptor returns a regular Interceptor. Multiple instances can be used and combined with other interceptors. * **Package: @connectum/interceptors (Layer 1)**: Does not increase `@connectum/core` surface area. Follows [ADR-003](./003-package-decomposition.md). * **Wildcards**: `*` for global, `Service/*` for service-level -- covers typical use cases. * **Zero new concepts**: Uses existing ConnectRPC Interceptor types. No BeforeHook, AfterHook, HookContext, or other new types. ### Negative * **Interceptor nesting**: Each pattern creates a nested interceptor chain. For 10+ patterns the call stack may become deep. **Mitigation**: >10 patterns is rare in production; performance benchmark in Phase 1. * **No after/error specific handling**: Unlike hooks, an interceptor sees the entire lifecycle (before+after+error in one). Separate after/error handling requires a custom interceptor. **Mitigation**: this is the standard ConnectRPC pattern, not a limitation. * **Limited pattern syntax**: Only 3 variants (*, Service/*, Service/Method). No regex, no multi-level wildcards. **Mitigation**: covers 95% of use cases; for complex routing use a custom interceptor. *** ## Alternatives Considered ### Alternative 1: Action Hooks (before/after/error) -- REJECTED **Rating:** 4/10 **Description:** Hooks API in createServer() options -- a 3-level hook system inspired by Moleculer. **Pros:** Declarative, per-method, separate before/after/error. **Cons:** Second execution model, increases core surface area, error chain interruption anti-pattern. **Why rejected:** Codex Debater analysis revealed a fundamental issue: two execution models (interceptors + hooks) create confusion. A single interceptor pattern is cleaner and simpler. ### Alternative 2: Filter function per interceptor **Rating:** 5/10 ```typescript createTimeoutInterceptor({ duration: 5000, filter: (req) => req.service.typeName === "PaymentService", }); ``` **Pros:** Simple implementation, single pattern. **Cons:** Each interceptor duplicates filter logic, no centralized configuration, no wildcards. **Why rejected:** Distributed filter logic instead of centralized routing. createMethodFilterInterceptor provides a single configuration point. ### Alternative 3: Decorator pattern (@Method) **Rating:** 3/10 **Why rejected:** Node.js 25.2.0+ type stripping does not support decorators ([ADR-001](./001-native-typescript-migration.md)). ConnectRPC uses function-based handlers, not classes. *** ## Implementation Plan ### Phase 1: Core Implementation (v0.2.x) **Package:** `@connectum/interceptors` **New files:** * `packages/interceptors/src/method-filter.ts` -- createMethodFilterInterceptor implementation * `packages/interceptors/tests/unit/method-filter.test.ts` -- unit tests **Modified files:** * `packages/interceptors/src/index.ts` -- export createMethodFilterInterceptor * `packages/interceptors/src/types.ts` -- MethodFilterMap, MethodFilterOptions types **Tests:** * Pattern matching: *, Service/*, Service/Method * Execution order: global -> service -> exact * Empty pattern array (no-op) * Multiple matching patterns * skipStreaming option * Integration with existing interceptors *** ## References 1. [ADR-006: Resilience Pattern Implementation](./006-resilience-pattern-implementation.md) -- interceptor chain, resilience patterns 2. ADR-010: Framework vs Infrastructure (internal planning document) -- optional interceptors, boundary 3. [ADR-003: Package Decomposition](./003-package-decomposition.md) -- Layer 1: @connectum/interceptors 4. [ConnectRPC Interceptors](https://connectrpc.com/docs/node/interceptors/) -- interceptor model 5. [Moleculer Action Hooks](https://moleculer.services/docs/0.14/actions.html#Action-hooks) -- inspiration (rejected approach) *** ## Changelog | Date | Author | Change | |------|--------|--------| | 2026-02-07 | Tech Lead | Initial ADR: Per-Method Action Hooks (rejected after review) | | 2026-02-07 | Tech Lead | Rewrite: createMethodFilterInterceptor (single interceptor model). Status: Accepted | --- --- url: /en/contributing/adr/020-reflection-proto-sync.md --- # ADR-020: Reflection-based Proto Synchronization **Status:** Accepted - 2026-02-11 (Phase 1 DONE, Phase 2 DONE) **Deciders:** Tech Lead, Platform Team **Tags:** `protobuf`, `reflection`, `proto-sync`, `cli`, `code-generation`, `openapi`, `npm`, `buf`, `grpcurl` **Related:** Extends [ADR-009: Buf CLI Migration](./009-buf-cli-migration.md), uses [ADR-003: Package Decomposition](./003-package-decomposition.md) (@connectum/proto -- package since removed, see ADR-003 update notes). *** ## Context ### Problem: proto synchronization between server and client A key challenge with protobuf is distributing proto definitions and generated types between a server and its clients. Currently, clients of Connectum services have no standard way to obtain proto files or generated TypeScript types. ### Current State #### 1. Reflection Server (incomplete) The file `packages/reflection/src/Reflection.ts` implements `grpc.reflection.v1.ServerReflection` (bidirectional streaming), but with critical TODOs: ```typescript // withReflection.ts -- current implementation (BROKEN) // fileByFilename and fileContainingSymbol return EMPTY descriptors: fileDescriptorProto: [], // TODO: Serialize file descriptors (lines 95, 117) ``` This means `grpcurl`, `buf curl`, Postman, and any reflection-based tools cannot obtain schema from a Connectum server. #### 2. Ready-made solution in the ConnectRPC ecosystem The package [`@lambdalisue/connectrpc-grpcreflect`](https://www.npmjs.com/package/@lambdalisue/connectrpc-grpcreflect) provides a **complete implementation** of the gRPC Server Reflection Protocol for ConnectRPC: * **Server-side**: `registerServerReflectionFromFileDescriptorSet()` -- registers reflection v1 + v1alpha on ConnectRouter * **Client-side**: `ServerReflectionClient` -- service discovery, FileDescriptorProto download, `buildFileRegistry()` * **Dependencies**: `@bufbuild/protobuf` ^2.10.1, `@connectrpc/connect` ^2.1.1 -- **exact match** with Connectum * **License**: MIT, 115 passing tests, active development #### 3. @connectum/proto package (Layer 0) \[Update: REMOVED, see ADR-003] > **Update (2026-02-12):** The `@connectum/proto` package has been removed from the monorepo. The description below is preserved for historical context. The package already contained proto definitions and generated types, but was not published to npm. > **Note**: gRPC Health Check and Reflection protos were removed from `@connectum/proto`. Health proto moved to `@connectum/healthcheck`, Reflection uses `@lambdalisue/connectrpc-grpcreflect`. WKT (`google/protobuf/*`) remain as build-time dependencies but are not exported (available from `@bufbuild/protobuf`). #### 4. Dependencies * `@bufbuild/protobuf` v2.10.2 -- contains `toBinary()`, `createFileRegistry()`, `FileDescriptorProtoSchema` * `@connectrpc/connect` v2.1.1 -- ConnectRPC framework * `@bufbuild/buf` -- Buf CLI v2 for code generation ([ADR-009](./009-buf-cli-migration.md)) * `@lambdalisue/connectrpc-grpcreflect` -- ConnectRPC-native reflection (server + client), compatible with `@bufbuild/protobuf` ^2.10.1 ### Requirements 1. **Standard way to obtain types**: Clients of Connectum services should get TypeScript types without manually copying proto files 2. **Working reflection**: grpcurl, buf curl, Postman should get full schema from dev/staging server 3. **Dev convenience**: Single command to sync types with a running server 4. **Security**: Reflection should not be enabled in production by default 5. **Use existing tools**: Minimum custom code, maximum existing npm packages and buf CLI *** ## Decision **Implement a phased proto synchronization strategy in 4 phases: npm publish (Phase 0), reflection server replacement (Phase 1), reflection CLI (Phase 2), OpenAPI generation (Phase 3).** ### Strategy: simple to complex > **Update (2026-02-12):** The original insight below has been revised. `@connectum/proto` was removed; instead of npm publish, proto distribution is handled via BSR deps + `buf.lock` and `@connectum/cli proto sync` (Phase 2). Phase 1 and Phase 2 remain the primary mechanisms. Current insight -- **proto distribution is solved by two mechanisms**: (1) BSR deps in `buf.yaml` for third-party proto definitions; (2) `@connectum/cli proto sync` for obtaining types from a running server via gRPC Reflection. ### Phase 0: BSR deps approach (v0.2.0) -- REVISED > **Update (2026-02-12): Phase 0 revised.** The `@connectum/proto` package was removed from the monorepo (see [ADR-003](./003-package-decomposition.md)). Instead of npm-publishing `@connectum/proto`, the recommended approach is BSR deps in `buf.yaml` for third-party proto definitions (`buf.build/googleapis/googleapis`, `buf.build/bufbuild/protovalidate`, etc.). Framework clients obtain types via `@connectum/cli proto sync` (Phase 2) from a running server or via their own `buf.yaml` with BSR deps + `buf.lock`. Connectum is a framework that provides proto distribution tools, not vendored definitions. **Current approach**: Clients use BSR deps in their `buf.yaml`: ```yaml # Client's buf.yaml version: v2 deps: - buf.build/googleapis/googleapis - buf.build/bufbuild/protovalidate ``` Or obtain types via the reflection CLI: ```bash connectum proto sync --from localhost:5000 --out ./generated/ ``` ### Phase 1: Replace Reflection Server with @lambdalisue/connectrpc-grpcreflect (v0.3.0) Instead of fixing the custom `withReflection.ts`, replace it with the proven community package `@lambdalisue/connectrpc-grpcreflect`, which: * Correctly serializes FileDescriptorProto (including transitive dependencies) * Supports gRPC Reflection v1 + v1alpha (auto-detection) * Uses the same `router.service()` pattern * Fully compatible with `@bufbuild/protobuf` ^2.10.1 and `@connectrpc/connect` ^2.1.1 ```typescript import { registerServerReflectionFromFileDescriptorSet } from "@lambdalisue/connectrpc-grpcreflect/server"; import { create, toBinary } from "@bufbuild/protobuf"; import { FileDescriptorSetSchema } from "@bufbuild/protobuf/wkt"; import type { DescFile } from "@bufbuild/protobuf"; /** * Convert DescFile[] (collected by Server.ts from router.service()) * to FileDescriptorSet for @lambdalisue/connectrpc-grpcreflect. */ function buildFileDescriptorSet(files: DescFile[]): Uint8Array { const set = create(FileDescriptorSetSchema, { file: files.map((f) => f.proto), }); return toBinary(FileDescriptorSetSchema, set); } // In Server.ts when registering reflection: if (reflection) { const binpb = buildFileDescriptorSet(registry); registerServerReflectionFromFileDescriptorSet(router, binpb); // Registers v1 + v1alpha automatically } ``` This replaces the **entire** custom `withReflection.ts` (143 lines with two TODOs) with ~10 lines of integration code. ### Security Model Reflection is explicitly enabled and disabled by default: > **Note (deprecated):** The `reflection` option was replaced by `protocols: [Reflection()]` in v1.0.0-beta.1 (see [ADR-022](/en/contributing/adr/022-protocol-extraction)). The examples below are kept for historical context. ```typescript const server = createServer({ services: [routes], port: 5000, // Reflection DISABLED by default reflection: false, }); // Typical pattern: enable only in development/staging const server = createServer({ services: [routes], port: 5000, reflection: process.env.NODE_ENV !== "production", }); // Explicit enablement (deliberate developer decision) const server = createServer({ services: [routes], port: 5000, reflection: true, }); ``` ### Phase 2: Reflection CLI MVP (v0.3.0) CLI tool for syncing types with a running development server: ```bash # Sync proto types with a running dev server connectum proto sync --from localhost:5000 --out ./generated/ # Specific services only connectum proto sync --from localhost:5000 --services "user.v1.*,order.v1.*" # Dry-run: show what will be synced connectum proto sync --from localhost:5000 --dry-run # With custom buf.gen.yaml configuration connectum proto sync --from localhost:5000 --config ./buf.gen.yaml ``` CLI pipeline architecture: ``` +--------------+ +----------------------+ +-----------------+ +------------+ | Running | | ServerReflectionClient| | FileDescriptorSet| | buf | | Connectum |--->| (@lambdalisue/ |--->| .binpb file |--->| generate | | Server | | connectrpc- | | (binary proto) | | (codegen) | | | | grpcreflect/client) | | | | | +--------------+ +----------------------+ +-----------------+ +------------+ | | | | gRPC Reflection listServices() + toBinary() -> TypeScript Protocol buildFileRegistry() FileDescriptorSet stubs in (auto v1/v1alpha) (ConnectRPC native) -> .binpb file --out dir ``` Key advantages: * **ConnectRPC-native** -- uses `@connectrpc/connect-node` transport, `@bufbuild/protobuf` types. No foreign dependencies (`@grpc/grpc-js`, `google-protobuf`). * **Single package** -- `@lambdalisue/connectrpc-grpcreflect` is used both server-side (Phase 1) and client-side (Phase 2). * **No .proto reconstruction** -- feeds binary FileDescriptorSet directly to `buf generate` ([Buf Inputs Reference](https://buf.build/docs/reference/inputs/)). ### Phase 3: OpenAPI Generation (v0.4.0) Add OpenAPI v3.1 generation from proto definitions for HTTP/JSON clients: ```bash # Generate OpenAPI from proto connectum proto openapi --out ./docs/openapi.yaml ``` ```yaml # buf.gen.yaml -- additional plugin plugins: - local: protoc-gen-es out: gen-ts opt: - target=ts - import_extension=.js include_imports: true - local: protoc-gen-connect-openapi out: docs opt: - format=yaml ``` Swagger UI can be connected as static HTML or a dev server endpoint for API visualization. *** ## Consequences ### Positive * **Zero manual proto distribution** -- a single command (`connectum proto sync` or BSR deps in `buf.yaml`) syncs types * **Always in sync** with the running server -- reflection CLI guarantees type freshness in development * **Low implementation complexity** -- 2-4 weeks instead of 6-10 thanks to ready-made tools (`@lambdalisue/connectrpc-grpcreflect`, `buf generate`, `protoc-gen-connect-openapi`) * **buf generate compatible** -- standard codegen pipeline, not a custom solution * **grpcurl/Postman support** -- completed reflection server enables service debugging and exploration * **Progressive complexity** -- BSR deps for the basic case, Phase 2 (CLI) for dev convenience, Phase 3 (OpenAPI) for web developers * **OpenAPI generation** -- documentation for HTTP/JSON clients, Swagger UI for free ### Negative * **Requires running server** (Phase 2) -- CLI depends on dev server availability for reflection. **Mitigation**: BSR deps in `buf.yaml` don't require a running server and cover the basic use case. * **Loss of comments** -- FileDescriptorProto does not contain comments from .proto files. **Mitigation**: not needed for codegen; source of truth for documentation = git-managed .proto files. * **Security risk** -- reflection in production exposes full API schema. **Mitigation**: disabled by default (`reflection: false`), opt-in per environment, documentation with warnings. * **Extra dependency** -- `@lambdalisue/connectrpc-grpcreflect` adds a dependency to `@connectum/core`. **Mitigation**: the package uses the same `@bufbuild/protobuf` and `@connectrpc/connect` already in the project -- zero new transitive dependencies. MIT license, 115 passing tests. *** ## Alternatives Considered ### Alternative 1: Buf Schema Registry (BSR) **Rating:** 6/10 **Description:** Managed registry with versioning, breaking change detection, multi-language SDK generation. Clients obtain types via `buf generate --from buf.build/org/api`. **Pros:** Semver versioning for proto contracts; multi-language SDK generation (Go, Java, Python, TypeScript); built-in breaking change detection; hosted documentation. **Cons:** External SaaS dependency (vendor lock-in); over-engineering for an alpha single-language framework; additional infrastructure and account; network dependency during generation. **Why rejected for now:** BSR is worth considering when multi-language SDK generation is needed. ### Alternative 2: Git Submodules for proto files **Rating:** 5/10 **Description:** Shared proto repository as a git submodule in each client project. **Pros:** Simple setup; git tags for versioning; all comments preserved; no external dependencies. **Cons:** Manual sync (git submodule update); git submodules are a known pain point (detached HEAD, nested repos); no automatic code generation; doesn't scale with growing client count. **Why rejected:** Git submodules create friction in the developer workflow. Manual synchronization contradicts the goal of zero manual distribution. ### Alternative 3: Only npm Package (no Reflection CLI) **Rating:** 7/10 **Description:** Publish `@connectum/proto` with generated TypeScript types to npm, without a reflection CLI. **Pros:** Simplest approach; semver versioning via changesets; works with existing npm ecosystem tools; no additional dependencies. **Cons:** Manual publish cycle for every proto change; no auto-sync with dev server; reflection server remains broken (no grpcurl/Postman support). **Why partially accepted:** Phase 0 used this approach as a baseline. Reflection CLI (Phase 2) supplements it for dev convenience. ### Alternative 4: OpenAPI-only (no gRPC Reflection) **Rating:** 5/10 **Description:** ConnectRPC supports HTTP/JSON, OpenAPI is generated via `protoc-gen-connect-openapi`, clients use `openapi-generator` for any language. **Pros:** Familiar to web developers; Swagger UI documentation for free; multi-language clients via openapi-generator; REST-like API exploration. **Cons:** Not suitable for gRPC-native clients (streaming, binary efficiency); loses streaming support (OpenAPI doesn't describe bidirectional streaming); duplication -- OpenAPI and protobuf describe the same API. **Why partially accepted:** Phase 3 adds OpenAPI generation as a supplement to gRPC reflection, not a replacement. ### Alternative 5: grpc-js-reflection-client (npm) **Rating:** 3/10 **Description:** Ready-made reflection client for Node.js using `@grpc/grpc-js` and `google-protobuf`. **Pros:** Ready-made solution, zero custom reflection code; active maintenance. **Cons:** Uses `@grpc/grpc-js` -- incompatible with ConnectRPC transport ecosystem; uses `google-protobuf` -- incompatible with `@bufbuild/protobuf` (Connectum standard); two competing protobuf runtimes in one project; no type interop between `google-protobuf` and `@bufbuild/protobuf` types. **Why rejected:** Incompatible dependency ecosystem. Connectum is fully built on `@bufbuild/protobuf` + `@connectrpc/connect`. Mixing with `@grpc/grpc-js` + `google-protobuf` creates dependency hell and type mismatches. `@lambdalisue/connectrpc-grpcreflect` provides the same functionality in ConnectRPC-native form. ### Alternative 6: Full Custom CLI (no buf generate) **Rating:** 4/10 **Description:** Custom .proto reconstruction from FileDescriptorProto + custom TypeScript codegen. Fully hand-written solution. **Pros:** No dependency on Buf CLI; full control over output; can add custom logic. **Cons:** 6-10 weeks of development instead of 2-4; reinventing the wheel (buf generate already does this); loss of comments during .proto reconstruction; custom codegen requires ongoing maintenance; bug parity with `protoc-gen-es` is impossible. **Why rejected:** `buf generate` accepts binary FileDescriptorSet (`.binpb`) directly -- no need to reconstruct `.proto` text files. Ready-made tools cover 100% of the pipeline. *** ## Implementation Plan ### Phase 0: BSR deps approach (v0.2.0) -- REVISED > **Update (2026-02-12):** Phase 0 revised. `@connectum/proto` removed. BSR deps approach is recommended instead of npm publish. **Current approach:** 1. Clients add third-party proto deps to their `buf.yaml` via BSR: `buf.build/googleapis/googleapis`, `buf.build/bufbuild/protovalidate`, etc. 2. Run `buf dep update` to update `buf.lock` 3. Use `buf generate` for code generation from their own proto files 4. Use `connectum proto sync` (Phase 2) to obtain types from a running server ### Phase 1: Replace Reflection Server (v0.3.0) -- 3-4 days 1. Add `@lambdalisue/connectrpc-grpcreflect` to `@connectum/core` dependencies 2. Replace custom `withReflection.ts` with `registerServerReflectionFromFileDescriptorSet()` 3. Convert `DescFile[]` registry to `FileDescriptorSet` binary 4. Remove legacy handling from `Server.ts` 5. Change `reflection` default to `false` in `createServer()` 6. Update unit and integration tests 7. Integration tests with `grpcurl` and `buf curl` 8. Document security implications ### Phase 2: Reflection CLI MVP (v0.3.0) -- 1-2 weeks 1. Create `@connectum/cli` package or subcommand in existing CLI 2. Use `ServerReflectionClient` from `@lambdalisue/connectrpc-grpcreflect/client` (ConnectRPC-native, zero foreign dependencies) 3. Implement pipeline: `ServerReflectionClient` -> `buildFileRegistry()` -> `.binpb` -> `buf generate` -> output 4. CLI interface: `connectum proto sync --from --out ` 5. Support `--dry-run`, `--services` filter, `--config` for custom buf.gen.yaml 6. Integration tests: sync against running Connectum server 7. Add output directory to `.gitignore` template ### Phase 3: OpenAPI Generation (v0.4.0) -- 1 week 1. Integrate `protoc-gen-connect-openapi` into buf.gen.yaml 2. Generate OpenAPI v3.1 from proto definitions 3. Swagger UI setup (static HTML or dev server endpoint) 4. Documentation for HTTP/JSON clients in guide/ *** ## Implementation Status ### Phase 1: Reflection Server -- DONE (2026-02-11) All Phase 1 tasks completed: | Task | Status | Description | |------|--------|-------------| | #34 | DONE | `withReflection.ts` replaced with `@lambdalisue/connectrpc-grpcreflect` server | | #24 | DONE | `reflection` default changed to `false` in `createServer()` | | #25 | DONE | Unit tests rewritten with real `GenFile` descriptors | | #26 | DONE | Integration test: real server + `ServerReflectionClient` verifying `listServices`, `getFileContainingSymbol`, `buildFileRegistry`, `getServiceDescriptor` | | #27 | DONE | Documentation updated | **Key implementation details:** * Server-side: `registerServerReflectionFromFileDescriptorSet()` from `@lambdalisue/connectrpc-grpcreflect/server` * `DescFile[]` registry collected by `Server.ts` via patched `router.service()` is converted to `FileDescriptorSet` and passed to the library * Both gRPC Reflection v1 and v1alpha are registered automatically * Integration test uses `ServerReflectionClient` from `@lambdalisue/connectrpc-grpcreflect/client` with `createGrpcTransport` (HTTP/2) * Files: * `packages/reflection/src/Reflection.ts` -- server-side wrapper * `packages/reflection/tests/unit/Reflection.test.ts` -- unit tests * `packages/reflection/tests/integration/reflection.test.ts` -- integration tests ### Phase 2: CLI Tool -- DONE (2026-02-11) All Phase 2 tasks completed: | Task | Status | Description | |------|--------|-------------| | #28 | DONE | `@connectum/cli` package scaffolded: package.json, citty entry point, directory structure | | #29 | DONE | Reflection client wrapper: `fetchReflectionData()`, `fetchFileDescriptorSetBinary()` | | #30 | DONE | Pipeline: ServerReflectionClient -> .binpb -> `buf generate` -> output directory | | #31 | DONE | `--dry-run` mode: list services and files without generating code | | #32 | DONE | Integration tests: fetchReflectionData, fetchFileDescriptorSetBinary, dry-run against real server | | #33 | DONE | README.md for @connectum/cli, ADR-020 updated | **Key implementation details:** * CLI framework: `citty` with nested subcommands (`connectum proto sync`) * Reflection client: `ServerReflectionClient` from `@lambdalisue/connectrpc-grpcreflect/client` * Transport: `createGrpcTransport` from `@connectrpc/connect-node` (HTTP/2) * Binary serialization: `create(FileDescriptorSetSchema)` + `toBinary()` from `@bufbuild/protobuf` * Code generation: `buf generate --output ` via `child_process.execSync` * Temporary files cleaned up after generation **Files:** * `packages/cli/src/index.ts` -- CLI entry point * `packages/cli/src/commands/proto-sync.ts` -- proto sync command with --from, --out, --template, --dry-run * `packages/cli/src/utils/reflection.ts` -- reflection client utilities * `packages/cli/tests/integration/proto-sync.test.ts` -- integration tests * `packages/cli/README.md` -- package documentation *** ## References 1. [gRPC Server Reflection Protocol](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) -- reflection protocol specification 2. [@lambdalisue/connectrpc-grpcreflect (npm)](https://www.npmjs.com/package/@lambdalisue/connectrpc-grpcreflect) -- ConnectRPC-native reflection server + client (v1 + v1alpha, `@bufbuild/protobuf` compatible) 3. [Buf Inputs Reference (.binpb)](https://buf.build/docs/reference/inputs/) -- binary FileDescriptorSet as input for buf generate 4. [protoc-gen-connect-openapi](https://github.com/sudorandom/protoc-gen-connect-openapi) -- OpenAPI generation from proto definitions 5. [ConnectRPC gRPC Compatibility](https://connectrpc.com/docs/go/grpc-compatibility/) -- gRPC protocol support in ConnectRPC 6. [Buf Schema Registry](https://buf.build/product/bsr) -- managed proto registry (Alternative 1) 7. [@bufbuild/protobuf v2](https://buf.build/blog/protobuf-es-v2) -- `createFileRegistry()`, `toBinary()`, `FileDescriptorProtoSchema` 8. [ADR-003: Package Decomposition](./003-package-decomposition.md) -- @connectum/proto placement in Layer 0 \[Update: @connectum/proto removed, see ADR-003] 9. [ADR-009: Buf CLI Migration](./009-buf-cli-migration.md) -- buf generate pipeline, buf.gen.yaml configuration 10. ADR-010: Framework vs Infrastructure (internal planning document) -- boundary: reflection = framework, registry = infrastructure *** ## Changelog | Date | Author | Change | |------|--------|--------| | 2026-02-07 | Tech Lead | Initial ADR: Reflection-based Proto Synchronization, 4-phase roadmap | | 2026-02-10 | Tech Lead | Replace grpc-js-reflection-client with @lambdalisue/connectrpc-grpcreflect (ConnectRPC-native, server+client). Phase 1: replace withReflection.ts instead of fixing TODOs | | 2026-02-11 | Tech Lead | Phase 1 DONE: Integration tests, documentation. Status updated to Accepted | | 2026-02-11 | Tech Lead | Phase 2 DONE: @connectum/cli package with proto sync command, integration tests, documentation | | 2026-02-12 | Tech Lead | Phase 0 revised: @connectum/proto removed, replaced by BSR deps approach. See ADR-003 update | --- --- url: /en/contributing/adr/022-protocol-extraction.md --- # ADR-022: Protocol Extraction to Separate Packages ## Status **Accepted** - 2026-02-11 ## Context In the current architecture, the `@connectum/core` package contains built-in protocol implementations: * **Healthcheck** (`./protocols/healthcheck/`) -- gRPC Health Check + HTTP endpoints * **Reflection** (`./protocols/reflection/`) -- gRPC Server Reflection v1 + v1alpha ### Problems with the current approach 1. **SRP violation**: Core is responsible for both server lifecycle AND protocol implementations 2. **Excessive dependencies**: `@lambdalisue/connectrpc-grpcreflect` is pulled in even if reflection is not used 3. **Tight coupling**: Protocols are hardwired into Server.ts via dynamic `import()` calls 4. **Extensibility**: Cannot add a custom protocol without modifying core 5. **Healthcheck bugs**: `update()` without serviceName updates only the first service instead of all; enum reverse mapping in HTTP handler ### Bugs discovered (fixed in new packages) | Bug | Description | Fix | |-----|-------------|-----| | #1 | `update()` without serviceName updates only the first service | Now updates ALL registered services | | #2 | `update()` with unknown serviceName silently creates an entry | Throws Error with description | | #3 | Singleton `Healthcheck` export -- shared state between servers | Removed; each `Healthcheck()` call creates a new manager | | #4 | `watch()` interval hardcoded to 500ms | Configurable via `watchInterval` option | | #5 | `ServingStatus[status]` enum reverse mapping in HTTP handler | Replaced with explicit Map | ## Decision Extract protocols into separate Layer 1 packages: ``` @connectum/healthcheck -- gRPC Health Check + HTTP endpoints @connectum/reflection -- gRPC Server Reflection (v1 + v1alpha) ``` ### New Protocol Registration API Introduce a `ProtocolRegistration` interface in `@connectum/core`: ```typescript interface ProtocolContext { readonly registry: ReadonlyArray; } type HttpHandler = (req: Http2ServerRequest, res: Http2ServerResponse) => boolean; interface ProtocolRegistration { readonly name: string; register(router: ConnectRouter, context: ProtocolContext): void; httpHandler?: HttpHandler; } ``` ### New Usage API ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [myRoutes], protocols: [Healthcheck({ httpEnabled: true, watchInterval: 1000 }), Reflection()], }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ### Backward Compatibility Legacy options `health` and `reflection` in `CreateServerOptions` are preserved with `@deprecated` annotations. When used, Server.ts automatically loads built-in implementations from `./protocols/`. ## Alternatives Considered ### Alternative 1: Plugin system with auto-discovery Automatic loading of protocols from node\_modules by convention (e.g., `connectum-plugin-*`). **Rejected**: Excessive complexity, implicit behavior, violates Explicit > Implicit. ### Alternative 2: Middleware pattern (like Express) ```typescript server.use(healthcheck()); server.use(reflection()); ``` **Rejected**: ConnectRPC router registration must happen BEFORE server start and in one place. The middleware pattern is not suitable for gRPC service registration. ### Alternative 3: Keep as-is, only extract types Extract only TypeScript types, leaving implementations in core. **Rejected**: Does not solve the excessive dependencies and extensibility problems. ## Consequences ### Positive * **Modularity**: Core can be used without healthcheck/reflection * **Extensibility**: Custom protocols via a single interface * **Fewer dependencies**: Core doesn't pull `@lambdalisue/connectrpc-grpcreflect` for reflection * **Bugs fixed**: HealthcheckManager correctly updates all services * **Configurability**: watch interval, HTTP path, httpEnabled ### Negative * **Breaking change** for code using `server.health` * **Two packages instead of one** for the typical use case * **Temporary code duplication** (built-in protocols preserved for backward compat) ### Package Layer Changes ``` Before: Layer 0: proto, utilities, otel Layer 1: interceptors Layer 2: core (+ healthcheck + reflection) Layer 3: testing After: Layer 0: core Layer 1: interceptors, healthcheck, reflection Layer 2: otel, testing ``` ## Migration Guide ### From `server.health.update()` to `healthcheckManager.update()` ```typescript // Before const server = createServer({ services: [routes], health: { enabled: true }, }); server.on('ready', () => { server.health.update(ServingStatus.SERVING); }); // After const server = createServer({ services: [routes], protocols: [Healthcheck()], }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); ``` ### From `reflection: true` to `Reflection()` ```typescript // Before const server = createServer({ services: [routes], reflection: true, }); // After import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [routes], protocols: [Reflection()], }); ``` ## References * ADR-003: Package Decomposition Strategy * gRPC Health Checking Protocol: https://github.com/grpc/grpc/blob/master/doc/health-checking.md * gRPC Server Reflection: https://grpc.io/docs/guides/reflection/ --- --- url: /en/contributing/adr/023-uniform-registration-api.md --- # ADR-023: Uniform Registration API for Services, Interceptors, and Protocols ## Status **Accepted** - 2026-02-11 ## Context In `@connectum/core` (Layer 2), the `Server.ts` class had tight coupling with `@connectum/interceptors` (Layer 1) through direct import of factory functions and hardcoded interceptor chain assembly logic. ### Problems with the previous architecture 1. **SRP violation**: `Server.ts` was responsible for both server lifecycle AND interceptor chain configuration (selection, creation, ordering). These are two distinct responsibilities combined in a single module. 2. **Tight coupling**: `@connectum/core` directly imported factories from `@connectum/interceptors`: * `createErrorHandlerInterceptor` * `createValidationInterceptor` * `createSerializerInterceptor` * `createLoggerInterceptor` * `createTracingInterceptor` * `createRedactInterceptor` This violated the layered architecture principle (ADR-003): Layer 2 knew about concrete Layer 1 implementations. 3. **`builtinInterceptors` option**: `CreateServerOptions` contained a nested `builtinInterceptors` object with fields for each built-in interceptor (errorHandler, logger, tracing, serializer, redact, validation). This created tight coupling between the `@connectum/core` API and the internal structure of `@connectum/interceptors`. 4. **Asymmetric API**: Services already had `addService()` and a readonly `server.routes` getter, but interceptors and protocols had no analogous runtime registration API: ```typescript // Services: full API server.addService(myRoute); console.log(server.routes); // ReadonlyArray // Interceptors: only via options createServer({ builtinInterceptors: { logger: false } }); // No addInterceptor(), no server.interceptors // Protocols: only via options createServer({ protocols: [...] }); // No addProtocol(), no server.protocols ``` ### ADR-022 Context During the implementation of ADR-022 (Protocol Extraction), the `ProtocolRegistration` interface and `protocols` option were introduced. The logical next step was to bring interceptors to the same pattern and remove the hardcoded logic from core. ## Decision ### 1. Extract `createDefaultInterceptors()` to `@connectum/interceptors` All interceptor chain assembly logic has been moved to the `createDefaultInterceptors()` factory function in `@connectum/interceptors`: ```typescript // @connectum/interceptors/src/defaults.ts export interface DefaultInterceptorOptions { errorHandler?: boolean | ErrorHandlerOptions; validation?: boolean | ValidationOptions; serializer?: boolean | SerializerOptions; logger?: boolean | LoggerOptions; tracing?: boolean | TracingOptions; redact?: boolean | RedactOptions; } export function createDefaultInterceptors( options?: DefaultInterceptorOptions ): Interceptor[]; ``` Interceptor order is fixed and documented: 1. **errorHandler** -- first, catches all downstream errors 2. **validation** -- second, validates before processing 3. **serializer** -- JSON serialization 4. **logger** -- logging (development only by default) 5. **tracing** -- OpenTelemetry distributed tracing 6. **redact** -- sensitive data redaction (last) ### 2. Remove `builtinInterceptors` from `CreateServerOptions` The `builtinInterceptors` option has been removed from `CreateServerOptions` (breaking change, no deprecation phase). ### 3. Two-way semantics for the `interceptors` option ```typescript interface CreateServerOptions { /** * ConnectRPC interceptors. * When omitted or [], no interceptors are applied. * Use createDefaultInterceptors() from @connectum/interceptors * to get the production-ready chain. */ interceptors?: Interceptor[]; } ``` | `interceptors` value | Behavior | |----------------------|----------| | `undefined` (omitted) or `[]` | No interceptors | | `[a, b, c]` (explicit array) | Used as-is | Implementation in the `ServerImpl` constructor: ```typescript this._interceptors = [...(options.interceptors ?? [])]; ``` > **Note:** This is a further simplification from the original ADR design. The auto-defaults behavior was removed to achieve **zero internal dependencies** for `@connectum/core` (Layer 0). Users explicitly pass `createDefaultInterceptors()` from `@connectum/interceptors` when they want the default chain. ### 4. Uniform Registration API Added `addInterceptor()` and `addProtocol()` following the existing `addService()` pattern: ```typescript interface Server { // Services (existing) addService(service: ServiceRoute): void; readonly routes: ReadonlyArray; // Interceptors (added) addInterceptor(interceptor: Interceptor): void; readonly interceptors: ReadonlyArray; // Protocols (added) addProtocol(protocol: ProtocolRegistration): void; readonly protocols: ReadonlyArray; } ``` All three `addX()` methods work identically: * Can only be called in the `CREATED` state (before `start()`) * Throw `Error` if the server is already running * Append the element to the internal array All three readonly getters return `ReadonlyArray`. ### Final Architecture ```mermaid graph TB subgraph "Layer 0: @connectum/core" Server["Server.ts
(lifecycle only)"] Types["types.ts
(interfaces)"] end subgraph "Layer 1: @connectum/interceptors" Defaults["defaults.ts
createDefaultInterceptors()"] Factories["errorHandler, timeout,
bulkhead, circuitBreaker,
retry, validation, serializer"] end subgraph "Layer 1: @connectum/healthcheck" HC["Healthcheck()"] end subgraph "Layer 1: @connectum/reflection" Ref["Reflection()"] end Defaults --> Factories Server -->|"defines interface
ProtocolRegistration"| Types HC -->|"implements"| Types Ref -->|"implements"| Types style Server fill:#4a9eff,color:#fff style Defaults fill:#90ee90 style Factories fill:#90ee90 style HC fill:#90ee90 style Ref fill:#90ee90 style Types fill:#4a9eff,color:#fff ``` > **Note:** There is no arrow from `core` to `interceptors`. Core has zero internal dependencies. Users compose interceptors and protocols in their application code. ## Alternatives Considered ### Alternative 1: Keep `builtinInterceptors` with a deprecation phase **Approach**: Keep `builtinInterceptors` as deprecated, map it to `createDefaultInterceptors()` inside core. **Rating**: 4/10 **Pros:** Backward compatibility for 1-2 releases; smooth migration for users. **Cons:** Tight coupling persists (core must know about DefaultInterceptorOptions); type duplication between packages; code complexity: two configuration paths, edge cases when combining. **Why rejected**: Breaking changes are acceptable when a migration guide is provided. Dual configuration creates more confusion than a clean break. ### Alternative 2: Interceptors as ProtocolRegistration **Approach**: Unify interceptors and protocols under a single interface (e.g., `Plugin`). **Rating**: 3/10 **Pros:** Single extension mechanism; smaller API surface. **Cons:** Interceptors and protocols are fundamentally different concepts in ConnectRPC; interceptors are a middleware chain (order matters), protocols are service registration; loss of type safety: `Interceptor` type from `@connectrpc/connect` is not compatible with `ProtocolRegistration`; contradicts ConnectRPC ecosystem. **Why rejected**: Interceptors and protocols have different semantics. Artificial unification hides the difference and complicates the type system. ### Alternative 3: Builder pattern instead of options object **Approach**: ```typescript const server = createServer() .withService(routes) .withInterceptors(createDefaultInterceptors()) .withProtocol(Healthcheck()) .build(); ``` **Rating**: 5/10 **Pros:** Fluent API, reads well; explicit configuration sequence. **Cons:** Requires deep refactoring of the entire API; incompatible with the current EventEmitter-based approach; two APIs (builder + options) = confusion; no precedent in ConnectRPC ecosystem. **Why rejected**: Too extensive a refactoring for the problem being solved. Options object + runtime `addX()` is a standard pattern in the Node.js ecosystem. ## Consequences ### Positive * **SRP**: `Server.ts` is responsible only for lifecycle (start/stop/state/events). Knowledge of concrete interceptors is moved to `@connectum/interceptors`. * **Uniform API**: Services, interceptors, and protocols follow the same pattern: ``` options.X -> configuration at creation server.addX() -> runtime registration (before start) server.X -> readonly getter ``` * **Zero coupling**: `@connectum/core` has no dependency on `@connectum/interceptors`. Users explicitly import and pass interceptors. * **Explicit control**: Users fully control the interceptor chain via the three-way semantics (`undefined` / `[]` / `[...]`). * **Testability**: `createDefaultInterceptors()` can be tested in isolation from Server. ### Negative * **Breaking change**: All call sites using `builtinInterceptors` require migration. * **Runner legacy API**: `Runner()` (deprecated) must call `createDefaultInterceptors()` itself to map from `RunnerOptions.interceptors` (which contained `DefaultInterceptorOptions`). * **Additional import**: To customize the interceptor chain, users must import `createDefaultInterceptors` from `@connectum/interceptors` (an additional package beyond `@connectum/core`). * **No zero-config interceptors**: Users must explicitly import `createDefaultInterceptors` from `@connectum/interceptors`. This trades convenience for architectural purity (core = Layer 0). ### Neutral * Total package count unchanged. * `@connectum/core` size decreased (chain assembly code removed). * `@connectum/interceptors` grew by one module (`defaults.ts`). ## Migration Guide ### Removing `builtinInterceptors` | Before (v0.1.x) | After (v0.2.x) | |------------------|-----------------| | `builtinInterceptors: { all: false }` | `interceptors: []` | | `builtinInterceptors: { logger: false }` | `interceptors: createDefaultInterceptors({ logger: false })` | | `builtinInterceptors: { errorHandler: { logErrors: true }, logger: false }` | `interceptors: createDefaultInterceptors({ errorHandler: { logErrors: true }, logger: false })` | | `builtinInterceptors: { ... }` (all defaults) | Omit `interceptors` (auto-defaults) | | `builtinInterceptors: { ... }, interceptors: [custom]` | `interceptors: [...createDefaultInterceptors(), custom]` | ### New Capabilities ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [myRoutes], protocols: [Healthcheck({ httpEnabled: true }), Reflection()], // interceptors omitted -> auto-defaults }); // Runtime registration (before start) server.addInterceptor(myCustomInterceptor); server.addProtocol(myCustomProtocol); // Readonly getters console.log(server.interceptors); // ReadonlyArray console.log(server.protocols); // ReadonlyArray console.log(server.routes); // ReadonlyArray server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ## References * [ADR-003: Package Decomposition](./003-package-decomposition.md) -- layered architecture, Layer 2 -> Layer 1 principle * [ADR-022: Protocol Extraction](./022-protocol-extraction.md) -- introduction of ProtocolRegistration interface ## Changelog | Date | Author | Change | |------|--------|--------| | 2026-02-11 | Claude | Initial ADR -- Uniform Registration API | --- --- url: /en/contributing/adr/024-auth-authz-strategy.md --- # ADR-024: Auth/Authz Strategy ## Status Accepted -- 2026-02-15 Revised -- 2026-02-17 (v0.2.0: Gateway, Session interceptors, Security fixes) Revised -- 2026-02-20 (v0.3.0: Proto-based authorization, corrected dependencies, removed deleted trusted-headers, marked OTel as unimplemented) ## Context Connectum — a universal gRPC/ConnectRPC framework — has no built-in authentication or authorization. Each team writes custom auth interceptors, there is no standard for context propagation, and no best practices for JWT handling. The project board references Envoy ext\_authz, JWT authentication, JWT claim authorization, and credential injection as requirements. **Key requirements:** 1. Generic auth mechanism (not locked to JWT) 2. JWT convenience layer (covers 80% of use cases) 3. Declarative authorization (RBAC/claims) 4. Context propagation (in-process + cross-service) 5. Test utilities for auth scenarios **Constraints:** * Zero changes to `@connectum/core` (ADR-003 layer rules) * Standard ConnectRPC `Interceptor` type (composable with existing interceptors) * Optional dependency — users who don't need auth don't install it ## Decision Create a new `@connectum/auth` package (Layer 1) with interceptor factories, auth context propagation, and test utilities. ### 1. Package Architecture **Layer 1** package with zero internal dependencies: | Dependency | Type | Purpose | |---|---|---| | `jose` | dependency | JWT verification, JWKS, signing | | `@connectrpc/connect` | dependency | Interceptor type, ConnectError, Code | | `@connectum/core` | dependency | SanitizableError protocol | | `@bufbuild/protobuf` | dependency | Proto reflection for proto-based authz | ### 2. Interceptor Factories #### 2.1 `createAuthInterceptor()` — Generic Authentication Pluggable authentication for any credential type (API keys, mTLS, opaque tokens, custom schemes). ````typescript /** * Create a generic authentication interceptor. * * Extracts credentials from request headers, verifies them using * a user-provided callback, and stores the resulting AuthContext * in AsyncLocalStorage for downstream access. * * @param options - Authentication options * @returns ConnectRPC interceptor * * @example API key authentication * ```typescript * import { createAuthInterceptor } from '@connectum/auth'; * * const auth = createAuthInterceptor({ * extractCredentials: (req) => req.header.get('x-api-key'), * verifyCredentials: async (apiKey) => { * const user = await db.findByApiKey(apiKey); * if (!user) throw new Error('Invalid API key'); * return { * subject: user.id, * roles: user.roles, * scopes: [], * claims: {}, * type: 'api-key', * }; * }, * }); * ``` */ export function createAuthInterceptor(options: AuthInterceptorOptions): Interceptor; ```` #### 2.2 `createJwtAuthInterceptor()` — JWT Convenience Pre-built JWT verification with JWKS support, key rotation, and standard claim mapping. ````typescript /** * Create a JWT authentication interceptor. * * Convenience wrapper around createAuthInterceptor() that handles * JWT extraction from Authorization header, verification via jose, * and standard claim mapping to AuthContext. * * Supports: * - JWKS remote key sets (with automatic caching and rotation) * - HMAC symmetric secrets * - Asymmetric public keys (RSA, EC, Ed25519) * - Issuer and audience validation * - Custom claim-to-role/scope mapping * * @param options - JWT authentication options * @returns ConnectRPC interceptor * * @example JWKS-based JWT auth (Auth0, Keycloak, etc.) * ```typescript * import { createJwtAuthInterceptor } from '@connectum/auth'; * * const jwtAuth = createJwtAuthInterceptor({ * jwksUri: 'https://auth.example.com/.well-known/jwks.json', * issuer: 'https://auth.example.com/', * audience: 'my-api', * claimsMapping: { * roles: 'realm_access.roles', * scopes: 'scope', * }, * }); * ``` */ export function createJwtAuthInterceptor(options: JwtAuthInterceptorOptions): Interceptor; ```` #### 2.3 `createAuthzInterceptor()` — Declarative Authorization Rule-based authorization with RBAC support and programmatic callback escape hatch. ````typescript /** * Create an authorization interceptor. * * Evaluates declarative rules and/or a programmatic callback against * the AuthContext established by the authentication interceptor. * * IMPORTANT: This interceptor MUST run AFTER an authentication interceptor * (createAuthInterceptor or createJwtAuthInterceptor) in the chain. * * @param options - Authorization options * @returns ConnectRPC interceptor * * @example RBAC with declarative rules * ```typescript * import { createAuthzInterceptor } from '@connectum/auth'; * * const authz = createAuthzInterceptor({ * defaultPolicy: 'deny', * rules: [ * { * name: 'public-access', * methods: ['public.v1.PublicService/*'], * effect: 'allow', * }, * { * name: 'admin-only', * methods: ['admin.v1.AdminService/*'], * requires: { roles: ['admin'] }, * effect: 'allow', * }, * ], * }); * ``` * * @example Programmatic authorization callback * ```typescript * const authz = createAuthzInterceptor({ * authorize: async (ctx, req) => { * return await permissionService.check({ * subject: ctx.subject, * resource: req.service, * action: req.method, * }); * }, * }); * ``` */ export function createAuthzInterceptor(options: AuthzInterceptorOptions): Interceptor; ```` #### 2.4 `createGatewayAuthInterceptor()` — Gateway Pre-Auth (v0.2.0) For services behind an API gateway that has already performed authentication. Reads pre-authenticated identity from gateway-injected headers. > **Revision note (v0.2.0):** Replaces `createTrustedHeadersReader()` which relied on `peerAddress` — unavailable in ConnectRPC interceptors. Trust is now established via header verification. ```typescript export function createGatewayAuthInterceptor(options: GatewayAuthInterceptorOptions): Interceptor; ``` **Trust mechanism:** Verifies a designated header value (shared secret, trusted IP via `x-real-ip`) against a list of expected values. Supports exact match and CIDR ranges. #### 2.5 `createSessionAuthInterceptor()` — Session-Based Auth (v0.2.0) Convenience wrapper for session-based auth systems (better-auth, Lucia, etc.). ```typescript export function createSessionAuthInterceptor(options: SessionAuthInterceptorOptions): Interceptor; ``` **Key difference from `createAuthInterceptor()`:** Passes full `Headers` object to `verifySession()` callback, enabling cookie-based authentication. Includes built-in LRU cache support. #### 2.6 `createProtoAuthzInterceptor()` — Proto-Based Authorization (v0.3.0) Reads authorization configuration from protobuf custom options (`connectum.auth.v1`) and applies declarative rules defined in `.proto` files. Falls back to programmatic rules and callbacks. Available via `@connectum/auth/proto` subpath export. ```typescript export function createProtoAuthzInterceptor(options?: ProtoAuthzInterceptorOptions): Interceptor; ``` **9-step authorization decision flow:** ``` 1. resolveMethodAuth(req.method) -- read proto options (WeakMap-cached) 2. public = true --> skip (allow without authn) 3. Get auth context -- lazy: don't throw yet 4. requires defined, no context --> throw Unauthenticated 4b. requires defined, has context --> satisfiesRequirements? allow : deny 5. policy = "allow" --> allow 6. policy = "deny" --> deny 7. Evaluate programmatic rules -- unconditional rules work without context 8. Fallback: authorize callback --> requires auth context 9. Apply defaultPolicy --> deny without context = Unauthenticated ``` **Proto reader utilities** (also from `@connectum/auth/proto`): * `resolveMethodAuth(method: DescMethod): ResolvedMethodAuth` — resolve effective auth config by merging service-level defaults with method-level overrides. Results cached via `WeakMap`. * `getPublicMethods(services: DescService[]): string[]` — extract public method patterns from service descriptors. Returns patterns in `"ServiceTypeName/MethodName"` format for use with `skipMethods`. ```typescript import { createProtoAuthzInterceptor, getPublicMethods, resolveMethodAuth } from '@connectum/auth/proto'; // Proto options in .proto files control authorization: // option (connectum.auth.v1.method_auth) = { public: true }; // option (connectum.auth.v1.service_auth) = { default_requires: { roles: ["admin"] } }; const authz = createProtoAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'admin-fallback', methods: ['admin.v1.*/*'], requires: { roles: ['admin'] }, effect: 'allow' }, ], }); ``` ### 3. Auth Context Propagation Two complementary mechanisms: #### 3.1 AsyncLocalStorage (In-Process) Primary mechanism for in-process context access. Zero-overhead, type-safe. ```typescript export const authContextStorage: AsyncLocalStorage; export function getAuthContext(): AuthContext | undefined; export function requireAuthContext(): AuthContext; // throws ConnectError(Unauthenticated) ``` #### 3.2 Request Headers (Cross-Service) Secondary mechanism for service-to-service propagation, following the Envoy credential injection pattern. ```typescript export const AUTH_HEADERS = { SUBJECT: 'x-auth-subject', ROLES: 'x-auth-roles', SCOPES: 'x-auth-scopes', CLAIMS: 'x-auth-claims', NAME: 'x-auth-name', TYPE: 'x-auth-type', } as const; export function parseAuthHeaders(headers: Headers): AuthContext | undefined; ``` ### 4. Interceptor Chain Position Auth/authz interceptors are positioned **immediately after errorHandler** and **before all other interceptors**: ``` errorHandler → AUTH → AUTHZ → timeout → bulkhead → circuitBreaker → retry → fallback → validation → serializer ``` **Rationale:** 1. `errorHandler` first — catches all errors including auth errors 2. `AUTH` second — reject unauthenticated requests before consuming timeout/bulkhead resources 3. `AUTHZ` third — reject unauthorized requests before any processing ### 5. Trusted Headers Reader (Removed) **Removed** in v0.3.0 (deleted from codebase). The original `createTrustedHeadersReader()` relied on `peerAddress` which is unavailable in ConnectRPC interceptors. Use `createGatewayAuthInterceptor()` instead — it provides the same trusted-headers-reading functionality with header-based trust verification (shared secret or `x-real-ip` CIDR matching). ### 6. OpenTelemetry Integration **Not implemented** — the `otelEnrichment` option and `@connectum/otel` dependency are absent from the current implementation. Planned for future. The `getAuthContext()` API makes auth context available for custom OTel interceptors to enrich spans with `enduser.*` attributes if needed. ### 7. ext\_authz: NOT Included **Decision: Do NOT include Envoy ext\_authz implementation.** **Rationale:** 1. Infrastructure-level concern (Envoy-specific), not application framework 2. Most users will not need it — Envoy/Istio handle this at mesh level 3. Violates universal framework principle 4. Any Connectum service can implement ext\_authz as a regular gRPC service **Instead:** Document in examples/ and docs/ how to build ext\_authz with Connectum. ### 8. Test Utilities Sub-path export `@connectum/auth/testing`: ```typescript export function createMockAuthContext(overrides?: Partial): AuthContext; export function createTestJwt(payload: Record, options?: { expiresIn?: string }): Promise; export const TEST_JWT_SECRET: string; export function withAuthContext(context: AuthContext, fn: () => T | Promise): Promise; ``` *** ## Package Structure ``` packages/auth/ ├── src/ │ ├── index.ts │ ├── types.ts │ ├── context.ts │ ├── auth-interceptor.ts │ ├── jwt-auth-interceptor.ts │ ├── authz-interceptor.ts │ ├── headers.ts │ ├── errors.ts # AuthzDeniedError, AuthzDeniedDetails │ ├── method-match.ts # matchesMethodPattern() │ ├── authz-utils.ts # satisfiesRequirements() │ ├── gateway-auth-interceptor.ts │ ├── session-auth-interceptor.ts │ └── cache.ts ├── src/proto/ # @connectum/auth/proto subpath (v0.3.0) │ ├── index.ts │ ├── proto-authz-interceptor.ts # createProtoAuthzInterceptor() │ └── reader.ts # resolveMethodAuth(), getPublicMethods() ├── src/testing/ │ ├── index.ts │ ├── mock-context.ts │ ├── test-jwt.ts │ └── with-context.ts ├── tests/ │ ├── unit/ │ └── integration/ ├── package.json ├── tsconfig.json └── README.md ``` *** ## Architecture Diagram ```mermaid graph TB subgraph "Layer 0: @connectum/core" Server["Server.ts
(lifecycle only)"] Types["types.ts
(SanitizableError protocol)"] end subgraph "Layer 1: @connectum/auth" AuthInt["createAuthInterceptor()
Generic authentication"] JwtAuth["createJwtAuthInterceptor()
JWT + JWKS verification"] Authz["createAuthzInterceptor()
Declarative rules engine"] ProtoAuthz["createProtoAuthzInterceptor()
Proto-based authorization"] Context["getAuthContext()
AsyncLocalStorage + Headers"] TestUtils["testing/
createMockAuthContext, createTestJwt"] GatewayAuth["createGatewayAuthInterceptor()
Gateway pre-auth headers"] SessionAuth["createSessionAuthInterceptor()
Session-based auth"] Cache["LruCache
In-memory TTL cache"] end subgraph "Layer 1: @connectum/interceptors" Defaults["createDefaultInterceptors()"] MethodFilter["createMethodFilterInterceptor()"] end subgraph "External" Jose["jose
(JWT library)"] ConnectRPC["@connectrpc/connect
(Interceptor type)"] BufProtobuf["@bufbuild/protobuf
(Proto reflection)"] end JwtAuth --> AuthInt JwtAuth --> Jose AuthInt --> Context AuthInt --> ConnectRPC Authz --> Context Authz --> ConnectRPC ProtoAuthz --> Context ProtoAuthz --> BufProtobuf ProtoAuthz --> ConnectRPC GatewayAuth --> Context GatewayAuth --> ConnectRPC SessionAuth --> Context SessionAuth --> Cache SessionAuth --> ConnectRPC Defaults --> ConnectRPC MethodFilter --> ConnectRPC AuthInt -.-> Types ``` ### Interceptor Chain Flow ```mermaid sequenceDiagram participant Client participant ErrorHandler participant Auth as Auth Interceptor participant Authz as Authz Interceptor participant Timeout participant Handler as Service Handler Client->>ErrorHandler: Request + Bearer token ErrorHandler->>Auth: Forward request alt Token missing or invalid Auth-->>ErrorHandler: throw ConnectError(Unauthenticated) ErrorHandler-->>Client: 16 UNAUTHENTICATED else Token valid Auth->>Auth: Set AuthContext (AsyncLocalStorage + headers) Auth->>Authz: Forward request + AuthContext alt Not authorized Authz-->>ErrorHandler: throw ConnectError(PermissionDenied) ErrorHandler-->>Client: 7 PERMISSION_DENIED else Authorized Authz->>Timeout: Forward request Timeout->>Handler: Forward request Handler->>Handler: getAuthContext() => AuthContext Handler-->>Client: Response end end ``` *** ## Consequences ### Positive 1. **Universal auth primitives** — generic `createAuthInterceptor()` works with any credential type 2. **JWT best practices out-of-the-box** — JWKS caching, key rotation, standard claim validation via `jose` 3. **Declarative authorization** — rule-based RBAC eliminates boilerplate; rules are auditable 4. **Standard context propagation** — dual mechanism covers in-process and cross-service 5. **Zero coupling with core** — only depends on `@connectrpc/connect` and `jose` 6. **Composable** — standard ConnectRPC `Interceptor`, works with `createMethodFilterInterceptor()` 7. **Testable** — built-in test utilities eliminate test boilerplate 8. **OTel-composable** — `getAuthContext()` makes auth data available for custom OTel interceptors to enrich spans ### Negative 1. **Additional package** — 7th package in monorepo. Mitigation: modular pattern, install only what's needed 2. **jose dependency** — ~50KB for JWT. Mitigation: tree-shakeable if only using generic auth 3. **Chain order is user's responsibility**. Mitigation: clear documentation and examples 4. **AsyncLocalStorage overhead** — <1us per context switch. Mitigation: Node.js ALS is mature 5. **No built-in token refresh** — client-side concern, out of scope ### Risks 1. **jose breaking changes** — Mitigation: pin `jose@^6`, wrap API internally 2. **Security vulnerabilities** — Mitigation: rely on `jose` for crypto, security review, comprehensive tests 3. **Overlap with infrastructure auth** — Mitigation: document when to use app-level vs infra-level auth 4. **Header spoofing** — Mitigation: `createGatewayAuthInterceptor()` with `trustSource` verification (shared secret or CIDR), fail-closed 5. **ALS fragility in streams** — Mitigation: context set at stream creation, documented *** ## Alternatives Considered ### Alternative 1: Extend `@connectum/interceptors` **Rating:** 4/10 Forces `jose` dependency on all interceptor users; violates SRP. Auth has different dependencies and lifecycle than resilience patterns. ### Alternative 2: JWT-only interceptor **Rating:** 5/10 Cannot support API keys, mTLS, opaque tokens. Violates universal framework principle. ### Alternative 3: Include Envoy ext\_authz **Rating:** 3/10 Infrastructure-level concern, Envoy-specific. Document as example instead. ### Alternative 4: ConnectRPC contextValues **Rating:** 6/10 contextValues available in handlers but NOT in interceptors. AsyncLocalStorage works everywhere in async call stack. ### Alternative 5: Policy-as-code (OPA/Rego) **Rating:** 5/10 Too heavy for embedded devices. Declarative rules + callback cover same use cases lighter. Users needing OPA can implement as `authorize` callback. *** ## Implementation Plan ### Phase 1: Core Auth 1. Create `packages/auth/` package structure 2. Implement `createAuthInterceptor()` with AsyncLocalStorage context 3. Implement `getAuthContext()`, `requireAuthContext()`, `parseAuthHeaders()` 4. Unit tests (>90% coverage) ### Phase 2: JWT + Authorization 5. Implement `createJwtAuthInterceptor()` with jose integration 6. Implement `createAuthzInterceptor()` with rule engine 7. Implement `createTrustedHeadersReader()` with fail-closed 8. Unit + integration tests ### Phase 3: Test Utilities 9. Implement `@connectum/auth/testing` sub-export 10. Integration tests with full auth chain ### Phase 4: Documentation & Examples 11. README.md, authentication guide, authorization guide 12. Example: `with-jwt-auth/` *** ## References 1. [ADR-003: Package Decomposition](./003-package-decomposition.md) 2. [ADR-006: Resilience Patterns](./006-resilience-pattern-implementation.md) 3. [ADR-014: Method Filter Interceptor](./014-method-filter-interceptor.md) 4. [ADR-023: Uniform Registration API](./023-uniform-registration-api.md) 5. [jose library](https://github.com/panva/jose) 6. [ConnectRPC Interceptors](https://connectrpc.com/docs/node/interceptors/) 7. [Envoy ext\_authz](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter) 8. [OpenTelemetry Semantic Conventions: End User](https://opentelemetry.io/docs/specs/semconv/attributes-registry/enduser/) *** ## Changelog | Date | Author | Change | |------|--------|--------| | 2026-02-15 | Software Architect | Initial ADR: Auth/Authz Strategy | | 2026-02-17 | Software Architect | v0.2.0 Revision: Gateway/Session interceptors, LRU cache, Security fixes (SEC-001, SEC-002, SEC-005) | | 2026-02-20 | Software Architect | v0.3.0 Revision: Proto-based authorization (`createProtoAuthzInterceptor`, `@connectum/auth/proto`), corrected dependencies (`@connectum/core`, `@bufbuild/protobuf`), removed deleted `trusted-headers.ts`, marked OTel as unimplemented | --- --- url: /en/contributing/adr/025-package-versioning-strategy.md --- # ADR-025: Package Versioning Strategy ## Status Accepted -- 2026-02-20 ## Context Connectum is a universal gRPC/ConnectRPC framework composed of 8 `@connectum/*` packages in a pnpm monorepo, managed by [changesets](https://github.com/changesets/changesets) for versioning and publishing. Packages are compiled via tsup before publication (compile-before-publish, see [ADR-001](./001-native-typescript-migration.md)). ### Current State All 8 packages use a **fixed versioning** strategy (`"fixed": [["@connectum/*"]]`), meaning every package always shares the same version number. The project is currently in a **pre-release (rc) phase** with all packages at `1.0.0-rc.4`. Current `.changeset/config.json`: ```json { "fixed": [["@connectum/*"]], "linked": [], "updateInternalDependencies": "patch" } ``` ### Package Dependency Graph The 8 packages are organized in 3 layers with varying degrees of coupling: ```mermaid graph TB subgraph "Layer 0: Foundation" Core["@connectum/core"] end subgraph "Layer 1: Extensions" Interceptors["@connectum/interceptors"] Healthcheck["@connectum/healthcheck"] Reflection["@connectum/reflection"] Auth["@connectum/auth"] Testing["@connectum/testing
(private)"] end subgraph "Layer 2: Tools" Otel["@connectum/otel"] CLI["@connectum/cli"] end Interceptors -- "dependency" --> Core Healthcheck -. "peerDependency" .-> Core Reflection -. "peerDependency" .-> Core Auth -- "dependency" --> Core Testing -- "dependency" --> Core Otel -.- |"zero @connectum deps"| Otel CLI -.- |"devDeps only"| CLI style Core fill:#4a90d9,color:#fff style Interceptors fill:#4a90d9,color:#fff style Healthcheck fill:#4a90d9,color:#fff style Reflection fill:#4a90d9,color:#fff style Auth fill:#7fb069,color:#fff style Otel fill:#7fb069,color:#fff style CLI fill:#7fb069,color:#fff style Testing fill:#999,color:#fff ``` ### The Problem With the current fixed strategy, **all** 8 packages are bumped together. This means: 1. A patch fix in `@connectum/auth` bumps `@connectum/otel` even though otel has zero `@connectum` dependencies 2. A new feature in `@connectum/cli` bumps `@connectum/core` even though core is unchanged 3. CHANGELOGs contain "bumped for consistency" entries with no actual changes 4. Semver loses its semantic meaning for independent packages While this is acceptable during the pre-release phase (simplicity > precision), it becomes semantically misleading after `1.0.0` stable when consumers rely on version numbers to understand what changed. ### Industry Precedents | Framework | Strategy | Rationale | |-----------|----------|-----------| | **Angular** | Fixed | Tight integration across all packages | | **NestJS** | Fixed (core) + Independent (modules) | Core packages move together, optional modules versioned independently | | **tRPC** | Fixed | Server/client API contract must match | | **Effect** | Independent | Pluggable extensions with peer dependencies | | **Connectum** | **Hybrid** (this ADR) | Core group tightly coupled, auth/otel/cli are optional extensions | Connectum is closest to the **NestJS model**: a tightly coupled core group with independently versioned optional extensions. ## Decision **Adopt a two-phase versioning strategy: Fixed during pre-release, Hybrid after 1.0.0 stable.** ### Phase 1: Pre-release (rc) through 1.0.0 -- Stay Fixed During the pre-release phase, **keep the current fixed strategy unchanged**. **Rationale:** * Changing versioning strategy during rc is unnecessary risk * "Connectum v1.0.0" as a single unified version is a clearer launch message * CI/CD (`release.yml`) works without modifications * Risk = 0, focus remains on stability and API surface finalization **Configuration (no change):** ```json { "fixed": [["@connectum/*"]], "linked": [], "updateInternalDependencies": "patch" } ``` ### Phase 2: After 1.0.0 stable -- Transition to Hybrid After the `1.0.0` stable release, split packages into two groups: #### Core Group (Fixed) Packages that are tightly coupled through `createServer()` and are almost always used together by consumers: | Package | Reason for inclusion | |---------|---------------------| | `@connectum/core` | Server factory, lifecycle, plugin system | | `@connectum/interceptors` | `createDefaultInterceptors()`, direct dependency on core | | `@connectum/healthcheck` | Protocol plugin for `createServer({ protocols: [...] })` | | `@connectum/reflection` | Protocol plugin for `createServer({ protocols: [...] })` | These 4 packages form the **standard Connectum setup**. A typical service uses all four: ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { Healthcheck } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [routes], interceptors: createDefaultInterceptors(), protocols: [Healthcheck({ httpEnabled: true }), Reflection()], }); ``` #### Independent Group Packages that are **optional extensions** with distinct lifecycles: | Package | Reason for independence | |---------|----------------------| | `@connectum/auth` | Optional authentication/authorization; consumers may handle auth at the gateway level | | `@connectum/otel` | Zero `@connectum` dependencies; pure OpenTelemetry instrumentation | | `@connectum/cli` | Developer tool; only `devDependencies`; not a runtime concern | | `@connectum/testing` | Private package; `devDependency` only; never published | #### Hybrid Configuration ```json { "fixed": [ [ "@connectum/core", "@connectum/interceptors", "@connectum/healthcheck", "@connectum/reflection" ] ], "linked": [], "updateInternalDependencies": "minor", "access": "public", "baseBranch": "main" } ``` Key changes from Phase 1: * `fixed` narrows from `["@connectum/*"]` to the 4 core packages * `updateInternalDependencies` changes from `"patch"` to `"minor"` -- when a core group package bumps minor, internal dependents (like `auth` depending on `core`) get their dependency range updated to `"minor"` rather than `"patch"`, providing wider compatibility #### Version Grouping Diagram ```mermaid graph LR subgraph "Core Group (Fixed Versioning)" direction TB C["@connectum/core
v1.2.0"] I["@connectum/interceptors
v1.2.0"] H["@connectum/healthcheck
v1.2.0"] R["@connectum/reflection
v1.2.0"] end subgraph "Independent Versioning" direction TB A["@connectum/auth
v1.1.0"] O["@connectum/otel
v1.0.3"] CLI["@connectum/cli
v1.3.0"] end C -.->|"same version
always"| I C -.->|"same version
always"| H C -.->|"same version
always"| R A -->|"dep: core ^1.0.0"| C O -.-|"no @connectum deps"| O CLI -.-|"devDeps only"| CLI style C fill:#4a90d9,color:#fff style I fill:#4a90d9,color:#fff style H fill:#4a90d9,color:#fff style R fill:#4a90d9,color:#fff style A fill:#7fb069,color:#fff style O fill:#7fb069,color:#fff style CLI fill:#7fb069,color:#fff ``` #### CI/CD Changes (`release.yml`) The current `release.yml` extracts the version for GitHub Release tagging using `.[0].version` (first published package). After the transition, it must target the core group explicitly: **Before (Phase 1):** ```bash # Any package works since all versions are identical VERSION=$(jq -r '.[0].version' < published.json) ``` **After (Phase 2):** ```bash # Target core specifically for tag and GitHub Release VERSION=$(jq -r '.[] | select(.name == "@connectum/core") | .version' < published.json) ``` The GitHub Release and git tag (`v1.2.0`) are tied to the **core group version**, since the core group represents the framework version. Independent packages (auth, otel, cli) do not create separate GitHub Releases -- their changes are tracked in per-package CHANGELOGs. ### Migration Checklist When transitioning from Phase 1 to Phase 2 (after `1.0.0` stable release): * \[ ] Update `.changeset/config.json` with the hybrid configuration above * \[ ] Update `release.yml` to use `select(.name == "@connectum/core")` for version extraction * \[ ] Verify all 8 packages are at `1.0.0` before the switch * \[ ] Update `docs/en/contributing/development-setup.md` with versioning guidelines * \[ ] Add a note to each independent package README explaining its versioning policy * \[ ] Create a compatibility matrix in documentation (core group version vs auth/otel/cli versions) * \[ ] Run a dry-run changeset version to confirm the hybrid config works: `pnpm changeset version --snapshot test` * \[ ] Update this ADR status to reflect Phase 2 activation ## Consequences ### Positive 1. **Phase 1: Zero risk** -- proven strategy for the rc phase, no changes needed 2. **Phase 2: Semantic accuracy** -- core group stays synchronized while auth/otel/cli bump only on real changes 3. **Cleaner CHANGELOGs** -- no more "bumped for consistency" entries in independent packages 4. **Smaller consumer updates** -- consumers of `@connectum/otel` don't need to update when only `@connectum/auth` changed 5. **Industry-aligned** -- follows the NestJS model, familiar to Node.js ecosystem developers 6. **Flexible evolution** -- independent packages can iterate faster without blocking or being blocked by core releases ### Negative 1. **CI/CD update required** -- `release.yml` needs modification during Phase 2 transition 2. **Compatibility awareness** -- consumers must understand that core group = one version, while auth/otel/cli are independent; needs clear documentation 3. **Version matrix complexity** -- documentation must maintain a compatibility matrix (e.g., "auth@1.1.0 requires core@^1.0.0") 4. **Changeset discipline** -- contributors must understand which packages belong to the fixed group and which are independent 5. **Potential confusion** -- `@connectum/auth@1.1.0` alongside `@connectum/core@1.2.0` may look inconsistent to users unfamiliar with the strategy ## Alternatives Considered ### Alternative 1: Stay Fixed Forever **Rating:** 6/10 Keep `"fixed": [["@connectum/*"]]` permanently. **Pros:** * Simplest mental model: "Connectum is version X" * Zero compatibility questions -- all packages always match * Minimal CI/CD complexity **Cons:** * Semantically inaccurate: `@connectum/otel` gets bumped when `@connectum/auth` changes, despite zero coupling * Noisy CHANGELOGs with empty "bumped for consistency" entries * Consumers update packages unnecessarily ### Alternative 2: Full Independent **Rating:** 4/10 Every package versioned independently with no fixed groups. **Pros:** * Maximum semantic precision * Each package bumps only on its own changes * True semver for every package **Cons:** * "Is `core@1.0.5` + `interceptors@1.2.0` + `healthcheck@1.1.3` compatible?" -- consumers cannot easily answer this * Core group packages are tightly coupled; independent versioning creates a false impression of independence * Significantly more complex release process and documentation ### Alternative 3: Linked (instead of Fixed for core group) **Rating:** 5/10 Use `"linked"` instead of `"fixed"` for the core group. With `linked`, packages share the same bump type but only packages with actual changesets are bumped. **Pros:** * More precise than fixed -- unchanged packages don't bump * Same bump type ensures version alignment intent **Cons:** * In practice, for 4 tightly coupled packages, nearly every change touches multiple packages * Creates version drift within the core group (e.g., `core@1.2.0` + `interceptors@1.1.0`), confusing consumers * For Connectum's core group, the result is approximately the same as fixed but with added complexity ## References 1. [ADR-001: Native TypeScript Migration](./001-native-typescript-migration.md) -- compile-before-publish strategy 2. [ADR-003: Package Decomposition](./003-package-decomposition.md) -- package structure and layer rules 3. [Changesets documentation: Fixed packages](https://github.com/changesets/changesets/blob/main/docs/fixed-packages.md) 4. [Changesets documentation: Linked packages](https://github.com/changesets/changesets/blob/main/docs/linked-packages.md) 5. [NestJS versioning strategy](https://github.com/nestjs/nest) -- similar hybrid approach *** ## Changelog | Date | Author | Change | |------|--------|--------| | 2026-02-20 | Software Architect | Initial ADR: two-phase versioning strategy (Fixed for rc, Hybrid after 1.0.0) | --- --- url: /en/contributing/adr.md description: >- Index of all accepted Architecture Decision Records (ADRs) for the Connectum framework. --- # Architecture Decision Records Architecture Decision Records (ADRs) capture important design decisions with their context, rationale, and consequences. Each ADR follows a standard format: Status, Context, Decision, Consequences, Alternatives. ## Accepted ADRs | # | Title | Date | Summary | |---|-------|------|---------| | 001 | [Native TypeScript](/en/contributing/adr/001-native-typescript-migration) | 2026-02-16 | Native TypeScript development + compile-before-publish with tsup | | 003 | [Package Decomposition](/en/contributing/adr/003-package-decomposition) | 2025-12-22 | Modular packages in dependency layers | | 005 | [Input Validation](/en/contributing/adr/005-input-validation-strategy) | 2025-12-24 | Protovalidate as primary validation mechanism | | 006 | [Resilience Patterns](/en/contributing/adr/006-resilience-pattern-implementation) | 2025-12-24 | Resilience interceptors with cockatiel library | | 007 | [Testing Strategy](/en/contributing/adr/007-testing-strategy) | 2025-12-24 | node:test runner, 90%+ coverage target | | 008 | [Performance Benchmarking](/en/contributing/adr/008-performance-benchmarking) | 2025-12-24 | k6 load testing, p95 < 100ms SLA | | 009 | [Buf CLI Migration](/en/contributing/adr/009-buf-cli-migration) | 2026-02-06 | Buf CLI v2 for proto generation + lint | | 014 | [Method Filter Interceptor](/en/contributing/adr/014-method-filter-interceptor) | 2026-02-07 | Per-method interceptor routing with wildcards | | 020 | [Reflection Proto Sync](/en/contributing/adr/020-reflection-proto-sync) | 2026-02-07 | 4-phase reflection-based proto synchronization | | 022 | [Protocol Extraction](/en/contributing/adr/022-protocol-extraction) | 2026-02-11 | Healthcheck/Reflection as separate packages | | 023 | [Uniform Registration API](/en/contributing/adr/023-uniform-registration-api) | 2026-02-11 | createDefaultInterceptors(), explicit interceptor control | | 024 | [Auth/Authz Strategy](/en/contributing/adr/024-auth-authz-strategy) | 2026-02-15 | @connectum/auth package with JWT, RBAC, context propagation | | 025 | [Package Versioning Strategy](/en/contributing/adr/025-package-versioning-strategy) | 2026-02-20 | Two-phase versioning: Fixed for rc, Hybrid after 1.0.0 stable | ## Creating a New ADR 1. Create `XXX-title.md` using the template below 2. Status starts as **Proposed** 3. After review and approval, change to **Accepted** 4. Update this index ```markdown # ADR-XXX: Title ## Status Proposed -- YYYY-MM-DD ## Context Why is this decision needed? ## Decision What did we decide? ## Consequences ### Positive ### Negative ## Alternatives Considered ``` --- --- url: /en/api/@connectum/otel/attributes.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / attributes # attributes RPC Semantic Convention attributes for OpenTelemetry Based on: * https://opentelemetry.io/docs/specs/semconv/rpc/connect-rpc/ * https://opentelemetry.io/docs/specs/semconv/rpc/rpc-metrics/ * https://opentelemetry.io/docs/specs/semconv/attributes-registry/rpc/ ## Type Aliases * [ConnectErrorCode](type-aliases/ConnectErrorCode.md) ## Variables * [ATTR\_ERROR\_TYPE](variables/ATTR_ERROR_TYPE.md) * [ATTR\_NETWORK\_PEER\_ADDRESS](variables/ATTR_NETWORK_PEER_ADDRESS.md) * [ATTR\_NETWORK\_PEER\_PORT](variables/ATTR_NETWORK_PEER_PORT.md) * [ATTR\_NETWORK\_PROTOCOL\_NAME](variables/ATTR_NETWORK_PROTOCOL_NAME.md) * [ATTR\_NETWORK\_TRANSPORT](variables/ATTR_NETWORK_TRANSPORT.md) * [ATTR\_RPC\_CONNECT\_RPC\_STATUS\_CODE](variables/ATTR_RPC_CONNECT_RPC_STATUS_CODE.md) * [ATTR\_RPC\_MESSAGE\_ID](variables/ATTR_RPC_MESSAGE_ID.md) * [ATTR\_RPC\_MESSAGE\_TYPE](variables/ATTR_RPC_MESSAGE_TYPE.md) * [ATTR\_RPC\_MESSAGE\_UNCOMPRESSED\_SIZE](variables/ATTR_RPC_MESSAGE_UNCOMPRESSED_SIZE.md) * [ATTR\_RPC\_METHOD](variables/ATTR_RPC_METHOD.md) * [ATTR\_RPC\_SERVICE](variables/ATTR_RPC_SERVICE.md) * [ATTR\_RPC\_SYSTEM](variables/ATTR_RPC_SYSTEM.md) * [ATTR\_SERVER\_ADDRESS](variables/ATTR_SERVER_ADDRESS.md) * [ATTR\_SERVER\_PORT](variables/ATTR_SERVER_PORT.md) * [ConnectErrorCode](variables/ConnectErrorCode.md) * [ConnectErrorCodeName](variables/ConnectErrorCodeName.md) * [RPC\_MESSAGE\_EVENT](variables/RPC_MESSAGE_EVENT.md) * [RPC\_SYSTEM\_CONNECT\_RPC](variables/RPC_SYSTEM_CONNECT_RPC.md) --- --- url: /en/guide/auth.md --- # Auth & Authz The `@connectum/auth` package provides authentication interceptors and two authorization models: **proto-based** (rules in `.proto` files) and **code-based** (programmatic rules in TypeScript). Proto-based is the recommended approach -- it keeps access control alongside your API contract and requires zero application code changes when rules evolve. ## Quick Start ### Proto-Based Authorization (Recommended) Define access rules directly in `.proto` files -- the interceptor reads them at runtime: ```protobuf import "connectum/auth/v1/options.proto"; service UserService { option (connectum.auth.v1.service_auth) = { default_policy: "deny" }; rpc GetProfile(GetProfileRequest) returns (GetProfileResponse) { option (connectum.auth.v1.method_auth) = { public: true }; } rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse) { option (connectum.auth.v1.method_auth) = { requires: { roles: ["admin"] } }; } } ``` ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { createJwtAuthInterceptor, createProtoAuthzInterceptor, getPublicMethods, } from '@connectum/auth'; import { UserService } from '#gen/user_pb.js'; const publicMethods = getPublicMethods([UserService]); const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', skipMethods: publicMethods, // synced from proto `public: true` }); const authz = createProtoAuthzInterceptor({ defaultPolicy: 'deny' }); const server = createServer({ services: [routes], interceptors: [...createDefaultInterceptors(), jwtAuth, authz], }); await server.start(); ``` ### Code-Based Authorization For services without proto annotations, use programmatic rules: ```typescript import { createAuthzInterceptor } from '@connectum/auth'; const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'public', methods: ['public.v1.PublicService/*'], effect: 'allow' }, { name: 'admin', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, ], }); ``` Both approaches can be combined -- proto options take priority, programmatic rules act as fallback. ## Key Concepts ### Authorization: Proto vs Code | | Proto-Based | Code-Based | |-|-------------|------------| | **Where rules live** | `.proto` files | TypeScript code | | **Interceptor** | `createProtoAuthzInterceptor()` | `createAuthzInterceptor()` | | **Change access rules** | Edit `.proto`, regenerate | Edit code, redeploy | | **Single source of truth** | Proto contract = access policy | Separate from API contract | | **Best for** | Most services (recommended) | Dynamic rules, legacy services | ### Authentication Strategies | Factory | Credential source | Use case | |---------|-------------------|----------| | `createAuthInterceptor` | Any (pluggable callback) | API keys, mTLS, opaque tokens | | `createJwtAuthInterceptor` | `Authorization: Bearer ` | Auth0, Keycloak, custom issuers | | `createGatewayAuthInterceptor` | Gateway-injected headers | Kong, Envoy, Traefik pre-auth | | `createSessionAuthInterceptor` | Session token (cookie or header) | better-auth, lucia, custom sessions | All factories produce a standard ConnectRPC `Interceptor` and store the authenticated identity in `AuthContext` via `AsyncLocalStorage`. ### When Do You Need App-Level Auth? | Scenario | Auth approach | |----------|--------------| | Services behind an API gateway that verifies tokens | Gateway auth (`createGatewayAuthInterceptor`) | | Services exposed directly to clients | JWT auth (`createJwtAuthInterceptor`) | | Services with session-based web clients | Session auth (`createSessionAuthInterceptor`) | | Custom or exotic credential schemes | Generic auth (`createAuthInterceptor`) | ### Interceptor Chain Position Auth interceptors must be placed **after** `errorHandler` and **before** resilience interceptors: ``` errorHandler -> AUTH -> AUTHZ -> timeout -> bulkhead -> circuitBreaker -> ... ``` ## Learn More * [JWT Authentication](/en/guide/auth/jwt) -- JWKS, HMAC, public key verification * [Gateway Authentication](/en/guide/auth/gateway) -- pre-authenticated headers from API gateways * [Session Authentication](/en/guide/auth/session) -- cookie-based and session-framework auth * [Authorization (RBAC)](/en/guide/auth/authorization) -- declarative rules, programmatic callbacks * [Proto-Based Authorization](/en/guide/auth/proto-authz) -- authz rules defined in `.proto` files * [Auth Context](/en/guide/auth/context) -- accessing identity in handlers, cross-service propagation, testing * [@connectum/auth](/en/packages/auth) -- Package Guide * [@connectum/auth API](/en/api/@connectum/auth/) -- Full API Reference * [ADR-024: Auth/Authz Strategy](/en/contributing/adr/024-auth-authz-strategy) -- design rationale --- --- url: /en/guide/auth/context.md --- # Auth Context All authentication interceptors in `@connectum/auth` store the verified identity in `AuthContext` via `AsyncLocalStorage`. This makes the identity available to any code running within the request scope -- service handlers, other interceptors, and utility functions. ## Accessing Auth Context in Handlers ### Optional Access Use `getAuthContext()` when authentication is optional (e.g. public endpoints that show extra data for logged-in users): ```typescript import { getAuthContext } from '@connectum/auth'; function getProduct(req: GetProductRequest) { const auth = getAuthContext(); // undefined if not authenticated if (auth) { // Show personalized pricing for logged-in users return getProductWithPricing(req, auth.subject); } return getPublicProduct(req); } ``` ### Required Access Use `requireAuthContext()` when the handler requires authentication. It throws `Unauthenticated` if no auth context exists: ```typescript import { requireAuthContext } from '@connectum/auth'; function updateProfile(req: UpdateProfileRequest) { const auth = requireAuthContext(); // throws if not authenticated console.log(`User: ${auth.subject}, roles: ${auth.roles}`); // ... } ``` ### AuthContext Shape The `AuthContext` object contains the following fields: | Field | Type | Description | |-------|------|-------------| | `subject` | `string` | User identifier (from JWT `sub`, gateway header, or session) | | `name` | `string \| undefined` | Display name | | `roles` | `string[]` | User roles | | `scopes` | `string[]` | OAuth scopes or permissions | | `claims` | `Record` | Raw claims from the token or session | | `type` | `string` | Auth type (`'jwt'`, `'gateway'`, `'session'`, `'custom'`) | ## Cross-Service Propagation Enable `propagateHeaders` to forward auth context to downstream services via HTTP headers. This is useful in microservice architectures where the downstream service trusts the upstream caller: ```typescript const jwtAuth = createJwtAuthInterceptor({ jwksUri: '...', propagateHeaders: true, propagatedClaims: ['email', 'org_id'], // optional: filter sensitive claims }); ``` ### Propagated Headers | Header | Content | |--------|---------| | `x-auth-subject` | User ID | | `x-auth-type` | Auth type | | `x-auth-name` | Display name | | `x-auth-roles` | Comma-separated roles | | `x-auth-scopes` | Comma-separated scopes | | `x-auth-claims` | JSON-encoded filtered claims | The downstream service can read these headers with `createGatewayAuthInterceptor`, completing the trust chain. ## Testing The `@connectum/auth/testing` subpath export provides helpers for unit and integration tests. ### Mock Auth Context Run a handler with a mock auth context: ```typescript import { createMockAuthContext, withAuthContext } from '@connectum/auth/testing'; const result = await withAuthContext( createMockAuthContext({ subject: 'user-1', roles: ['admin'] }), () => myHandler(request), ); ``` ### Test JWT Generate a signed JWT for integration tests: ```typescript import { createTestJwt, TEST_JWT_SECRET } from '@connectum/auth/testing'; const token = await createTestJwt({ sub: 'user-1', roles: ['admin'] }); // Use with createJwtAuthInterceptor({ secret: TEST_JWT_SECRET }) ``` ### Full Test Example ```typescript import { describe, it } from 'node:test'; import assert from 'node:assert'; import { createMockAuthContext, withAuthContext } from '@connectum/auth/testing'; describe('updateProfile', () => { it('should update the profile for an authenticated user', async () => { const auth = createMockAuthContext({ subject: 'user-42', name: 'Alice', roles: ['user'], }); const result = await withAuthContext(auth, () => updateProfile({ name: 'Alice Updated' }), ); assert.strictEqual(result.name, 'Alice Updated'); }); it('should reject unauthenticated requests', async () => { await assert.rejects( () => updateProfile({ name: 'Nope' }), (err) => err.code === 'UNAUTHENTICATED', ); }); }); ``` ## Related * [Auth Overview](/en/guide/auth) -- all authentication strategies * [JWT Authentication](/en/guide/auth/jwt) -- token verification * [Gateway Authentication](/en/guide/auth/gateway) -- header-based auth * [Authorization](/en/guide/auth/authorization) -- RBAC and access control * [@connectum/auth](/en/packages/auth) -- Package Guide * [@connectum/auth API](/en/api/@connectum/auth/) -- Full API Reference --- --- url: /en/guide/auth/authorization.md --- # Authorization The `createAuthzInterceptor` enforces access control after authentication. It supports declarative rules, proto-based options, and a programmatic fallback callback. ## Declarative Rules Define rules as an ordered list. The first matching rule wins: ```typescript import { createAuthzInterceptor } from '@connectum/auth'; const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'public', methods: ['public.v1.PublicService/*'], effect: 'allow' }, { name: 'admin-only', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, { name: 'write-scope', methods: ['data.v1.DataService/Write*'], requires: { scopes: ['write'] }, effect: 'allow' }, ], }); ``` ### Rule Fields | Field | Type | Description | |-------|------|-------------| | `name` | `string` | Rule name for logging and debugging | | `methods` | `string[]` | Method patterns (same syntax as `createMethodFilterInterceptor`) | | `requires` | `{ roles?, scopes? }` | Required roles and/or scopes | | `effect` | `'allow' \| 'deny'` | What to do when the rule matches | ### Matching Semantics * **Roles** use **any-of** semantics -- the user needs at least one of the listed roles. * **Scopes** use **all-of** semantics -- the user needs all listed scopes. * Rules without `requires` match all authenticated users (or all requests, if the method is public). ### Method Patterns | Pattern | Description | |---------|-------------| | `'public.v1.PublicService/*'` | All methods of the service | | `'data.v1.DataService/Write*'` | Methods starting with `Write` | | `'admin.v1.AdminService/DeleteUser'` | Exact method match | ## Programmatic Callback For complex logic that can not be expressed as rules, add an `authorize` callback. It is invoked only when no rule matches: ```typescript const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [...], authorize: (context, req) => context.roles.includes('superadmin'), }); ``` If `authorize` returns `true`, the request is allowed. If it returns `false`, the `defaultPolicy` applies. ## Proto-Based Authorization For defining authorization rules directly in `.proto` files using custom options, see the dedicated [Proto-Based Authorization](/en/guide/auth/proto-authz) page. Proto options are read at runtime by `createProtoAuthzInterceptor()` and take priority over programmatic rules. ## Interceptor Chain Position Auth and authz interceptors must be placed **after** `errorHandler` and **before** resilience interceptors: ``` errorHandler -> AUTH -> AUTHZ -> timeout -> bulkhead -> circuitBreaker -> retry -> ... ``` This ensures: * Authentication errors are properly formatted by `errorHandler` * Unauthenticated requests are rejected before consuming resilience resources * Auth context is available to all downstream interceptors ```typescript const server = createServer({ services: [routes], interceptors: [ ...createDefaultInterceptors(), jwtAuth, // after errorHandler chain authz, // after authentication ], }); ``` ## Full Example ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { createJwtAuthInterceptor, createAuthzInterceptor } from '@connectum/auth'; const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', issuer: 'https://auth.example.com/', }); const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'public', methods: ['public.v1.PublicService/*'], effect: 'allow' }, { name: 'admin-only', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, { name: 'write-scope', methods: ['data.v1.DataService/Write*'], requires: { scopes: ['write'] }, effect: 'allow' }, ], authorize: (context, req) => { // Fallback: superadmins can do anything return context.roles.includes('superadmin'); }, }); const server = createServer({ services: [routes], interceptors: [...createDefaultInterceptors(), jwtAuth, authz], }); await server.start(); ``` ## Related * [Auth Overview](/en/guide/auth) -- all authentication strategies * [JWT Authentication](/en/guide/auth/jwt) -- token verification * [Proto-Based Authorization](/en/guide/auth/proto-authz) -- declarative authz via `.proto` options * [Auth Context](/en/guide/auth/context) -- accessing identity in handlers * [Method Filtering](/en/guide/interceptors/method-filtering) -- per-method interceptor routing * [@connectum/auth](/en/packages/auth) -- Package Guide * [@connectum/auth API](/en/api/@connectum/auth/) -- Full API Reference * [ADR-024: Auth/Authz Strategy](/en/contributing/adr/024-auth-authz-strategy) -- design rationale --- --- url: /en/guide/observability/backends.md --- # Backends & Configuration Configure OpenTelemetry exporters, provider management, and integration with observability backends like Jaeger and Grafana. ## Environment Variables Reference ### Service Metadata | Variable | Description | |----------|-------------| | `OTEL_SERVICE_NAME` | Service name (required) | | `OTEL_SERVICE_VERSION` | Service version | | `OTEL_SERVICE_NAMESPACE` | Service namespace (e.g., `production`) | ### Exporters | Variable | Description | Values | |----------|-------------|--------| | `OTEL_TRACES_EXPORTER` | Trace exporter | `otlp`, `console`, `none` | | `OTEL_METRICS_EXPORTER` | Metrics exporter | `otlp`, `console`, `none` | | `OTEL_LOGS_EXPORTER` | Logs exporter | `otlp`, `console`, `none` | ### OTLP Endpoints | Variable | Description | |----------|-------------| | `OTEL_EXPORTER_OTLP_ENDPOINT` | Base OTLP endpoint | | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Traces endpoint (overrides base) | | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | Metrics endpoint (overrides base) | | `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | Logs endpoint (overrides base) | ### OTLP Settings | Variable | Description | |----------|-------------| | `OTEL_EXPORTER_OTLP_PROTOCOL` | Protocol: `http/protobuf` or `grpc` | | `OTEL_EXPORTER_OTLP_HEADERS` | Headers (comma-separated `key=value`) | ### Batch Span Processor | Variable | Default | Description | |----------|---------|-------------| | `OTEL_BSP_SCHEDULE_DELAY` | `5000` | Schedule delay (ms) | | `OTEL_BSP_MAX_QUEUE_SIZE` | `2048` | Max queue size | | `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` | `512` | Max batch size | | `OTEL_BSP_EXPORT_TIMEOUT` | `30000` | Export timeout (ms) | ### Instrumentations | Variable | Description | |----------|-------------| | `OTEL_NODE_DISABLED_INSTRUMENTATIONS` | Comma-separated list of disabled auto-instrumentations | ## Provider Management The OTel provider initializes lazily when you first call `getTracer()`, `getMeter()`, or `getLogger()`. For explicit control: ```typescript import { initProvider, shutdownProvider } from '@connectum/otel'; // Explicit initialization (optional) initProvider({ serviceName: 'my-service', serviceVersion: '1.0.0', }); // Graceful shutdown (flush pending telemetry) server.onShutdown('otel', async () => { await shutdownProvider(); }); ``` ## Development vs Production Configuration ### Development Use console exporters for immediate visibility: ```bash OTEL_SERVICE_NAME=greeter-service OTEL_TRACES_EXPORTER=console OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=console ``` ### Production Export to an OTLP-compatible collector (Jaeger, Grafana Tempo, Datadog): ```bash OTEL_SERVICE_NAME=greeter-service OTEL_SERVICE_VERSION=1.0.0 OTEL_SERVICE_NAMESPACE=production OTEL_TRACES_EXPORTER=otlp OTEL_METRICS_EXPORTER=otlp OTEL_LOGS_EXPORTER=otlp OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_BSP_SCHEDULE_DELAY=5000 OTEL_BSP_MAX_QUEUE_SIZE=2048 OTEL_NODE_DISABLED_INSTRUMENTATIONS=fs,dns ``` ## Integration with Backends ### Jaeger ```yaml # docker-compose.yml services: jaeger: image: jaegertracing/all-in-one:latest ports: - "16686:16686" # Jaeger UI - "4318:4318" # OTLP HTTP ``` ```bash OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 ``` ### Grafana (Tempo + Prometheus + Loki) ```bash # Traces -> Tempo OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://tempo:4318/v1/traces # Metrics -> Prometheus (via OTLP) OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://prometheus:4318/v1/metrics # Logs -> Loki (via OTLP) OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://loki:4318/v1/logs ``` ## Related * [Observability Overview](/en/guide/observability) -- back to overview * [Tracing](/en/guide/observability/tracing) -- server/client interceptors * [Metrics](/en/guide/observability/metrics) -- custom metrics * [Logging](/en/guide/observability/logging) -- structured logging * [Docker Deployment](/en/guide/production/docker) -- containerized setup * [@connectum/otel](/en/packages/otel) -- Package Guide * [@connectum/otel API](/en/api/@connectum/otel/) -- Full API Reference --- --- url: /en/guide/interceptors/built-in.md --- # Built-in Interceptors Connectum provides 8 production-ready interceptors via `createDefaultInterceptors()`. They form a fixed chain that covers error handling, resilience, validation, and serialization. ## The Default Chain ``` errorHandler -> timeout -> bulkhead -> circuitBreaker -> retry -> fallback -> validation -> serializer ``` | # | Interceptor | Purpose | Default | |---|-------------|---------|---------| | 1 | **errorHandler** | Normalizes errors into `ConnectError` | Enabled | | 2 | **timeout** | Limits request execution time | Enabled (30s) | | 3 | **bulkhead** | Limits concurrent requests | Enabled (capacity 10, queue 10) | | 4 | **circuitBreaker** | Prevents cascading failures | Enabled (threshold 5) | | 5 | **retry** | Retries transient failures with exponential backoff | Enabled (3 retries) | | 6 | **fallback** | Graceful degradation | Disabled | | 7 | **validation** | Validates via `@connectrpc/validate` | Enabled | | 8 | **serializer** | JSON serialization for protobuf | Enabled | The order is deliberate: `errorHandler` is outermost (catches everything), `serializer` is innermost (closest to the handler). ## Using with createServer The recommended way to add the built-in interceptors: ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: createDefaultInterceptors(), shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ## Customizing the Default Chain Pass options to `createDefaultInterceptors()` to customize individual interceptors. Set an interceptor to `false` to disable it entirely: ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; const interceptors = createDefaultInterceptors({ timeout: { duration: 10_000 }, // Custom timeout (10s instead of 30s) retry: false, // Disable retry bulkhead: { capacity: 20, queueSize: 20 }, // Higher concurrency limits // All others remain at defaults }); const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors, shutdown: { autoShutdown: true }, }); ``` ## Combining with Custom Interceptors Spread the default chain and append your own interceptors: ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: [ ...createDefaultInterceptors(), myCustomInterceptor, // Added after the built-in chain ], shutdown: { autoShutdown: true }, }); ``` ::: tip Auth interceptors require a specific position If your custom interceptor is an authentication or authorization interceptor from `@connectum/auth`, it must be placed **immediately after** `errorHandler` -- before `timeout` and other resilience interceptors. See the [Custom Interceptors](/en/guide/interceptors/custom) guide for a manual chain example and [ADR-024](/en/contributing/adr/024-auth-authz-strategy) for the rationale. ::: ## Standalone Usage You can use `createDefaultInterceptors()` outside of `createServer`: ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; const interceptors = createDefaultInterceptors({ timeout: { duration: 10_000 }, retry: { maxRetries: 5 }, }); ``` For detailed documentation on each interceptor, see the [@connectum/interceptors README](https://github.com/Connectum-Framework/connectum/tree/main/packages/interceptors). ## Execution Order Interceptors execute in the order they are defined. Each interceptor wraps the next one: ``` Request -> interceptor1 -> interceptor2 -> interceptor3 -> handler Response <- interceptor1 <- interceptor2 <- interceptor3 <- handler ``` This means: * **Before-logic** of the first interceptor runs first * **After-logic** of the first interceptor runs last * The first interceptor is the outer layer (ideal for error handling) * The last interceptor is closest to the handler (ideal for serialization) This is why the default chain places `errorHandler` first and `serializer` last. ## Best Practices 1. **Error handler first** -- place the error handler first in the chain so it catches errors from all subsequent interceptors. 2. **Do not mutate `req.message`** -- create a new request object via spread: `{ ...req, message: newMessage }`. 3. **Always call `next()`** -- if the interceptor does not abort the chain, it must call `next(req)` and return the result. 4. **Cleanup in `finally`** -- use `try/finally` for resource cleanup (timers, counters). 5. **Type safety** -- use `import type { Interceptor }` for type-safe interceptor definitions. 6. **Use factories** -- wrap interceptors in `create*Interceptor(options)` for configurability. 7. **`skip*` options for technical limitations** -- options like `skipStreaming` and `skipGrpcServices` are meant for technical limitations of the interceptor, not for business routing. 8. **`createMethodFilterInterceptor` for routing** -- use it for declarative interceptor routing by service and method. ## Related * [Interceptors Overview](/en/guide/interceptors) -- quick start and key concepts * [Custom Interceptors](/en/guide/interceptors/custom) -- factory pattern, error handling, testing * [Method Filtering](/en/guide/interceptors/method-filtering) -- per-service and per-method routing * [@connectum/interceptors](/en/packages/interceptors) -- Package Guide * [@connectum/interceptors API](/en/api/@connectum/interceptors/) -- Full API Reference * [ADR-006: Resilience Patterns](/en/contributing/adr/006-resilience-pattern-implementation) -- design rationale for the interceptor chain --- --- url: /en/api/@connectum/interceptors/bulkhead.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / bulkhead # bulkhead Bulkhead interceptor Limits concurrent requests to prevent resource exhaustion. ## Functions * [createBulkheadInterceptor](functions/createBulkheadInterceptor.md) --- --- url: /en/api/@connectum/interceptors/circuit-breaker.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / circuit-breaker # circuit-breaker Circuit breaker interceptor Prevents cascading failures by breaking circuit when service fails repeatedly. ## Functions * [createCircuitBreakerInterceptor](functions/createCircuitBreakerInterceptor.md) --- --- url: /en/api/@connectum/auth/classes/AuthzDeniedError.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AuthzDeniedError # Class: AuthzDeniedError Defined in: [packages/auth/src/errors.ts:26](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L26) Authorization denied error. Carries server-side details (rule name, required roles/scopes) while exposing only "Access denied" to the client via SanitizableError protocol. ## Extends * `ConnectError` ## Implements * `SanitizableError` ## Constructors ### Constructor > **new AuthzDeniedError**(`details`): `AuthzDeniedError` Defined in: [packages/auth/src/errors.ts:39](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L39) #### Parameters ##### details [`AuthzDeniedDetails`](../interfaces/AuthzDeniedDetails.md) #### Returns `AuthzDeniedError` #### Overrides `ConnectError.constructor` ## Properties ### authzDetails > `readonly` **authzDetails**: [`AuthzDeniedDetails`](../interfaces/AuthzDeniedDetails.md) Defined in: [packages/auth/src/errors.ts:29](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L29) *** ### cause > **cause**: `unknown` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:46 The underlying cause of this error, if any. In cases where the actual cause is elided with the error message, the cause is specified here so that we don't leak the underlying error, but instead make it available for logging. #### Inherited from `ConnectError.cause` *** ### clientMessage > `readonly` **clientMessage**: `"Access denied"` = `"Access denied"` Defined in: [packages/auth/src/errors.ts:27](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L27) #### Implementation of `SanitizableError.clientMessage` *** ### code > `readonly` **code**: `Code` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:20 The Code for this error. #### Inherited from `ConnectError.code` *** ### details > **details**: (`OutgoingDetail` | `IncomingDetail`)\[] Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:32 When an error is parsed from the wire, incoming error details are stored in this property. They can be retrieved using findDetails(). When an error is constructed to be sent over the wire, outgoing error details are stored in this property as well. #### Inherited from `ConnectError.details` *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from `ConnectError.message` *** ### metadata > `readonly` **metadata**: `Headers` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:24 A union of response headers and trailers associated with this error. #### Inherited from `ConnectError.metadata` *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:40 #### Inherited from `ConnectError.name` *** ### rawMessage > `readonly` **rawMessage**: `string` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:39 The error message, but without a status code in front. For example, a new `ConnectError("hello", Code.NotFound)` will have the message `[not found] hello`, and the rawMessage `hello`. #### Inherited from `ConnectError.rawMessage` *** ### ruleName > `readonly` **ruleName**: `string` Defined in: [packages/auth/src/errors.ts:28](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L28) *** ### stack? > `optional` **stack**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from `ConnectError.stack` *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:67 The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured *after* the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. #### Inherited from `ConnectError.stackTraceLimit` ## Accessors ### serverDetails #### Get Signature > **get** **serverDetails**(): `Readonly`<`Record`<`string`, `unknown`>> Defined in: [packages/auth/src/errors.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L31) ##### Returns `Readonly`<`Record`<`string`, `unknown`>> #### Implementation of `SanitizableError.serverDetails` ## Methods ### findDetails() #### Call Signature > **findDetails**<`Desc`>(`desc`): `MessageShape`<`Desc`>\[] Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:77 Retrieve error details from a ConnectError. On the wire, error details are wrapped with google.protobuf.Any, so that a server or middleware can attach arbitrary data to an error. This function decodes the array of error details from the ConnectError object, and returns an array with the decoded messages. Any decoding errors are ignored, and the detail will simply be omitted from the list. ##### Type Parameters ###### Desc `Desc` *extends* `DescMessage` ##### Parameters ###### desc `Desc` ##### Returns `MessageShape`<`Desc`>\[] ##### Inherited from `ConnectError.findDetails` #### Call Signature > **findDetails**(`registry`): `Message`\[] Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:78 Retrieve error details from a ConnectError. On the wire, error details are wrapped with google.protobuf.Any, so that a server or middleware can attach arbitrary data to an error. This function decodes the array of error details from the ConnectError object, and returns an array with the decoded messages. Any decoding errors are ignored, and the detail will simply be omitted from the list. ##### Parameters ###### registry `Registry` ##### Returns `Message`\[] ##### Inherited from `ConnectError.findDetails` *** ### \[hasInstance]\() > `static` **\[hasInstance]**(`v`): `boolean` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:68 #### Parameters ##### v `unknown` #### Returns `boolean` #### Inherited from `ConnectError.[hasInstance]` *** ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:51 Creates a `.stack` property on `targetObject`, which when accessed returns a string representing the location in the code at which `Error.captureStackTrace()` was called. ```js const myObject = {}; Error.captureStackTrace(myObject); myObject.stack; // Similar to `new Error().stack` ``` The first line of the trace will be prefixed with `${myObject.name}: ${myObject.message}`. The optional `constructorOpt` argument accepts a function. If given, all frames above `constructorOpt`, including `constructorOpt`, will be omitted from the generated stack trace. The `constructorOpt` argument is useful for hiding implementation details of error generation from the user. For instance: ```js function a() { b(); } function b() { c(); } function c() { // Create an error without stack trace to avoid calculating the stack trace twice. const { stackTraceLimit } = Error; Error.stackTraceLimit = 0; const error = new Error(); Error.stackTraceLimit = stackTraceLimit; // Capture the stack trace above function b Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace throw error; } a(); ``` #### Parameters ##### targetObject `object` ##### constructorOpt? `Function` #### Returns `void` #### Inherited from `ConnectError.captureStackTrace` *** ### from() > `static` **from**(`reason`, `code?`): `ConnectError` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.1\_@bufbuild+protobuf@2.11.0/node\_modules/@connectrpc/connect/dist/esm/connect-error.d.ts:67 Convert any value - typically a caught error into a ConnectError, following these rules: * If the value is already a ConnectError, return it as is. * If the value is an AbortError or TimeoutError from the fetch API, return the message of the error with code Canceled. * For other Errors, return the error message with code Unknown by default. * For other values, return the values String representation as a message, with the code Unknown by default. The original value will be used for the "cause" property for the new ConnectError. #### Parameters ##### reason `unknown` ##### code? `Code` #### Returns `ConnectError` #### Inherited from `ConnectError.from` *** ### isError() > `static` **isError**(`error`): `error is Error` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.esnext.error.d.ts:23 Indicates whether the argument provided is a built-in Error instance or not. #### Parameters ##### error `unknown` #### Returns `error is Error` #### Inherited from `ConnectError.isError` *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:55 #### Parameters ##### err `Error` ##### stackTraces `CallSite`\[] #### Returns `any` #### See https://v8.dev/docs/stack-trace-api#customizing-stack-traces #### Inherited from `ConnectError.prepareStackTrace` --- --- url: /en/api/@connectum/healthcheck/classes/HealthcheckManager.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / HealthcheckManager # Class: HealthcheckManager Defined in: [HealthcheckManager.ts:26](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/HealthcheckManager.ts#L26) Healthcheck manager Manages health status for all registered services. Module-level singleton. Import `healthcheckManager` from the package. ## Example ```typescript import { healthcheckManager, ServingStatus } from '@connectum/healthcheck'; // After server.start(): healthcheckManager.update(ServingStatus.SERVING); ``` ## Constructors ### Constructor > **new HealthcheckManager**(): `HealthcheckManager` #### Returns `HealthcheckManager` ## Methods ### areAllHealthy() > **areAllHealthy**(): `boolean` Defined in: [HealthcheckManager.ts:80](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/HealthcheckManager.ts#L80) Check if all services are healthy (SERVING) #### Returns `boolean` True if all services are SERVING *** ### clear() > **clear**(): `void` Defined in: [HealthcheckManager.ts:108](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/HealthcheckManager.ts#L108) Clear all services #### Returns `void` *** ### getAllStatuses() > **getAllStatuses**(): `Map`<`string`, [`ServiceStatus`](../interfaces/ServiceStatus.md)> Defined in: [HealthcheckManager.ts:71](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/HealthcheckManager.ts#L71) Get all services health status #### Returns `Map`<`string`, [`ServiceStatus`](../interfaces/ServiceStatus.md)> Map of service name to health status *** ### getStatus() > **getStatus**(`service`): [`ServiceStatus`](../interfaces/ServiceStatus.md) | `undefined` Defined in: [HealthcheckManager.ts:62](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/HealthcheckManager.ts#L62) Get service health status #### Parameters ##### service `string` Service name #### Returns [`ServiceStatus`](../interfaces/ServiceStatus.md) | `undefined` Service status or undefined if not found *** ### initialize() > **initialize**(`serviceNames`): `void` Defined in: [HealthcheckManager.ts:96](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/HealthcheckManager.ts#L96) Initialize services map Merges new service names with existing state. Services that were already registered retain their current status. New services start with UNKNOWN status. #### Parameters ##### serviceNames `string`\[] Array of service names to track #### Returns `void` *** ### update() > **update**(`status`, `service?`): `void` Defined in: [HealthcheckManager.ts:39](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/HealthcheckManager.ts#L39) Update service health status When called without a service name, updates ALL registered services. When called with an unknown service name, throws an error. #### Parameters ##### status `HealthCheckResponse_ServingStatus` New serving status ##### service? `string` Service name (if not provided, updates all services) #### Returns `void` #### Throws Error if service name is provided but not registered --- --- url: /en/api/@connectum/auth/classes/LruCache.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / LruCache # Class: LruCache\ Defined in: [packages/auth/src/cache.ts:13](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/cache.ts#L13) ## Type Parameters ### T `T` ## Constructors ### Constructor > **new LruCache**<`T`>(`options`): `LruCache`<`T`> Defined in: [packages/auth/src/cache.ts:18](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/cache.ts#L18) #### Parameters ##### options ###### maxSize? `number` ###### ttl `number` #### Returns `LruCache`<`T`> ## Accessors ### size #### Get Signature > **get** **size**(): `number` Defined in: [packages/auth/src/cache.ts:63](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/cache.ts#L63) ##### Returns `number` ## Methods ### clear() > **clear**(): `void` Defined in: [packages/auth/src/cache.ts:59](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/cache.ts#L59) #### Returns `void` *** ### get() > **get**(`key`): `T` | `undefined` Defined in: [packages/auth/src/cache.ts:26](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/cache.ts#L26) #### Parameters ##### key `string` #### Returns `T` | `undefined` *** ### set() > **set**(`key`, `value`): `void` Defined in: [packages/auth/src/cache.ts:41](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/cache.ts#L41) #### Parameters ##### key `string` ##### value `T` #### Returns `void` --- --- url: /en/contributing/cli-commands.md --- # CLI Commands Reference ## Overview Complete reference of CLI commands for working with the Connectum monorepo. ## Prerequisites * **Node.js**: 25+ (for development), 18+ (for consumers) * **pnpm**: 10+ * **protoc**: Latest version for proto generation ### Installation Check ```bash # Check Node.js version node --version # Should be 25+ for development # Check pnpm version pnpm --version # Should be >= 10.0.0 # Check protoc version protoc --version # Should be installed ``` ## Root-Level Commands Commands are executed from the monorepo root. ### Installation ```bash # Install all dependencies pnpm install # Install with frozen lockfile (CI/CD) pnpm install --frozen-lockfile # Update all dependencies pnpm update # Update specific package pnpm update @connectum/core ``` ### Build Commands ```bash # Build all packages (tsup → dist/) pnpm build # Build specific package pnpm --filter @connectum/core build # Build only proto files pnpm build:proto # Clean all build outputs pnpm clean ``` Each package compiles TypeScript to JavaScript + type declarations (`dist/`) using tsup. The output includes source maps for IDE jump-to-source support. ### Type Checking ```bash # Type check all packages pnpm typecheck # Type check specific package pnpm --filter @connectum/otel typecheck # Watch mode (continuous type checking) pnpm --filter @connectum/core typecheck --watch ``` ### Testing ```bash # Run all tests pnpm test # Run only unit tests pnpm test:unit # Run only integration tests pnpm test:integration # Run tests for specific package pnpm --filter @connectum/core test # Run tests with coverage pnpm test -- --coverage # Watch mode pnpm --filter @connectum/core test -- --watch ``` ### Linting and Formatting ```bash # Check code style (Biome) pnpm lint # Fix code style issues pnpm format # Check specific package pnpm --filter @connectum/interceptors lint # Run Biome directly biome check src/ # Fix with Biome biome check --write src/ ``` ### Development ```bash # Run all packages in development mode (parallel) pnpm dev # Run specific package pnpm --filter @connectum/core dev # Run with environment file pnpm --filter @connectum/core dev ``` ### Versioning and Release ```bash # Create changeset (interactive) pnpm changeset # Version packages (update versions based on changesets) pnpm changeset version # Publish packages to npm pnpm changeset publish # Publish with specific tag pnpm changeset publish --tag alpha pnpm changeset publish --tag beta ``` ### Documentation ```bash # Generate API Reference from JSDoc comments (TypeDoc → docs/en/api/) pnpm docs:api # Or use the convenience script from workspace root (Connectum/) ./gen-api-docs.sh # Skip proto rebuild if already done ./gen-api-docs.sh --skip-proto ``` The generated API Reference is output to `docs/en/api/` and integrates with VitePress sidebar automatically via `typedoc-sidebar.json`. ## Package-Level Commands Commands for working with individual packages. ### Navigation ```bash # Navigate to package directory cd packages/core # Or use pnpm filter pnpm --filter @connectum/core ``` ### Common Package Scripts Most packages support: ```bash # Start package (production mode) pnpm start # Development mode with watch pnpm dev # Build package pnpm build # Type check pnpm typecheck # Run tests pnpm test pnpm test:unit pnpm test:integration # Lint pnpm lint # Clean build outputs pnpm clean ``` ### Package-Specific Commands #### @connectum/core ```bash # Start server example pnpm --filter @connectum/core start # Development with watch pnpm --filter @connectum/core dev # Run integration tests pnpm --filter @connectum/core test:integration ``` #### @connectum/cli ```bash # Run CLI commands pnpm --filter @connectum/cli start # Development with watch pnpm --filter @connectum/cli dev ``` #### examples/ (directory, not a package) ```bash # Run basic example node examples/basic-service/src/index.ts # Run example with custom interceptor node examples/with-custom-interceptor/src/index.ts # Development mode with watch node --watch examples/basic-service/src/index.ts ``` ## Turbo Commands Turborepo orchestration commands. ### Run Tasks ```bash # Run task for all packages turbo run build turbo run test turbo run typecheck # Run task for specific package turbo run build --filter=@connectum/core # Run in parallel turbo run build --parallel # Force (ignore cache) turbo run build --force # Dry run (show what would run) turbo run build --dry-run ``` ### Cache Management ```bash # Clear turbo cache turbo run build --force # Or manually delete rm -rf .turbo ``` ## pnpm Workspace Commands ### Filtering ```bash # Run command in specific package pnpm --filter @connectum/core # Run in all packages matching pattern pnpm --filter "@connectum/*" build # Run in package and dependencies pnpm --filter @connectum/core... build # Run in package and dependents pnpm --filter ...@connectum/otel build ``` ### Dependencies ```bash # List all dependencies pnpm list # List dependencies for specific package pnpm --filter @connectum/core list # Show dependency tree pnpm list --depth 3 # Why is package installed? pnpm why @bufbuild/protobuf # Outdated packages pnpm outdated # Update interactive pnpm update -i ``` ### Workspace Management ```bash # Add dependency to specific package pnpm --filter @connectum/core add @connectrpc/connect # Add dev dependency pnpm --filter @connectum/core add -D typescript # Add workspace dependency pnpm --filter @connectum/core add @connectum/otel@workspace:^ # Remove dependency pnpm --filter @connectum/core remove @connectrpc/connect # Link all workspace packages pnpm install ``` ## Development Workflow Commands ### New Package Setup ```bash # Create package directory mkdir -p packages/my-package/{src,tests} # Create package.json cat > packages/my-package/package.json < packages/my-package/tsconfig.json <&1 | grep -q "Error" || echo "Type stripping works!" # Verify pnpm workspace pnpm list --depth 0 # Verify proto compiler protoc --version # Verify Biome biome --version ``` ### Performance Analysis ```bash # Turbo performance analysis turbo run build --profile # Bundle size analysis pnpm --filter @connectum/core exec du -sh node_modules # Dependency analysis pnpm why # Find duplicate dependencies pnpm dedupe ``` ## Advanced Commands ### Monorepo Utilities ```bash # Run command in all packages pnpm -r exec # Example: update all package versions pnpm -r exec npm version patch # Run command in parallel pnpm -r --parallel exec # Run script in all packages pnpm -r run build ``` ### Git Hooks (Husky) ```bash # Install git hooks pnpm prepare # Skip git hooks (not recommended) git commit --no-verify -m "message" # Test commit message echo "feat: test message DEV-123" | pnpm commitlint ``` ### Environment Management ```bash # Load environment from file export $(cat .env | xargs) && pnpm dev # Run with environment variables PORT=3000 NODE_ENV=production pnpm start # Use .env file pnpm --filter @connectum/core dev # Automatically loads .env ``` ## Quick Reference ### Most Used Commands ```bash # Development workflow pnpm install # Install dependencies pnpm dev # Start development pnpm typecheck # Check types pnpm test # Run tests pnpm lint # Check code style # Build and release pnpm build # Build all packages pnpm changeset # Create changeset pnpm changeset version # Bump versions pnpm changeset publish # Publish to npm # Cleanup pnpm clean # Clean build outputs rm -rf node_modules # Remove dependencies pnpm install # Reinstall ``` ### Package Filters Cheat Sheet ```bash # Specific package pnpm --filter @connectum/core # Multiple packages pnpm --filter @connectum/{core,interceptors} # All packages matching pattern pnpm --filter "@connectum/*" # Package and dependencies pnpm --filter @connectum/core... # Package and dependents pnpm --filter ...@connectum/otel # Exclude pattern pnpm --filter "!@connectum/testing" ``` ## References * **Turborepo Docs**: https://turbo.build/repo/docs * **pnpm Workspaces**: https://pnpm.io/workspaces * **pnpm Filtering**: https://pnpm.io/filtering * **Node.js Test Runner**: https://nodejs.org/api/test.html * **Biome CLI**: https://biomejs.dev/reference/cli/ ## See Also * [About Connectum](/en/guide/about) -- System architecture * [Development Setup](./development-setup) -- Environment setup guide --- --- url: /en/guide/service-communication/client-interceptors.md --- # Client Interceptors Interceptors for outgoing gRPC client calls -- observability, resilience, and custom logic. ## OTel Client Interceptor `createOtelClientInterceptor()` instruments outgoing RPC calls with OpenTelemetry tracing and metrics: ```typescript import { createGrpcTransport } from '@connectrpc/connect-node'; import { createOtelClientInterceptor } from '@connectum/otel'; const transport = createGrpcTransport({ baseUrl: 'http://user-service:5001', httpVersion: '2', interceptors: [ createOtelClientInterceptor({ serverAddress: 'user-service', // Required serverPort: 5001, }), ], }); ``` The interceptor: * Injects trace context into outgoing requests via `propagation.inject()` -- downstream services receive the parent span * Creates `SpanKind.CLIENT` spans with [OTel semantic conventions](https://opentelemetry.io/docs/specs/semconv/rpc/connect-rpc/) * Records `rpc.client.*` metrics (duration, request/response size) ### Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `serverAddress` | `string` | **(required)** | Target server address (`server.address` attribute) | | `serverPort` | `number` | -- | Target server port (`server.port` attribute) | | `withoutTracing` | `boolean` | `false` | Disable span creation (metrics only) | | `withoutMetrics` | `boolean` | `false` | Disable metric recording (tracing only) | | `filter` | `OtelFilter` | -- | Skip specific RPCs from instrumentation | | `attributeFilter` | `OtelAttributeFilter` | -- | Exclude specific span attributes | | `recordMessages` | `boolean` | `false` | Include message content in span events (may contain sensitive data) | ### Trace Context Propagation When both server and client interceptors are configured, trace context flows automatically across service boundaries: ``` Service A (server span) → Service A (client span) → Service B (server span) ``` ```typescript // Service A: server OTel interceptor + client OTel interceptor const server = createServer({ services: [routes], interceptors: [ createOtelInterceptor({ serverPort: 5000 }), // Server spans ], }); const userTransport = createGrpcTransport({ baseUrl: 'http://user-service:5001', httpVersion: '2', interceptors: [ createOtelClientInterceptor({ // Client spans serverAddress: 'user-service', serverPort: 5001, }), ], }); ``` In a trace viewer (Jaeger, Grafana Tempo), you'll see a single trace spanning both services with linked spans. ## Resilience for Clients Use `createDefaultInterceptors()` on client transports for circuit breaker, timeout, and retry. Disable server-only interceptors: ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; const transport = createGrpcTransport({ baseUrl: 'http://inventory-service:5000', httpVersion: '2', interceptors: [ createOtelClientInterceptor({ serverAddress: 'inventory-service', serverPort: 5000, }), ...createDefaultInterceptors({ circuitBreaker: { failureThreshold: 5 }, timeout: { duration: 5_000 }, retry: { maxRetries: 2 }, // Disable server-only interceptors bulkhead: false, errorHandler: false, serializer: false, validation: false, }), ], }); ``` ### Circuit Breaker Behavior The circuit breaker tracks consecutive failures per client transport: | State | Behavior | |-------|----------| | **Closed** | Requests pass through normally | | **Open** | Requests fail immediately with `Unavailable` (no downstream call) | | **Half-Open** | A single probe request is allowed; success closes, failure re-opens | The default `failureThreshold` is 5 consecutive failures. After the circuit opens, it automatically transitions to half-open after a cooldown period. ### Per-Service Configuration Create separate transports with different resilience settings for each downstream service: ```typescript // Critical service: aggressive retry, short timeout const paymentTransport = createGrpcTransport({ baseUrl: 'http://payment-service:5000', httpVersion: '2', interceptors: [ createOtelClientInterceptor({ serverAddress: 'payment-service', serverPort: 5000 }), ...createDefaultInterceptors({ timeout: { duration: 3_000 }, retry: { maxRetries: 3 }, circuitBreaker: { failureThreshold: 3 }, bulkhead: false, errorHandler: false, serializer: false, validation: false, }), ], }); // Non-critical service: lenient timeout, fewer retries const recommendationTransport = createGrpcTransport({ baseUrl: 'http://recommendation-service:5000', httpVersion: '2', interceptors: [ createOtelClientInterceptor({ serverAddress: 'recommendation-service', serverPort: 5000 }), ...createDefaultInterceptors({ timeout: { duration: 10_000 }, retry: { maxRetries: 1 }, circuitBreaker: { failureThreshold: 10 }, bulkhead: false, errorHandler: false, serializer: false, validation: false, }), ], }); ``` ## Client Metrics `createRpcClientMetrics()` provides standalone client metrics following OTel semantic conventions: ```typescript import { createRpcClientMetrics, getMeter } from '@connectum/otel'; const meter = getMeter(); const clientMetrics = createRpcClientMetrics(meter); ``` | Metric | Name | Unit | Description | |--------|------|------|-------------| | `callDuration` | `rpc.client.call.duration` | seconds | Histogram of call durations | | `requestSize` | `rpc.client.request.size` | bytes | Histogram of request sizes | | `responseSize` | `rpc.client.response.size` | bytes | Histogram of response sizes | These metrics are recorded automatically when using `createOtelClientInterceptor()` (unless `withoutMetrics: true`). ## Streaming Instrumentation Both server and client interceptors automatically instrument streaming RPCs (client streaming, server streaming, and bidirectional). **Span lifecycle** for streaming calls: 1. Span starts when the RPC begins 2. Individual `rpc.message` events are recorded for each sent/received message (when `recordMessages` is enabled) 3. Span ends when the stream is fully consumed, errors, or is broken This ensures accurate duration measurement for long-lived streams. ### Streaming Attributes | Attribute | Value | Description | |-----------|-------|-------------| | `rpc.message.id` | sequential number | Message sequence number within the stream | | `rpc.message.type` | `"SENT"` / `"RECEIVED"` | Message direction | | `rpc.message.uncompressed_size` | bytes (estimated) | Estimated message size | | `network.transport` | `"tcp"` | Network transport protocol | ## Related * [Service Communication](/en/guide/service-communication) -- overview, transport configuration, service discovery * [Communication Patterns](./patterns) -- request-response, fan-out, streaming * [Distributed Tracing](/en/guide/observability/tracing) -- server/client interceptors, deep tracing * [Interceptors](/en/guide/interceptors) -- server-side interceptor chain * [@connectum/otel](/en/packages/otel) -- Package Guide * [@connectum/otel API](/en/api/@connectum/otel/) -- Full API Reference --- --- url: /en/api/@connectum/otel/client-interceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / client-interceptor # client-interceptor ConnectRPC OpenTelemetry client interceptor Creates a ConnectRPC interceptor that instruments outgoing RPC calls with OpenTelemetry tracing and metrics following semantic conventions. Key differences from the server interceptor: * Uses `propagation.inject()` to propagate trace context to outgoing requests * Uses `SpanKind.CLIENT` instead of `SpanKind.SERVER` * Uses `rpc.client.*` metrics instead of `rpc.server.*` * `serverAddress` is REQUIRED (target server, not local hostname) * No `trustRemote` option (client always creates spans in active context) ## See * https://opentelemetry.io/docs/specs/semconv/rpc/connect-rpc/ * https://opentelemetry.io/docs/specs/semconv/rpc/rpc-metrics/ ## Functions * [createOtelClientInterceptor](functions/createOtelClientInterceptor.md) --- --- url: /en/api/@connectum/cli/commands/proto-sync.md --- [Connectum API Reference](../../../../index.md) / [@connectum/cli](../../index.md) / commands/proto-sync # commands/proto-sync Proto sync command Syncs proto types from a running Connectum server via gRPC Reflection. Pipeline: 1. Connect to server via ServerReflectionClient 2. Discover services and build FileRegistry 3. Serialize as FileDescriptorSet binary (.binpb) 4. Run `buf generate` with .binpb input ## Interfaces * [ProtoSyncOptions](interfaces/ProtoSyncOptions.md) ## Variables * [protoSyncCommand](variables/protoSyncCommand.md) ## Functions * [executeProtoSync](functions/executeProtoSync.md) --- --- url: /en/guide/service-communication/patterns.md --- # Communication Patterns Common inter-service communication patterns for gRPC/ConnectRPC microservices. ## Request-Response Chain The most common pattern -- one service calls another sequentially, each call waiting for a response before proceeding. ```mermaid sequenceDiagram participant GW as API Gateway participant OS as Order Service participant IS as Inventory Service participant PS as Payment Service GW->>OS: CreateOrder (gRPC) OS->>IS: CheckStock (gRPC) IS-->>OS: StockResponse OS->>PS: ProcessPayment (gRPC) PS-->>OS: PaymentResponse OS-->>GW: OrderResponse ``` ```typescript import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { InventoryService } from '#gen/inventory/v1/inventory_pb.js'; import { PaymentService } from '#gen/payment/v1/payment_pb.js'; const inventoryClient = createClient(InventoryService, inventoryTransport); const paymentClient = createClient(PaymentService, paymentTransport); // Sequential: check stock, then process payment const routes = (router) => { router.service(OrderService, { async createOrder(req) { const stock = await inventoryClient.checkStock({ sku: req.sku }); if (!stock.available) { throw new ConnectError('Out of stock', Code.FailedPrecondition); } const payment = await paymentClient.processPayment({ amount: stock.price, customerId: req.customerId, }); // ... create order with stock and payment data }, }); }; ``` ::: tip When to use Use sequential chains when each step depends on the result of the previous one. If steps are independent, prefer [Fan-Out / Fan-In](#fan-out-fan-in) for better latency. ::: ## Fan-Out / Fan-In When a service needs data from multiple independent downstream services, call them in parallel with `Promise.all`: ```typescript async createOrder(req) { // Fan-out: parallel calls to independent services const [stock, pricing, customerProfile] = await Promise.all([ inventoryClient.checkStock({ sku: req.sku }), pricingClient.getPrice({ sku: req.sku }), customerClient.getProfile({ customerId: req.customerId }), ]); // Fan-in: combine results return createOrderFromData(stock, pricing, customerProfile); } ``` **Benefits:** * Total latency = max(individual latencies) instead of sum * Each downstream call gets its own OTel client span * Circuit breakers operate independently per client **Considerations:** * If one call fails, `Promise.all` rejects immediately -- use `Promise.allSettled` if partial results are acceptable * Each parallel call consumes a connection from the HTTP/2 connection pool ### Partial Failure Handling ```typescript const results = await Promise.allSettled([ inventoryClient.checkStock({ sku: req.sku }), pricingClient.getPrice({ sku: req.sku }), recommendationClient.getSuggestions({ sku: req.sku }), ]); const [stockResult, pricingResult, suggestionsResult] = results; // Stock and pricing are required if (stockResult.status === 'rejected' || pricingResult.status === 'rejected') { throw new ConnectError('Required service unavailable', Code.Unavailable); } // Suggestions are optional -- degrade gracefully const suggestions = suggestionsResult.status === 'fulfilled' ? suggestionsResult.value.items : []; ``` ## Server Streaming For real-time data feeds -- the server sends a stream of messages in response to a single request: ```typescript // Client consuming a server stream for await (const update of orderClient.watchOrderStatus({ orderId: '123' })) { console.log(`Order status: ${update.status}`); } ``` Streaming RPCs are fully instrumented by `createOtelClientInterceptor()`. The span covers the entire stream lifecycle -- from the initial request to stream completion. Individual messages are recorded as span events when `recordMessages` is enabled. See [Client Interceptors -- Streaming Instrumentation](./client-interceptors#streaming-instrumentation) for details. ## Error Handling gRPC errors propagate as `ConnectError` with standard status codes. Handle them explicitly when calling downstream services: ```typescript import { ConnectError, Code } from '@connectrpc/connect'; try { const stock = await inventoryClient.checkStock({ sku: req.sku }); } catch (err) { if (err instanceof ConnectError) { switch (err.code) { case Code.NotFound: throw new ConnectError('SKU not found', Code.InvalidArgument); case Code.Unavailable: // Circuit breaker may have opened, or service is down throw new ConnectError('Inventory service unavailable', Code.Unavailable); case Code.DeadlineExceeded: throw new ConnectError('Inventory check timed out', Code.DeadlineExceeded); default: throw new ConnectError('Inventory check failed', Code.Internal); } } throw err; } ``` ### Error Translation When forwarding errors from a downstream service, translate status codes to match your service's API contract. Don't leak internal error details to callers: | Downstream Error | Recommended Translation | |-----------------|------------------------| | `NotFound` | `InvalidArgument` or `NotFound` (depending on context) | | `Unavailable` | `Unavailable` (propagate) | | `DeadlineExceeded` | `DeadlineExceeded` (propagate) | | `Internal` | `Internal` (log details, return generic message) | | `PermissionDenied` | `Internal` (don't expose auth details) | ## Related * [Service Communication](/en/guide/service-communication) -- overview, transport configuration, service discovery * [Client Interceptors](./client-interceptors) -- OTel, resilience, circuit breaker configuration * [Architecture Patterns](/en/guide/production/architecture) -- full production architecture reference * [Interceptors](/en/guide/interceptors) -- server-side interceptor chain --- --- url: /en/api/@connectum/core/config.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / config # config Configuration module Provides type-safe environment configuration validation using Zod schemas. Follows 12-Factor App principles. ## Example ```typescript import { parseEnvConfig, type ConnectumEnv } from '@connectum/core/config'; // Parse environment with defaults const config = parseEnvConfig(); // Use validated config console.log(`Starting server on port ${config.PORT}`); console.log(`Log level: ${config.LOG_LEVEL}`); console.log(`HTTP health enabled: ${config.HTTP_HEALTH_ENABLED}`); ``` ## References ### BooleanFromStringSchema Re-exports [BooleanFromStringSchema](../variables/BooleanFromStringSchema.md) *** ### ConnectumEnv Re-exports [ConnectumEnv](../type-aliases/ConnectumEnv.md) *** ### ConnectumEnvSchema Re-exports [ConnectumEnvSchema](../variables/ConnectumEnvSchema.md) *** ### LogFormatSchema Re-exports [LogFormatSchema](../variables/LogFormatSchema.md) *** ### LoggerBackendSchema Re-exports [LoggerBackendSchema](../variables/LoggerBackendSchema.md) *** ### LogLevelSchema Re-exports [LogLevelSchema](../variables/LogLevelSchema.md) *** ### NodeEnvSchema Re-exports [NodeEnvSchema](../variables/NodeEnvSchema.md) *** ### parseEnvConfig Re-exports [parseEnvConfig](../functions/parseEnvConfig.md) *** ### safeParseEnvConfig Re-exports [safeParseEnvConfig](../functions/safeParseEnvConfig.md) --- --- url: /en.md --- ## See It in Action ::: code-group ```protobuf [deployment.proto] syntax = "proto3"; package platform.v1; import "buf/validate/validate.proto"; import "google/api/annotations.proto"; import "connectum/auth/v1/options.proto"; service DeploymentService { option (connectum.auth.v1.service_auth) = { default_policy: "deny" default_requires: { roles: ["platform-engineer"] } }; rpc CreateDeployment(CreateDeploymentRequest) returns (Deployment) { option (google.api.http) = { post: "/v1/deployments" body: "*" }; } rpc GetDeployment(GetDeploymentRequest) returns (Deployment) { option (connectum.auth.v1.method_auth) = { public: true }; option (google.api.http) = { get: "/v1/deployments/{deployment_id}" }; } } message CreateDeploymentRequest { string namespace = 1 [(buf.validate.field).string.min_len = 1]; string image = 2 [(buf.validate.field).string.pattern = "^[a-z0-9./:-]+$"]; int32 replicas = 3 [(buf.validate.field).int32 = { gte: 1, lte: 100 }]; } message GetDeploymentRequest { string deployment_id = 1 [(buf.validate.field).string.uuid = true]; } message Deployment { string deployment_id = 1; string namespace = 2; string image = 3; int32 replicas = 4; string status = 5; string created_by = 6; } ``` ```typescript [deploymentService.ts] import { create } from '@bufbuild/protobuf'; import type { ConnectRouter } from '@connectrpc/connect'; import { requireAuthContext } from '@connectum/auth'; import { getLogger, getMeter } from '@connectum/otel'; import { DeploymentService, DeploymentSchema } from '#gen/platform_pb.js'; const logger = getLogger('DeploymentService'); const deployments = getMeter().createCounter('platform.deployments.total'); export default (router: ConnectRouter) => { router.service(DeploymentService, { async createDeployment(req) { const auth = requireAuthContext(); deployments.add(1, { namespace: req.namespace }); logger.info('Deployment created', { namespace: req.namespace, image: req.image, createdBy: auth.subject, }); return create(DeploymentSchema, { deploymentId: crypto.randomUUID(), namespace: req.namespace, image: req.image, replicas: req.replicas, status: 'PENDING', createdBy: auth.subject, }); }, }); }; ``` ```typescript [server.ts] import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { createOtelInterceptor } from '@connectum/otel'; import { createJwtAuthInterceptor, createProtoAuthzInterceptor } from '@connectum/auth'; import routes from '#services/deploymentService.js'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: [ createOtelInterceptor({ serverPort: 5000 }), createJwtAuthInterceptor({ jwksUri: process.env.JWKS_URI! }), createProtoAuthzInterceptor(), ...createDefaultInterceptors(), ], shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ```bash [cli] # Sync proto types from a running server (gRPC Server Reflection) connectum proto sync --from localhost:5000 --out ./gen # gRPC call (requires platform-engineer role) grpcurl -d '{"namespace":"prod","image":"api:v2.1.0","replicas":3}' \ -H "Authorization: Bearer $TOKEN" \ localhost:5000 platform.v1.DeploymentService/CreateDeployment # REST via Envoy Gateway (gRPC-JSON transcoding from google.api.http) curl -X POST http://gateway:8080/v1/deployments \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"namespace":"prod","image":"api:v2.1.0","replicas":3}' ``` ::: --- --- url: /en/api.md --- # Connectum API Reference ## Packages * [@connectum/auth](@connectum/auth/index.md) * [@connectum/cli](@connectum/cli/index.md) * [@connectum/core](@connectum/core/index.md) * [@connectum/healthcheck](@connectum/healthcheck/index.md) * [@connectum/interceptors](@connectum/interceptors/index.md) * [@connectum/otel](@connectum/otel/index.md) * [@connectum/reflection](@connectum/reflection/index.md) --- --- url: /en/guide/about.md --- # Connectum Framework ## What is Connectum? **Connectum** is a production-ready framework for building gRPC/ConnectRPC microservices on Node.js 18+. It eliminates boilerplate by bundling health checks, observability, resilience, authentication, and graceful shutdown into a single `createServer()` call. Engineering teams with enterprise requirements -- observability, mTLS, RBAC, circuit breakers -- need a unified, well-designed framework instead of gluing together dozens of libraries. Connectum provides that foundation with a pluggable architecture where every capability is an explicit, optional package. ## The Problem Building production-ready gRPC/ConnectRPC microservices on Node.js requires: * A lot of boilerplate code for every new service * Manual observability setup (tracing, metrics, logging) * Integration of health checks and graceful shutdown * TLS, validation, and reflection configuration * Understanding multiple libraries and their interactions Existing solutions (NestJS, tRPC) are either too heavy or lack native gRPC support. ## What Connectum Provides | Feature | Description | |---------|-------------| | gRPC/ConnectRPC Server | HTTP/2 server with gRPC, ConnectRPC, and gRPC-Web support | | TLS Support | Built-in TLS, mTLS, auto-reload certificates | | Health Checks | gRPC Health protocol + HTTP `/healthz`, `/readyz` endpoints | | Graceful Shutdown | Drain connections, configurable timeout, ordered shutdown hooks | | Interceptors Chain | Resilience interceptors with fixed execution order | | OpenTelemetry | Distributed tracing, RPC metrics, structured logging | | Server Reflection | gRPC Server Reflection for grpcurl and similar tools | | Auth & Authz | JWT, gateway, session auth; declarative RBAC; proto-based authorization | | Input Validation | protovalidate integration for automatic request validation | | CLI Tools | Code generation and project scaffolding | ## Core Principles These principles guide every design decision in Connectum. Each links to its Architecture Decision Record for full rationale. 1. **Native TypeScript** -- write TypeScript natively on Node.js 25+; packages compile to JS + type declarations for consumers on Node.js 18+. [ADR-001](/en/contributing/adr/001-native-typescript-migration) 2. **Modular Architecture** -- eight packages organized in dependency layers where each layer can only depend on lower layers. [ADR-003](/en/contributing/adr/003-package-decomposition) 3. **Pluggable Protocols** -- health checks and server reflection are separate packages registered via the `protocols` array; custom protocols implement the same interface. [ADR-022](/en/contributing/adr/022-protocol-extraction) 4. **Resilience by Default** -- a fixed-order interceptor chain (errorHandler, timeout, bulkhead, circuitBreaker, retry, fallback, validation) with per-interceptor configuration. [ADR-006](/en/contributing/adr/006-resilience-pattern-implementation) 5. **Proto-First Validation** -- request validation uses protovalidate constraints defined directly in `.proto` files and enforced automatically by the validation interceptor. [ADR-005](/en/contributing/adr/005-input-validation-strategy) 6. **Explicit Lifecycle** -- `createServer()` is the single entry point with no hidden defaults; interceptors and protocols are explicit parameters. [ADR-023](/en/contributing/adr/023-uniform-registration-api) 7. **Observable by Design** -- OpenTelemetry instrumentation is a first-class package providing distributed tracing, RPC metrics, and structured logging out of the box. 8. **Production-Ready** -- TLS/mTLS, graceful shutdown with dependency-ordered hooks, JWT/RBAC auth, and Kubernetes-compatible health probes are built-in capabilities. ## Architecture Overview ```mermaid graph BT subgraph L0["Layer 0 — Foundation"] CORE["@connectum/core"] end subgraph L1["Layer 1 — Extensions"] AUTH["@connectum/auth"] HC["@connectum/healthcheck"] REF["@connectum/reflection"] INT["@connectum/interceptors"] end subgraph L2["Layer 2 — Tools"] CLI["@connectum/cli"] OTEL["@connectum/otel"] TEST["@connectum/testing"] end AUTH --> CORE HC --> CORE REF --> CORE INT --> CORE ``` **Layer 0 (Foundation):** `@connectum/core` -- server factory with lifecycle control, zero internal dependencies. **Layer 1 (Extensions):** Authentication, interceptors, health checks, reflection -- depends on Layer 0 or external packages only. **Layer 2 (Tools):** OpenTelemetry, CLI, testing utilities -- may depend on all lower layers. ## Non-Goals * **Not an ORM** -- Connectum does not manage databases * **Not a full-stack framework** -- gRPC/ConnectRPC server-side only * **Not CommonJS** -- ESM only * **Not legacy Node.js** -- requires Node.js 18+ (development requires Node.js 25+) ## Next Steps * [Quickstart](/en/guide/quickstart) -- create your first service * [Server](/en/guide/server) -- understand server lifecycle * [Interceptors](/en/guide/interceptors) -- the interceptor chain * [Architecture Overview](#architecture-overview) -- package layers and dependency rules ## External Resources * [ConnectRPC Documentation](https://connectrpc.com/docs) * [OpenTelemetry Node.js](https://opentelemetry.io/docs/instrumentation/js/) * [gRPC Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) * [protovalidate](https://github.com/bufbuild/protovalidate) --- --- url: /en/contributing.md description: >- How to contribute to the Connectum framework -- guidelines, setup, and conventions. --- # Contributing to Connectum Thank you for your interest in contributing to Connectum! This guide will help you get started. ## Where to Start 1. **[Development Setup](/en/contributing/development-setup)** -- clone repos, install dependencies, run tests 2. **[CLI Commands](/en/contributing/cli-commands)** -- all commands for working with the monorepo 3. **[About Connectum](/en/guide/about)** -- understand the package layers and design ## Repository Structure Connectum is organized as 3 independent repositories under the [Connectum-Framework](https://github.com/Connectum-Framework) GitHub organization: | Repository | Description | |-----------|-------------| | [connectum](https://github.com/Connectum-Framework/connectum) | Framework code -- pnpm workspace monorepo | | [docs](https://github.com/Connectum-Framework/docs) | Documentation site (VitePress) | | [examples](https://github.com/Connectum-Framework/examples) | Usage examples | ## Guidelines ### Code Style * **Biome** for linting and formatting (`pnpm lint` / `pnpm format`) * **Native TypeScript** -- no `enum`, explicit `import type`, `.ts` extensions * **Named parameters** -- prefer options objects over positional arguments * Node.js 25+ required for development (consumers: Node.js 18+) ### Commits * Use [Conventional Commits](https://www.conventionalcommits.org/) format * One logical change per commit * Run `pnpm typecheck && pnpm test` before committing ### Architecture Decision Records Significant design decisions are documented as ADRs in the [ADR index](/en/contributing/adr/index). When proposing a change that affects the architecture, create a new ADR. ## Quick Commands ```bash cd connectum pnpm install # Install dependencies pnpm typecheck # Type check all packages pnpm test # Run all tests pnpm lint # Check code style pnpm format # Auto-fix formatting pnpm changeset # Create a changeset for versioning ``` --- --- url: /en/guide/interceptors/custom.md --- # Creating Custom Interceptors Connectum's interceptor system is built on top of the standard ConnectRPC `Interceptor` interface. You can create custom interceptors to add authentication, rate limiting, caching, or any cross-cutting concern to your services. ## The Interceptor Interface A ConnectRPC interceptor is a function that receives a `next` handler and returns a new handler. The returned handler receives the request, can modify it, call `next`, and modify the response: ```typescript import type { Interceptor } from '@connectrpc/connect'; const myInterceptor: Interceptor = (next) => async (req) => { // Before the request reaches the service handler console.log(`Incoming: ${req.service.typeName}/${req.method.name}`); const response = await next(req); // After the service handler returns console.log('Response received'); return response; }; ``` ## Factory Pattern Connectum follows the factory pattern for all interceptors -- a function that accepts an options object and returns an `Interceptor`. This is the recommended approach for reusable interceptors: ::: tip The **[@connectum/auth](/en/packages/auth)** package provides production-ready authentication and authorization interceptors. Use it instead of building custom auth interceptors from scratch. ::: ```typescript import type { Interceptor } from '@connectrpc/connect'; interface AuthInterceptorOptions { /** Header name to read the token from */ headerName?: string; /** Function to validate the token */ validateToken: (token: string) => Promise; } function createAuthInterceptor(options: AuthInterceptorOptions): Interceptor { const { headerName = 'authorization', validateToken } = options; return (next) => async (req) => { const token = req.header.get(headerName); if (!token) { throw new ConnectError('Missing authentication token', Code.Unauthenticated); } const isValid = await validateToken(token); if (!isValid) { throw new ConnectError('Invalid authentication token', Code.Unauthenticated); } return next(req); }; } ``` ::: tip The `InterceptorFactory` type from `@connectum/interceptors` can enforce this pattern: ```typescript import type { InterceptorFactory } from '@connectum/interceptors'; const createAuthInterceptor: InterceptorFactory = (options) => { // ...returns Interceptor }; ``` ::: ## Accessing Request Metadata Inside an interceptor you have access to the full request context: ```typescript const inspector: Interceptor = (next) => async (req) => { // Service and method information const serviceName = req.service.typeName; // e.g. "user.v1.UserService" const methodName = req.method.name; // e.g. "GetUser" const methodKind = req.method.kind; // "unary", "server_streaming", etc. // Request headers const contentType = req.header.get('content-type'); const customHeader = req.header.get('x-request-id'); // Request message (unary only) if (req.stream === false) { console.log('Request payload:', req.message); } const response = await next(req); // Response headers and trailers response.header.set('x-served-by', 'connectum'); return response; }; ``` ## Error Handling Within Interceptors Always use `ConnectError` from `@connectrpc/connect` to return proper gRPC status codes: ```typescript import { Code, ConnectError } from '@connectrpc/connect'; import type { Interceptor } from '@connectrpc/connect'; function createRateLimitInterceptor(options: { maxRequests: number; windowMs: number; }): Interceptor { const { maxRequests, windowMs } = options; const counters = new Map(); return (next) => async (req) => { const key = req.service.typeName; const now = Date.now(); let entry = counters.get(key); if (!entry || now >= entry.resetAt) { entry = { count: 0, resetAt: now + windowMs }; counters.set(key, entry); } entry.count++; if (entry.count > maxRequests) { throw new ConnectError( `Rate limit exceeded: ${maxRequests} requests per ${windowMs}ms`, Code.ResourceExhausted, ); } return next(req); }; } ``` ::: warning When the built-in `errorHandler` interceptor is active (enabled by default), it will catch any uncaught errors from your interceptors and normalize them to `ConnectError`. If you throw a `ConnectError`, its code is preserved. Non-`ConnectError` exceptions are mapped to `Code.Internal`. ::: ## Composing with Built-in Interceptors Use `createDefaultInterceptors()` to build the default chain, then append your custom interceptors: ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; const server = createServer({ services: [routes], port: 5000, interceptors: [ ...createDefaultInterceptors({ timeout: { duration: 10_000 } }), // Custom interceptors appended after the built-in chain createRateLimitInterceptor({ maxRequests: 100, windowMs: 60_000 }), ], }); ``` ::: warning Auth interceptor chain position Auth interceptors must be placed **immediately after** `errorHandler`, before timeout and other resilience interceptors. When using `@connectum/auth`, compose the chain manually: ::: ```typescript // Manual chain with auth (ADR-024 order): // errorHandler -> AUTH -> AUTHZ -> timeout -> ... import { createErrorHandlerInterceptor, createTimeoutInterceptor, createSerializerInterceptor, } from '@connectum/interceptors'; import { createJwtAuthInterceptor, createAuthzInterceptor } from '@connectum/auth'; const server = createServer({ services: [routes], interceptors: [ createErrorHandlerInterceptor({ logErrors: true }), createJwtAuthInterceptor({ jwksUri: '...', issuer: '...' }), createAuthzInterceptor({ defaultPolicy: 'deny', rules: [...] }), createTimeoutInterceptor({ duration: 5_000 }), createSerializerInterceptor(), ], }); ``` To replace the built-in chain entirely, provide only your own interceptors: ```typescript import { createErrorHandlerInterceptor, createTimeoutInterceptor, createSerializerInterceptor, } from '@connectum/interceptors'; const server = createServer({ services: [routes], interceptors: [ createErrorHandlerInterceptor({ logErrors: true }), createTimeoutInterceptor({ duration: 5_000 }), createSerializerInterceptor(), ], }); ``` ## Example: Audit Log Interceptor ```typescript import type { Interceptor } from '@connectrpc/connect'; function createAuditLogInterceptor(options: { logger?: (entry: Record) => void; } = {}): Interceptor { const { logger = (e) => console.log('[audit]', JSON.stringify(e)) } = options; return (next) => async (req) => { const start = performance.now(); try { const response = await next(req); logger({ service: req.service.typeName, method: req.method.name, success: true, durationMs: Math.round(performance.now() - start) }); return response; } catch (error) { logger({ service: req.service.typeName, method: req.method.name, success: false, durationMs: Math.round(performance.now() - start) }); throw error; } }; } ``` ## Testing Custom Interceptors Use `node:test`. Create a mock `next` function and invoke the interceptor directly: ```typescript import { describe, it } from 'node:test'; import assert from 'node:assert'; import { ConnectError, Code } from '@connectrpc/connect'; describe('createAuthInterceptor', () => { const interceptor = createAuthInterceptor({ validateToken: async (token) => token === 'valid-token', }); const mockReq = (headers: Record) => ({ header: new Headers(headers), service: { typeName: 'test.v1.TestService' }, method: { name: 'Test', kind: 'unary' }, stream: false, }); const mockNext = async () => ({ header: new Headers(), trailer: new Headers() }); it('should pass with valid token', async () => { const handler = interceptor(mockNext); const response = await handler(mockReq({ authorization: 'valid-token' })); assert.ok(response); }); it('should reject missing token', async () => { const handler = interceptor(mockNext); await assert.rejects(() => handler(mockReq({})), (err) => { assert.ok(err instanceof ConnectError); assert.strictEqual(err.code, Code.Unauthenticated); return true; }); }); }); ``` ## Related * [Interceptors Overview](/en/guide/interceptors) -- quick start and key concepts * [Built-in Interceptors](/en/guide/interceptors/built-in) -- default chain reference * [Method Filtering](/en/guide/interceptors/method-filtering) -- per-method interceptor routing * [Custom Protocols](/en/guide/protocols/custom) -- creating protocol plugins * [@connectum/interceptors](/en/packages/interceptors) -- Package Guide * [@connectum/interceptors API](/en/api/@connectum/interceptors/) -- Full API Reference --- --- url: /en/guide/protocols/custom.md --- # Creating Custom Protocol Plugins Connectum uses a protocol plugin system to extend the server with additional gRPC services and HTTP endpoints. Built-in protocols include `Healthcheck` and `Reflection`, but you can create your own. ## The ProtocolRegistration Interface Every protocol plugin implements the `ProtocolRegistration` interface exported from `@connectum/core`: ```typescript interface ProtocolRegistration { /** Protocol name for identification (e.g. "healthcheck", "reflection") */ readonly name: string; /** Register protocol services on the router */ register(router: ConnectRouter, context: ProtocolContext): void; /** Optional HTTP handler for fallback routing (e.g. /healthz endpoint) */ httpHandler?: HttpHandler; } ``` The `ProtocolContext` provides access to registered service file descriptors: ```typescript interface ProtocolContext { /** Registered service file descriptors */ readonly registry: ReadonlyArray; } ``` The optional `HttpHandler` is called for raw HTTP requests that do not match any ConnectRPC route: ```typescript /** * @returns true if the request was handled, false otherwise */ type HttpHandler = (req: Http2ServerRequest, res: Http2ServerResponse) => boolean; ``` ## How Protocols Are Registered Protocols are passed to `createServer()` via the `protocols` array. During `server.start()`, each protocol's `register()` method is called with the ConnectRouter and a context containing all registered service file descriptors: ```typescript import { createServer } from '@connectum/core'; import { Healthcheck } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [routes], protocols: [ Healthcheck({ httpEnabled: true }), Reflection(), myCustomProtocol, // Your custom protocol ], }); ``` Protocols can also be added before starting the server: ```typescript const server = createServer({ services: [routes] }); server.addProtocol(myCustomProtocol); await server.start(); ``` ::: warning Protocols must be added before calling `server.start()`. Adding a protocol after the server is running will throw an error. ::: ## Creating a Custom Protocol ### Minimal Example: Service Info Endpoint A protocol that registers a gRPC service to return metadata about the running server: ```typescript import type { ConnectRouter } from '@connectrpc/connect'; import type { ProtocolRegistration, ProtocolContext } from '@connectum/core'; import { InfoService } from '#gen/info_pb.js'; function ServerInfo(): ProtocolRegistration { const startedAt = new Date().toISOString(); return { name: 'server-info', register(router: ConnectRouter, context: ProtocolContext): void { const serviceNames = context.registry.flatMap( (file) => file.services.map((s) => s.typeName), ); router.service(InfoService, { getInfo: () => ({ startedAt, serviceCount: serviceNames.length, services: serviceNames, }), }); }, }; } ``` ### With HTTP Handler Add a raw HTTP endpoint alongside the gRPC service. The `httpHandler` function receives HTTP/2 requests that do not match any ConnectRPC route. Return `true` if you handled the request, `false` to pass it along: ```typescript import type { Http2ServerRequest, Http2ServerResponse } from 'node:http2'; import type { ProtocolRegistration, ProtocolContext } from '@connectum/core'; function CustomHealthEndpoint(): ProtocolRegistration { const protocol: ProtocolRegistration = { name: 'custom-health', register(_router, _context): void { // No gRPC service needed -- HTTP-only protocol }, httpHandler(req: Http2ServerRequest, res: Http2ServerResponse): boolean { if (req.url === '/healthz' && req.method === 'GET') { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() })); return true; } return false; }, }; return protocol; } ``` ::: tip The built-in `Healthcheck` protocol already provides HTTP health endpoints at `/healthz`, `/health`, and `/readyz` when `httpEnabled: true` is set. Use a custom HTTP handler only when you need non-standard behavior. ::: ## Example: Prometheus Metrics Endpoint A protocol that exposes a `/metrics` HTTP endpoint for Prometheus scraping: ```typescript import type { Http2ServerRequest, Http2ServerResponse } from 'node:http2'; import type { ProtocolRegistration } from '@connectum/core'; function Metrics(options: { path?: string; collect: () => string; }): ProtocolRegistration { const { path = '/metrics', collect } = options; return { name: 'prometheus-metrics', register(): void { // HTTP-only protocol, no gRPC service registration needed }, httpHandler(req: Http2ServerRequest, res: Http2ServerResponse): boolean { if (req.url === path && req.method === 'GET') { res.writeHead(200, { 'content-type': 'text/plain; version=0.0.4; charset=utf-8' }); res.end(collect()); return true; } return false; }, }; } ``` Usage: ```typescript import { createServer } from '@connectum/core'; import { Healthcheck } from '@connectum/healthcheck'; const server = createServer({ services: [routes], protocols: [ Healthcheck({ httpEnabled: true }), Metrics({ path: '/metrics', collect: () => generatePrometheusMetrics(), }), ], }); ``` ## Using ProtocolContext The `context.registry` field contains an array of `DescFile` objects (from `@bufbuild/protobuf`) representing the proto file descriptors of all registered services. This is how the built-in Reflection protocol discovers available services: ```typescript register(router, context): void { // List all registered service type names for (const file of context.registry) { for (const service of file.services) { console.log(`Registered: ${service.typeName}`); for (const method of service.methods) { console.log(` - ${method.name} (${method.kind})`); } } } } ``` ## Protocol Design Guidelines 1. **Use the factory pattern** -- Return `ProtocolRegistration` from a function that accepts options. This matches the convention of `Healthcheck()` and `Reflection()`. 2. **Name your protocol** -- The `name` field is used for identification and logging. Choose a descriptive, lowercase name. 3. **Keep register() synchronous** -- The `register` method signature is synchronous. If you need async setup, do it before creating the protocol or inside the service handlers. 4. **Return `false` from httpHandler for unmatched routes** -- This allows other protocols and the default 404 handler to process the request. 5. **Use ProtocolContext for service discovery** -- Do not hardcode service names. Use `context.registry` to discover what services are available. ## Related * [Protocols Overview](/en/guide/protocols) -- back to overview * [Server Reflection](/en/guide/protocols/reflection) -- built-in reflection protocol * [Custom Interceptors](/en/guide/interceptors/custom) -- creating custom interceptor middleware * [@connectum/core](/en/packages/core) -- Package Guide * [@connectum/core API](/en/api/@connectum/core/) -- Full API Reference --- --- url: /en/api/@connectum/interceptors/defaults.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / defaults # defaults Default interceptor chain factory Creates the production-ready interceptor chain with resilience patterns. The interceptor order is fixed: errorHandler → timeout → bulkhead → circuitBreaker → retry → fallback → validation → serializer. ## Interfaces * [DefaultInterceptorOptions](interfaces/DefaultInterceptorOptions.md) ## Functions * [createDefaultInterceptors](functions/createDefaultInterceptors.md) --- --- url: /en/contributing/development-setup.md --- # Development Setup How to set up the development environment for contributing to Connectum. ## Requirements * Node.js >= 25.2.0 * pnpm >= 10 ## Quick Start ### 1. Clone Repositories ```bash # Create root directory mkdir Connectum && cd Connectum # Clone all 3 repositories git clone https://github.com/Connectum-Framework/connectum.git git clone https://github.com/Connectum-Framework/docs.git git clone https://github.com/Connectum-Framework/examples.git ``` ### 2. Install Dependencies ```bash cd connectum pnpm install ``` ### 3. Verify Environment ```bash # Check Node.js node --version # >= 25.2.0 # Check pnpm pnpm --version # >= 10 # Type checking pnpm typecheck # Run tests pnpm test ``` ### 4. Start Development ```bash pnpm dev ``` ## Further Resources * [CLI Commands](./cli-commands) -- full command reference * [About Connectum](/en/guide/about) -- framework architecture --- --- url: /en/guide/production/docker.md description: >- Multi-stage Dockerfile, docker-compose, and image optimization for Connectum gRPC/ConnectRPC microservices. --- # Docker Containerization Connectum packages ship **compiled JavaScript** (`.js` + `.d.ts` + source maps), so they work on any Node.js version >= 18.0.0. If your own application code is written in TypeScript, you can either use Node.js 25+ (native type stripping for `.ts` files) or compile your code with a build tool before containerizing. ::: tip Full Example All Docker files described below are available in the [production-ready example](https://github.com/Connectum-Framework/examples/tree/main/production-ready). ::: ## Multi-Stage Dockerfile ### Recommended: `node:25-slim` Two-stage build: install dependencies in an isolated stage, then copy only production `node_modules` into a slim runtime image with a non-root user and health check. See [Dockerfile](https://github.com/Connectum-Framework/examples/blob/main/production-ready/Dockerfile) for the full listing. Key highlights: * **Stage 1 (deps)** -- `pnpm install --frozen-lockfile --prod` for reproducible, minimal dependencies * **Stage 2 (runtime)** -- non-root `connectum` user, `curl` for HEALTHCHECK, native TypeScript via `node src/index.ts` * Environment defaults: `NODE_ENV=production`, `PORT=5000`, `LOG_FORMAT=json`, health and graceful shutdown enabled ::: tip Base image selection If your own application code is compiled to JavaScript (e.g., via tsup or tsx), you can use any Node.js 18+ base image instead of `node:25-slim`. Use `node:25-slim` only when you want to run your own `.ts` files natively via Node.js type stripping. ::: ### Alternative Runtimes: Bun and tsx You can replace the Node.js `CMD` with Bun or tsx: ```dockerfile # Node.js 25+ (native TypeScript for your own .ts files) CMD ["node", "src/index.ts"] # Bun CMD ["bun", "src/index.ts"] # tsx (works on Node.js 18+) CMD ["npx", "tsx", "src/index.ts"] ``` When using **tsx**, you can use any Node.js 18+ base image (e.g., `node:18-slim`, `node:20-slim`, `node:22-slim`) and add `tsx` as a dependency. Since `@connectum/*` packages ship compiled JavaScript, no special loader is needed for any runtime. ### Alpine Variant (Smaller Image) If you need a smaller image and do not depend on native modules requiring glibc, use the Alpine variant. See [Dockerfile.alpine](https://github.com/Connectum-Framework/examples/blob/main/production-ready/Dockerfile.alpine) for the full listing. ### Image Size Comparison | Base Image | Approximate Size | Use Case | |---|---|---| | `node:25-slim` | ~200 MB | General production (recommended) | | `node:25-alpine` | ~140 MB | Size-optimized, no native glibc modules | | `node:25` | ~1 GB | Development only, avoid in production | ## .dockerignore Keep images clean by excluding dependencies, tests, IDE files, dev configs, and proto sources. See [.dockerignore](https://github.com/Connectum-Framework/examples/blob/main/production-ready/.dockerignore) for the full listing. ## Docker Compose for Local Development A multi-service development environment with Connectum services, OpenTelemetry Collector, Jaeger, Prometheus, and Grafana. See [docker-compose.yml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/docker-compose.yml) for the full listing. **Services included:** | Service | Port | Description | |---|---|---| | `order-service` | 5000 | Connectum gRPC service | | `inventory-service` | 5001 | Connectum gRPC service | | `otel-collector` | 4317, 4318, 8889 | OpenTelemetry Collector (OTLP gRPC/HTTP, Prometheus) | | `jaeger` | 16686 | Distributed tracing UI | | `prometheus` | 9090 | Metrics collection | | `grafana` | 3000 | Dashboards and visualization | ### OTel Collector Configuration See [otel-collector-config.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/otel-collector-config.yaml) for the full listing. The collector receives OTLP traces and metrics, batches them, and exports traces to Jaeger and metrics to Prometheus. ## Image Optimization Tips ### 1. Layer Caching Always copy `package.json` and `pnpm-lock.yaml` before source code. Docker caches the `pnpm install` layer and only re-runs it when dependencies change. ### 2. Production Dependencies Only Use `pnpm install --frozen-lockfile --prod` to exclude devDependencies. This can reduce `node_modules` size by 50-70%. ### 3. Prune Unnecessary Files After install, remove package manager caches: ```dockerfile RUN pnpm install --frozen-lockfile --prod \ && pnpm store prune ``` ### 4. Use `.dockerignore` Aggressively Every file not needed at runtime should be in `.dockerignore`. This speeds up the build context transfer and reduces image size. ### 5. Pin Base Image Digests in Production For reproducible builds, pin to a specific image digest: ```dockerfile FROM node:25-slim@sha256: AS runtime ``` ::: warning Never use the `latest` tag in production Dockerfiles. Always pin to a specific Node.js version (e.g., `node:25.2.0-slim`) to avoid unexpected breaking changes. ::: ## Environment Variables Reference These environment variables configure a Connectum service inside a container: | Variable | Description | Default | |---|---|---| | `NODE_ENV` | Runtime environment | `development` | | `PORT` | Server listen port | `5000` | | `LISTEN` | Bind address | `0.0.0.0` | | `LOG_LEVEL` | Log verbosity (`debug`, `info`, `warn`, `error`) | `info` | | `LOG_FORMAT` | Log output format (`json`, `pretty`) | `json` | | `LOG_BACKEND` | Logger backend (`otel`, `pino`, `console`) | `otel` | | `HTTP_HEALTH_ENABLED` | Enable HTTP health endpoints | `false` | | `GRACEFUL_SHUTDOWN_ENABLED` | Enable graceful shutdown on SIGTERM/SIGINT | `true` | | `GRACEFUL_SHUTDOWN_TIMEOUT_MS` | Shutdown timeout in ms | `30000` | | `OTEL_SERVICE_NAME` | OpenTelemetry service name | `connectum-service` | | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint | -- | | `OTEL_TRACES_EXPORTER` | Trace exporter (`console`, `otlp/http`, `otlp/grpc`, `none`) | -- | | `OTEL_METRICS_EXPORTER` | Metrics exporter | -- | | `OTEL_LOGS_EXPORTER` | Logs exporter | -- | ## What's Next * [Kubernetes Deployment](./kubernetes.md) -- Deploy your containerized service to Kubernetes * [Envoy Gateway](./envoy-gateway.md) -- Expose gRPC services to REST clients * [Service Mesh with Istio](./service-mesh.md) -- Automatic mTLS and traffic management --- --- url: /en/guide/server/configuration.md --- # Environment Configuration Connectum provides type-safe environment configuration using [Zod](https://zod.dev/) schemas, following the [12-Factor App](https://12factor.net/config) methodology. All configuration is read from environment variables with sensible defaults. ## ConnectumEnvSchema The `ConnectumEnvSchema` exported from `@connectum/core` defines all recognized environment variables: ```typescript import { parseEnvConfig } from '@connectum/core'; const config = parseEnvConfig(); console.log(config.PORT); // 5000 (default) console.log(config.LISTEN); // "0.0.0.0" (default) console.log(config.LOG_LEVEL); // "info" (default) console.log(config.NODE_ENV); // "development" (default) ``` ## Environment Variables Reference ### Server | Variable | Type | Default | Description | |----------|------|---------|-------------| | `PORT` | `number` | `5000` | Server port (1--65535) | | `LISTEN` | `string` | `"0.0.0.0"` | Listen address | | `NODE_ENV` | `enum` | `"development"` | `"development"`, `"production"`, or `"test"` | ### Logging | Variable | Type | Default | Description | |----------|------|---------|-------------| | `LOG_LEVEL` | `enum` | `"info"` | `"debug"`, `"info"`, `"warn"`, or `"error"` | | `LOG_FORMAT` | `enum` | `"json"` | `"json"` (structured) or `"pretty"` (human-readable) | | `LOG_BACKEND` | `enum` | `"otel"` | `"otel"`, `"pino"`, or `"console"` | ### Health Check | Variable | Type | Default | Description | |----------|------|---------|-------------| | `HTTP_HEALTH_ENABLED` | `boolean` | `false` | Enable HTTP health endpoints (`/healthz`, `/health`, `/readyz`) | | `HTTP_HEALTH_PATH` | `string` | `"/healthz"` | Primary HTTP health endpoint path | ### OpenTelemetry | Variable | Type | Default | Description | |----------|------|---------|-------------| | `OTEL_SERVICE_NAME` | `string` | -- | Service name reported to the collector | | `OTEL_EXPORTER_OTLP_ENDPOINT` | `string (url)` | -- | OTLP collector endpoint (e.g. `http://localhost:4318`) | ### Graceful Shutdown | Variable | Type | Default | Description | |----------|------|---------|-------------| | `GRACEFUL_SHUTDOWN_ENABLED` | `boolean` | `true` | Enable automatic graceful shutdown on SIGTERM/SIGINT | | `GRACEFUL_SHUTDOWN_TIMEOUT_MS` | `number` | `30000` | Maximum time (ms) to wait for in-flight requests (0--300000) | ::: tip Boolean environment variables accept `"true"`, `"false"`, `"1"`, `"0"`, `"yes"`, and `"no"`. ::: ## Parsing and Validation ### parseEnvConfig() Parses `process.env` (or a custom object) and throws a `ZodError` if validation fails: ```typescript import { parseEnvConfig } from '@connectum/core'; try { const config = parseEnvConfig(); console.log(`Starting on port ${config.PORT}`); } catch (err) { console.error('Invalid configuration:', err.message); process.exit(1); } ``` ### safeParseEnvConfig() Returns a Zod result object instead of throwing: ```typescript import { safeParseEnvConfig } from '@connectum/core'; const result = safeParseEnvConfig(); if (result.success) { console.log(`Port: ${result.data.PORT}`); } else { console.error('Validation errors:'); console.error(result.error.format()); process.exit(1); } ``` ### Custom Environment Source Both functions accept an optional env object, useful for testing: ```typescript const config = parseEnvConfig({ PORT: '8080', NODE_ENV: 'production', LOG_LEVEL: 'warn', }); ``` ## Using Config with createServer() The env schema defines defaults that align with `CreateServerOptions`. You can wire them together: ```typescript import { createServer, parseEnvConfig } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import routes from '#gen/routes.js'; const env = parseEnvConfig(); const server = createServer({ services: [routes], port: env.PORT, host: env.LISTEN, protocols: [ Healthcheck({ httpEnabled: env.HTTP_HEALTH_ENABLED }), Reflection(), ], shutdown: { autoShutdown: env.GRACEFUL_SHUTDOWN_ENABLED, timeout: env.GRACEFUL_SHUTDOWN_TIMEOUT_MS, }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ## TLS Configuration TLS is configured via the `tls` option in `createServer()`. Certificates can be loaded from explicit paths or a directory: ```typescript import { createServer, readTLSCertificates } from '@connectum/core'; // Option A: Explicit paths const server = createServer({ services: [routes], tls: { keyPath: '/etc/ssl/server.key', certPath: '/etc/ssl/server.crt', }, }); // Option B: Directory (looks for server.key and server.crt) const server = createServer({ services: [routes], tls: { dirPath: '/etc/ssl/certs', }, }); ``` The `TLS_DIR_PATH` environment variable sets the default directory for `getTLSPath()`: | Variable | Default (dev) | Default (prod) | |----------|--------------|----------------| | `TLS_DIR_PATH` | `../../keys` (relative to cwd) | cwd | ::: warning In production, always set `TLS_DIR_PATH` explicitly or provide `keyPath`/`certPath` in the `tls` option. ::: ## Extending the Schema You can compose `ConnectumEnvSchema` with your own application-specific variables using Zod's `.merge()` or `.extend()`: ```typescript import { z } from 'zod'; import { ConnectumEnvSchema } from '@connectum/core'; const AppEnvSchema = ConnectumEnvSchema.extend({ DATABASE_URL: z.string().url(), REDIS_URL: z.string().url().optional(), API_KEY: z.string().min(32), }); type AppEnv = z.infer; const config: AppEnv = AppEnvSchema.parse(process.env); ``` ## Configuration by Environment ### Development `.env` ```bash PORT=5000 NODE_ENV=development LOG_LEVEL=debug LOG_FORMAT=pretty LOG_BACKEND=console HTTP_HEALTH_ENABLED=true GRACEFUL_SHUTDOWN_ENABLED=false ``` ### Production `.env` ```bash PORT=5000 NODE_ENV=production LOG_LEVEL=warn LOG_FORMAT=json LOG_BACKEND=otel OTEL_SERVICE_NAME=my-service OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 HTTP_HEALTH_ENABLED=true GRACEFUL_SHUTDOWN_ENABLED=true GRACEFUL_SHUTDOWN_TIMEOUT_MS=30000 TLS_DIR_PATH=/etc/ssl/connectum ``` ## Docker and Kubernetes ### Dockerfile ```dockerfile FROM node:25-slim WORKDIR /app COPY . . RUN corepack enable && pnpm install --frozen-lockfile ENV NODE_ENV=production EXPOSE 5000 CMD ["node", "--experimental-strip-types", "src/index.ts"] ``` ### Kubernetes Pod Spec (excerpt) ```yaml containers: - name: my-service image: my-service:latest ports: - containerPort: 5000 env: - { name: NODE_ENV, value: "production" } - { name: LOG_LEVEL, value: "info" } - { name: OTEL_SERVICE_NAME, value: "my-service" } - { name: HTTP_HEALTH_ENABLED, value: "true" } - { name: GRACEFUL_SHUTDOWN_ENABLED, value: "true" } livenessProbe: httpGet: { path: /healthz, port: 5000 } readinessProbe: httpGet: { path: /readyz, port: 5000 } ``` ::: tip Set `GRACEFUL_SHUTDOWN_TIMEOUT_MS` to a value lower than the Kubernetes `terminationGracePeriodSeconds` (default 30s) to ensure the application shuts down before the pod is force-killed. ::: ## Related * [Server Overview](/en/guide/server) -- quick start and key concepts * [Graceful Shutdown](/en/guide/server/graceful-shutdown) -- shutdown hooks and Kubernetes integration * [Custom Interceptors](/en/guide/interceptors/custom) -- creating custom interceptors * [Custom Protocols](/en/guide/protocols/custom) -- creating protocol plugins * [Method Filtering](/en/guide/interceptors/method-filtering) -- per-method interceptor routing * [@connectum/core](/en/packages/core) -- Package Guide * [@connectum/core API](/en/api/@connectum/core/) -- Full API Reference --- --- url: /en/guide/production/envoy-gateway.md description: >- gRPC-JSON transcoding with Envoy Gateway, OpenAPI generation from proto files, and Swagger UI for Connectum services. --- # Envoy Gateway + OpenAPI ::: tip Full Example All Envoy Gateway configs described below are available in the [production-ready/envoy-gateway](https://github.com/Connectum-Framework/examples/tree/main/production-ready/envoy-gateway) directory. ::: Connectum services communicate via gRPC, but many external clients (browsers, mobile apps, third-party integrations) need REST/JSON APIs. Envoy Gateway provides **gRPC-JSON transcoding** -- automatically converting REST requests into gRPC calls and vice versa -- without writing any REST handlers. ## Request Flow ```mermaid graph LR subgraph Clients WEB["Browser / SPA"] MOB["Mobile App"] CLI["REST Client
(curl, Postman)"] end subgraph Gateway["Envoy Gateway"] ROUTE["HTTPRoute"] TRANSCODE["gRPC-JSON
Transcoder Filter"] SWAGGER["Swagger UI
/docs"] end subgraph Services["Connectum Services"] SVC["Order Service
gRPC :5000"] end WEB -->|"POST /v1/orders
Content-Type: application/json"| ROUTE MOB -->|"GET /v1/orders/123"| ROUTE CLI -->|"GET /docs"| SWAGGER ROUTE --> TRANSCODE TRANSCODE -->|"gRPC binary
HTTP/2"| SVC SVC -->|"gRPC response"| TRANSCODE TRANSCODE -->|"JSON response"| ROUTE ``` ## Prerequisites * Kubernetes cluster with [Envoy Gateway](https://gateway.envoyproxy.io/) installed * Proto files with `google.api.http` annotations * `google/api/annotations.proto` and `google/api/http.proto` are available via buf BSR deps ## Step 1: Annotate Proto Files Add HTTP bindings to your proto service methods using `google.api.http`. The proto file defines CRUD operations for OrderService with REST path mappings (e.g. `POST /v1/orders`, `GET /v1/orders/{order_id}`) and buf validation rules. See [orders.proto](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/proto/orders.proto) for the full proto definition. ::: tip The `google/api/http.proto` and `google/api/annotations.proto` files are available through buf BSR deps. Add them to your [buf.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/buf.yaml) dependencies and import them directly in your proto files without vendoring. ::: ## Step 2: Generate OpenAPI Spec Generate an OpenAPI v3 specification from your annotated proto files using `protoc-gen-openapiv3`: ### Install ```bash # Install the protoc plugin go install github.com/google/gnostic/cmd/protoc-gen-openapi@latest # Or via buf plugin # Add to buf.gen.yaml ``` ### buf.gen.yaml Configuration This configures buf to generate both ConnectRPC TypeScript stubs (`protoc-gen-es`) and an OpenAPI v3 spec (`protoc-gen-openapi`) with proto-style naming and string enums. See [buf.gen.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/buf.gen.yaml) for the full configuration. ### Generate ```bash # Generate TypeScript stubs + OpenAPI spec buf generate # Output: openapi/mycompany/orders/v1/orders.openapi.yaml ``` ### Example Generated OpenAPI ```yaml openapi: 3.0.3 info: title: Order Service API version: 1.0.0 paths: /v1/orders: post: operationId: OrderService_CreateOrder requestBody: content: application/json: schema: $ref: '#/components/schemas/CreateOrderRequest' responses: '200': content: application/json: schema: $ref: '#/components/schemas/CreateOrderResponse' get: operationId: OrderService_ListOrders parameters: - name: page_size in: query schema: type: integer - name: page_token in: query schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/ListOrdersResponse' /v1/orders/{order_id}: get: operationId: OrderService_GetOrder parameters: - name: order_id in: path required: true schema: type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/Order' ``` ## Step 3: Generate Proto Descriptor Envoy's gRPC-JSON transcoder requires a compiled proto descriptor set: ```bash buf build -o proto-descriptor.pb # Or with protoc directly: protoc \ --include_imports \ --include_source_info \ --descriptor_set_out=proto-descriptor.pb \ -I proto/ \ proto/mycompany/orders/v1/orders.proto ``` ## Step 4: Kubernetes Gateway API Resources ### GatewayClass and Gateway This manifest defines a GatewayClass and Gateway with three listeners: HTTP on port 80, HTTPS on port 443 (with TLS termination), and a dedicated gRPC listener on port 9090 for native gRPC clients. See [gateway.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/gateway.yaml) for the full manifest. ### GRPCRoute (Native gRPC Traffic) Routes native gRPC traffic directly to the OrderService backend on port 5000, matching on the fully qualified gRPC service name. See [grpc-route.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/grpc-route.yaml) for the full manifest. ### HTTPRoute (REST-to-gRPC Transcoding) Maps REST paths (`/v1/orders` prefix) to the gRPC backend for transcoding, and routes `/docs` to the Swagger UI service. Attached to both HTTP and HTTPS listeners. See [http-route.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/http-route.yaml) for the full manifest. ## Step 5: Envoy Filter for gRPC-JSON Transcoding If your Envoy Gateway version supports EnvoyPatchPolicy, configure the transcoder filter directly. This patch injects the `grpc_json_transcoder` HTTP filter into the listener chain, configuring it with the proto descriptor, target services, and JSON print options (whitespace, primitive fields, proto field names). See [envoy-patch-policy.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/envoy-patch-policy.yaml) for the full manifest. Alternatively, store the proto descriptor in a ConfigMap. This creates a ConfigMap with the base64-encoded proto descriptor binary that can be mounted into Envoy pods. See [proto-descriptor-configmap.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/proto-descriptor-configmap.yaml) for the full manifest. ## Step 6: Swagger UI Deployment Deploy Swagger UI to serve the generated OpenAPI spec. This manifest includes a ConfigMap for the OpenAPI spec, a Deployment running the official `swaggerapi/swagger-ui` image with the spec mounted at `/specs`, and a ClusterIP Service exposing port 8080. See [swagger-ui.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/swagger-ui.yaml) for the full manifest. ## Rate Limiting Add rate limiting at the gateway level to protect your Connectum services. This BackendTrafficPolicy applies local rate limiting of 100 requests per second to the REST HTTPRoute. See [rate-limit.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/rate-limit.yaml) for the full manifest. ## Load Balancing Configure request-level (L7) load balancing for gRPC backends. This is essential because gRPC uses persistent HTTP/2 connections. The policy applies RoundRobin load balancing to the REST HTTPRoute. See [backend-traffic-policy.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/envoy-gateway/backend-traffic-policy.yaml) for the full manifest. ## Full Example: End-to-End Request ### REST Client Sends Request ```bash # Create an order via REST curl -X POST https://api.example.com/v1/orders \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cust-123", "items": [ {"sku": "ITEM-001", "quantity": 2} ] }' # Get an order via REST curl https://api.example.com/v1/orders/550e8400-e29b-41d4-a716-446655440000 ``` ### What Happens Under the Hood 1. REST request arrives at Envoy Gateway on port 80/443 2. HTTPRoute matches `/v1/orders` prefix 3. Envoy's `grpc_json_transcoder` filter converts JSON to gRPC binary using the proto descriptor 4. Request is forwarded to `order-service:5000` as a native gRPC call 5. Connectum service processes the gRPC request through its interceptor chain 6. gRPC response is converted back to JSON by the transcoder 7. JSON response is returned to the client ### Native gRPC Client (Unchanged) ```typescript import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { OrderService } from '#gen/mycompany/orders/v1/orders_pb.js'; const transport = createGrpcTransport({ baseUrl: 'http://api.example.com:9090', httpVersion: '2', }); const client = createClient(OrderService, transport); const order = await client.getOrder({ orderId: '550e8400-e29b-41d4-a716-446655440000' }); ``` ## CI/CD: Automate Proto Descriptor Generation Add proto descriptor generation to your CI pipeline so Envoy always has an up-to-date descriptor: ```yaml # .github/workflows/proto.yml (excerpt) - name: Generate proto descriptor run: buf build -o proto-descriptor.pb - name: Update ConfigMap run: | kubectl create configmap proto-descriptors \ --from-file=proto-descriptor.pb \ --namespace=connectum \ --dry-run=client -o yaml | kubectl apply -f - - name: Restart Gateway (pick up new descriptor) run: kubectl rollout restart deployment envoy-gateway -n connectum ``` ::: warning When you add new services or change HTTP annotations, you must regenerate the proto descriptor and update the Envoy configuration. Automate this in CI to prevent drift between proto definitions and the gateway configuration. ::: ## What's Next * [Service Mesh with Istio](./service-mesh.md) -- Automatic mTLS and traffic splitting * [Kubernetes Deployment](./kubernetes.md) -- Core deployment manifests * [Architecture Patterns](./architecture.md) -- Service communication patterns --- --- url: /en/guide/typescript/erasable-syntax.md --- # Erasable Syntax Node.js 25+ executes `.ts` files directly by **stripping type annotations** at load time. This is **not** full TypeScript compilation -- it only removes type syntax, leaving the remaining JavaScript intact. Your TypeScript code must be valid JavaScript after type annotations are removed. ## How Native TypeScript Works ```bash # Run TypeScript directly -- no tsc needed node src/index.ts ``` The key implication: your TypeScript code must be valid JavaScript after type annotations are removed. This is called **erasable syntax only**. ## The erasableSyntaxOnly Constraint Enable this in your `tsconfig.json`: ```json { "compilerOptions": { "erasableSyntaxOnly": true } } ``` This makes TypeScript's type checker enforce that you only use syntax that can be erased, catching violations at development time. ## What You Cannot Use The following TypeScript features generate runtime code and **cannot** be erased: ### No `enum` ```typescript // WRONG: enum generates runtime code enum Status { PENDING = 1, ACTIVE = 2, CLOSED = 3, } // CORRECT: use const object with 'as const' const Status = { PENDING: 1, ACTIVE: 2, CLOSED: 3, } as const; type Status = typeof Status[keyof typeof Status]; // Status = 1 | 2 | 3 ``` This pattern gives you: * Type safety (same as enum) * Runtime values (accessible in code) * String literal type via `keyof typeof` * No extra compilation step ### No `namespace` with Runtime Code ```typescript // WRONG: namespace with runtime value namespace MyApp { export const version = '1.0.0'; } // CORRECT: use a module export const version = '1.0.0'; ``` ::: tip Type-only namespaces (containing only types/interfaces) are allowed since they are fully erasable: ```typescript // OK: type-only namespace namespace MyTypes { export interface Config { port: number; } } ``` ::: ### No Parameter Properties ```typescript // WRONG: parameter properties generate assignment code class Server { constructor(private port: number) {} } // CORRECT: explicit property declaration class Server { private port: number; constructor(port: number) { this.port = port; } } ``` ### No Legacy Decorators Legacy (experimental) decorators are not erasable. TC39 stage 3 decorators are supported in newer Node.js versions: ```typescript // WRONG: legacy decorator @Injectable() class UserService {} // OK: TC39 stage 3 decorators (if supported by your Node.js version) ``` ## Import Rules ### Explicit `import type` With `verbatimModuleSyntax: true`, you must separate type imports from value imports: ```typescript // CORRECT: explicit type import import type { ConnectRouter } from '@connectrpc/connect'; import type { SayHelloRequest } from '#gen/greeter_pb.js'; // CORRECT: value import import { create } from '@bufbuild/protobuf'; import { GreeterService } from '#gen/greeter_pb.js'; // CORRECT: mixed import with inline type import { GreeterService, type SayHelloRequest } from '#gen/greeter_pb.js'; // WRONG: type imported as value (caught by verbatimModuleSyntax) import { SayHelloRequest } from '#gen/greeter_pb.js'; // ^ This is a type, must use 'import type' ``` ### File Extensions in Imports Use `.ts` extensions in relative imports of source files. The `rewriteRelativeImportExtensions` option handles module resolution: ```typescript // CORRECT: .ts extension for source files import { greeterServiceRoutes } from './services/greeterService.ts'; import type { Config } from './config.ts'; // CORRECT: no extension for package imports import { createServer } from '@connectum/core'; import { create } from '@bufbuild/protobuf'; // WRONG: .js extension for source files (outdated convention) import { greeterServiceRoutes } from './services/greeterService.js'; // WRONG: no extension for relative imports import { greeterServiceRoutes } from './services/greeterService'; ``` ### Generated Code (`#gen/`) Imports Generated protobuf files (`#gen/*`) always use `.js` extensions. This is the convention set by `protobuf-es` (`import_extension=.js` in `buf.gen.yaml`): ```typescript // CORRECT: .js for generated protobuf files import { GreeterService } from '#gen/greeter_pb.js'; import type { SayHelloRequest } from '#gen/greeter_pb.js'; import routes from '#gen/routes.js'; // WRONG: .ts for generated files import { GreeterService } from '#gen/greeter_pb.ts'; ``` The `#gen/` path alias is defined in `package.json` via the `imports` field (`"#gen/*": "./gen/*"`). ### Node.js Built-in Modules Always use the `node:` prefix for Node.js built-in modules: ```typescript // CORRECT import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { setTimeout } from 'node:timers/promises'; // WRONG: no node: prefix import { readFileSync } from 'fs'; ``` ## tsconfig.json Configuration Here is the recommended `tsconfig.json` for Connectum projects: ```json { "compilerOptions": { // No compilation -- TypeScript runs natively "noEmit": true, // ECMAScript and module targets "target": "esnext", "module": "nodenext", "moduleResolution": "nodenext", // Native TypeScript execution constraints "erasableSyntaxOnly": true, "verbatimModuleSyntax": true, "rewriteRelativeImportExtensions": true, // Strict type checking "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*.ts", "gen/**/*.ts"], "exclude": ["node_modules"] } ``` ### Key Options Explained | Option | Value | Purpose | |--------|-------|---------| | `noEmit` | `true` | No compilation output -- TypeScript is for type checking only | | `erasableSyntaxOnly` | `true` | Enforce erasable-only syntax | | `verbatimModuleSyntax` | `true` | Require explicit `import type` | | `rewriteRelativeImportExtensions` | `true` | Allow `.ts` extensions in imports | | `module` | `nodenext` | Node.js ESM module system | | `moduleResolution` | `nodenext` | Node.js module resolution algorithm | ## Related * [TypeScript Overview](/en/guide/typescript) -- back to overview * [Runtime Support](/en/guide/typescript/runtime-support) -- Node.js, Bun, tsx comparison * [Proto Enums](/en/guide/typescript/proto-enums) -- workaround for proto enum generation * [Patterns & Workflow](/en/guide/typescript/patterns) -- common TypeScript patterns --- --- url: /en/api/@connectum/interceptors/errorHandler.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / errorHandler # errorHandler Error handler interceptor Transforms errors into ConnectError with proper error codes. Recognizes SanitizableError protocol for safe client-facing messages. ## Functions * [createErrorHandlerInterceptor](functions/createErrorHandlerInterceptor.md) --- --- url: /en/api/@connectum/interceptors/fallback.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / fallback # fallback Fallback interceptor Provides graceful degradation when service fails. ## Functions * [createFallbackInterceptor](functions/createFallbackInterceptor.md) --- --- url: /en/api/@connectum/otel/shared/functions/applyAttributeFilter.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [shared](../index.md) / applyAttributeFilter # Function: applyAttributeFilter() > **applyAttributeFilter**(`attrs`, `filter?`): `Attributes` Defined in: [packages/otel/src/shared.ts:181](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L181) Applies an attribute filter to the given attributes. ## Parameters ### attrs `Record`<`string`, `string` | `number`> Base attributes to filter ### filter? [`OtelAttributeFilter`](../../type-aliases/OtelAttributeFilter.md) Optional filter function ## Returns `Attributes` Filtered attributes --- --- url: /en/api/@connectum/otel/shared/functions/buildBaseAttributes.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [shared](../index.md) / buildBaseAttributes # Function: buildBaseAttributes() > **buildBaseAttributes**(`params`): `Record`<`string`, `string` | `number`> Defined in: [packages/otel/src/shared.ts:158](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L158) Builds standard RPC base attributes per OTel semantic conventions. ## Parameters ### params [`BaseAttributeParams`](../interfaces/BaseAttributeParams.md) Service, method, server address/port info ## Returns `Record`<`string`, `string` | `number`> Record of base attributes --- --- url: /en/api/@connectum/otel/shared/functions/buildErrorAttributes.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [shared](../index.md) / buildErrorAttributes # Function: buildErrorAttributes() > **buildErrorAttributes**(`error`): `Record`<`string`, `string` | `number`> Defined in: [packages/otel/src/shared.ts:129](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L129) Builds error-specific attributes for spans and metrics. For ConnectError instances, records the Connect error code name and numeric code. For generic Error instances, records the error constructor name. For unknown error types, records "UNKNOWN". ## Parameters ### error `unknown` The caught error ## Returns `Record`<`string`, `string` | `number`> Record of error attributes to attach to spans/metrics --- --- url: /en/api/@connectum/reflection/functions/collectFileProtos.md --- [Connectum API Reference](../../../index.md) / [@connectum/reflection](../index.md) / collectFileProtos # Function: collectFileProtos() > **collectFileProtos**(`files`): `FileDescriptorProto`\[] Defined in: [utils.ts:19](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/reflection/src/utils.ts#L19) Recursively collect FileDescriptorProto objects from DescFile entries, including transitive dependencies. Dependencies are visited depth-first before the file itself, and duplicates are eliminated by file name. ## Parameters ### files readonly `DescFile`\[] Array of DescFile entries to collect protos from ## Returns `FileDescriptorProto`\[] Deduplicated array of FileDescriptorProto objects --- --- url: /en/api/@connectum/auth/functions/createAuthInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createAuthInterceptor # Function: createAuthInterceptor() > **createAuthInterceptor**(`options`): `Interceptor` Defined in: [packages/auth/src/auth-interceptor.ts:81](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/auth-interceptor.ts#L81) Create a generic authentication interceptor. Extracts credentials from request headers, verifies them using a user-provided callback, and stores the resulting AuthContext in AsyncLocalStorage for downstream access. ## Parameters ### options [`AuthInterceptorOptions`](../interfaces/AuthInterceptorOptions.md) Authentication options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createAuthInterceptor } from '@connectum/auth'; const auth = createAuthInterceptor({ extractCredentials: (req) => req.header.get('x-api-key'), verifyCredentials: async (apiKey) => { const user = await db.findByApiKey(apiKey); if (!user) throw new Error('Invalid API key'); return { subject: user.id, roles: user.roles, scopes: [], claims: {}, type: 'api-key', }; }, }); ``` ```typescript const auth = createAuthInterceptor({ verifyCredentials: async (token) => { const payload = await verifyToken(token); return { subject: payload.sub, roles: payload.roles ?? [], scopes: payload.scope?.split(' ') ?? [], claims: payload, type: 'jwt', }; }, }); ``` --- --- url: /en/api/@connectum/auth/functions/createAuthzInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createAuthzInterceptor # Function: createAuthzInterceptor() > **createAuthzInterceptor**(`options?`): `Interceptor` Defined in: [packages/auth/src/authz-interceptor.ts:85](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/authz-interceptor.ts#L85) Create an authorization interceptor. Evaluates declarative rules and/or a programmatic callback against the AuthContext established by the authentication interceptor. IMPORTANT: This interceptor MUST run AFTER an authentication interceptor in the chain. ## Parameters ### options? [`AuthzInterceptorOptions`](../interfaces/AuthzInterceptorOptions.md) = `{}` Authorization options ## Returns `Interceptor` ConnectRPC interceptor ## Example ```typescript import { createAuthzInterceptor } from '@connectum/auth'; const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'public', methods: ['public.v1.PublicService/*'], effect: 'allow' }, { name: 'admin', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, ], }); ``` --- --- url: >- /en/api/@connectum/interceptors/bulkhead/functions/createBulkheadInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [bulkhead](../index.md) / createBulkheadInterceptor # Function: createBulkheadInterceptor() > **createBulkheadInterceptor**(`options?`): `Interceptor` Defined in: [bulkhead.ts:56](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/bulkhead.ts#L56) Create bulkhead interceptor Limits concurrent requests to prevent resource exhaustion. Requests beyond capacity are queued. Requests beyond queue size are rejected. ## Parameters ### options? [`BulkheadOptions`](../../interfaces/BulkheadOptions.md) = `{}` Bulkhead options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createServer } from '@connectum/core'; import { createBulkheadInterceptor } from '@connectum/interceptors'; import { myRoutes } from './routes.js'; const server = createServer({ services: [myRoutes], interceptors: [ createBulkheadInterceptor({ capacity: 10, // Max 10 concurrent requests queueSize: 10, // Queue up to 10 pending requests skipStreaming: true, }), ], }); await server.start(); ``` ```typescript import { createConnectTransport } from '@connectrpc/connect-node'; import { createBulkheadInterceptor } from '@connectum/interceptors'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [ createBulkheadInterceptor({ capacity: 5, queueSize: 5 }), ], }); ``` --- --- url: >- /en/api/@connectum/interceptors/circuit-breaker/functions/createCircuitBreakerInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [circuit-breaker](../index.md) / createCircuitBreakerInterceptor # Function: createCircuitBreakerInterceptor() > **createCircuitBreakerInterceptor**(`options?`): `Interceptor` Defined in: [circuit-breaker.ts:61](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/circuit-breaker.ts#L61) Create circuit breaker interceptor Prevents cascading failures by opening circuit after consecutive failures. When circuit is open, requests fail immediately without calling the service. Circuit States: * Closed (normal): Requests pass through * Open (failing): Requests rejected immediately * Half-Open (testing): Single request allowed to test recovery ## Parameters ### options? [`CircuitBreakerOptions`](../../interfaces/CircuitBreakerOptions.md) = `{}` Circuit breaker options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createServer } from '@connectum/core'; import { createCircuitBreakerInterceptor } from '@connectum/interceptors'; import { myRoutes } from './routes.js'; const server = createServer({ services: [myRoutes], interceptors: [ createCircuitBreakerInterceptor({ threshold: 5, // Open after 5 consecutive failures halfOpenAfter: 30000, // Try again after 30 seconds skipStreaming: true, // Skip streaming calls }), ], }); await server.start(); ``` ```typescript import { createConnectTransport } from '@connectrpc/connect-node'; import { createCircuitBreakerInterceptor } from '@connectum/interceptors'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [ createCircuitBreakerInterceptor({ threshold: 3 }), ], }); ``` --- --- url: >- /en/api/@connectum/interceptors/defaults/functions/createDefaultInterceptors.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [defaults](../index.md) / createDefaultInterceptors # Function: createDefaultInterceptors() > **createDefaultInterceptors**(`options?`): `Interceptor`\[] Defined in: [defaults.ts:128](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L128) Creates the default interceptor chain with the specified configuration. The interceptor order is fixed and intentional: 1. **errorHandler** - Catch-all error normalization (outermost, must be first) 2. **timeout** - Enforce deadline before any processing 3. **bulkhead** - Limit concurrency 4. **circuitBreaker** - Prevent cascading failures 5. **retry** - Retry transient failures (exponential backoff) 6. **fallback** - Graceful degradation (DISABLED by default) 7. **validation** - @connectrpc/validate (createValidateInterceptor) 8. **serializer** - JSON serialization (innermost) ## Parameters ### options? [`DefaultInterceptorOptions`](../interfaces/DefaultInterceptorOptions.md) = `{}` Configuration for each interceptor ## Returns `Interceptor`\[] Array of configured interceptors in the correct order ## Example ```typescript // All defaults (fallback disabled) const interceptors = createDefaultInterceptors(); // Disable retry, custom timeout const interceptors = createDefaultInterceptors({ retry: false, timeout: { duration: 10000 }, }); // Enable fallback with handler const interceptors = createDefaultInterceptors({ fallback: { handler: () => ({ data: [] }) }, }); // No interceptors: omit `interceptors` option in createServer() // or pass `interceptors: []` ``` --- --- url: >- /en/api/@connectum/interceptors/errorHandler/functions/createErrorHandlerInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [errorHandler](../index.md) / createErrorHandlerInterceptor # Function: createErrorHandlerInterceptor() > **createErrorHandlerInterceptor**(`options?`): `Interceptor` Defined in: [errorHandler.ts:48](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/errorHandler.ts#L48) Create error handler interceptor Catches all errors and transforms them into ConnectError instances with proper error codes. Recognizes SanitizableError for safe client-facing messages while preserving server details for logging. IMPORTANT: This interceptor should be FIRST in the chain to catch all errors. ## Parameters ### options? [`ErrorHandlerOptions`](../../interfaces/ErrorHandlerOptions.md) = `{}` Error handler options ## Returns `Interceptor` ConnectRPC interceptor ## Example ```typescript import { createServer } from '@connectum/core'; import { createErrorHandlerInterceptor } from '@connectum/interceptors'; import { myRoutes } from './routes.js'; const server = createServer({ services: [myRoutes], interceptors: [ createErrorHandlerInterceptor({ onError: ({ error, code, serverDetails, stack }) => { logger.error('RPC error', { error: error.message, code, serverDetails, stack }); }, }), ], }); await server.start(); ``` --- --- url: >- /en/api/@connectum/interceptors/fallback/functions/createFallbackInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [fallback](../index.md) / createFallbackInterceptor # Function: createFallbackInterceptor() > **createFallbackInterceptor**<`T`>(`options`): `Interceptor` Defined in: [fallback.ts:57](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/fallback.ts#L57) Create fallback interceptor Provides fallback response when service fails, enabling graceful degradation. ## Type Parameters ### T `T` = `unknown` ## Parameters ### options [`FallbackOptions`](../../interfaces/FallbackOptions.md)<`T`> Fallback options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createServer } from '@connectum/core'; import { createFallbackInterceptor } from '@connectum/interceptors'; import { myRoutes } from './routes.js'; const server = createServer({ services: [myRoutes], interceptors: [ createFallbackInterceptor({ handler: (error) => { console.error('Service failed, returning cached data:', error); return { message: getCachedData() }; }, skipStreaming: true, }), ], }); await server.start(); ``` ```typescript import { createConnectTransport } from '@connectrpc/connect-node'; import { createFallbackInterceptor } from '@connectum/interceptors'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [ createFallbackInterceptor({ handler: () => ({ data: [] }), }), ], }); ``` --- --- url: /en/api/@connectum/auth/functions/createGatewayAuthInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createGatewayAuthInterceptor # Function: createGatewayAuthInterceptor() > **createGatewayAuthInterceptor**(`options`): `Interceptor` Defined in: [packages/auth/src/gateway-auth-interceptor.ts:92](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/gateway-auth-interceptor.ts#L92) Create a gateway authentication interceptor. Reads pre-authenticated identity from gateway-injected headers. Trust is established by checking a designated header value against a list of expected values (shared secrets or trusted IP ranges). ## Parameters ### options [`GatewayAuthInterceptorOptions`](../interfaces/GatewayAuthInterceptorOptions.md) Gateway auth configuration ## Returns `Interceptor` ConnectRPC interceptor ## Example ```typescript const gatewayAuth = createGatewayAuthInterceptor({ headerMapping: { subject: 'x-user-id', name: 'x-user-name', roles: 'x-user-roles', }, trustSource: { header: 'x-gateway-secret', expectedValues: [process.env.GATEWAY_SECRET], }, }); ``` --- --- url: /en/api/@connectum/healthcheck/functions/createHealthcheckManager.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / createHealthcheckManager # Function: createHealthcheckManager() > **createHealthcheckManager**(): [`HealthcheckManager`](../classes/HealthcheckManager.md) Defined in: [HealthcheckManager.ts:118](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/HealthcheckManager.ts#L118) Create a new isolated HealthcheckManager instance Useful for testing or running multiple servers in one process. ## Returns [`HealthcheckManager`](../classes/HealthcheckManager.md) --- --- url: /en/api/@connectum/healthcheck/functions/createHttpHealthHandler.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / createHttpHealthHandler # Function: createHttpHealthHandler() > **createHttpHealthHandler**(`manager`, `healthPaths?`): `HttpHandler` Defined in: [httpHandler.ts:57](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/httpHandler.ts#L57) Create HTTP health handler that mirrors gRPC healthcheck status Returns an HttpHandler compatible with the ProtocolRegistration interface. ## Parameters ### manager [`HealthcheckManager`](../classes/HealthcheckManager.md) Healthcheck manager instance ### healthPaths? `string`\[] = `DEFAULT_HTTP_PATHS` HTTP health endpoint paths ## Returns `HttpHandler` HTTP handler function that returns true if request was handled --- --- url: /en/api/@connectum/auth/functions/createJwtAuthInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createJwtAuthInterceptor # Function: createJwtAuthInterceptor() > **createJwtAuthInterceptor**(`options`): `Interceptor` Defined in: [packages/auth/src/jwt-auth-interceptor.ts:168](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/jwt-auth-interceptor.ts#L168) Create a JWT authentication interceptor. Convenience wrapper around createAuthInterceptor() that handles JWT extraction from Authorization header, verification via jose, and standard claim mapping to AuthContext. ## Parameters ### options [`JwtAuthInterceptorOptions`](../interfaces/JwtAuthInterceptorOptions.md) JWT authentication options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createJwtAuthInterceptor } from '@connectum/auth'; const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', issuer: 'https://auth.example.com/', audience: 'my-api', claimsMapping: { roles: 'realm_access.roles', scopes: 'scope', }, }); ``` ```typescript const jwtAuth = createJwtAuthInterceptor({ secret: process.env.JWT_SECRET, issuer: 'my-service', }); ``` --- --- url: /en/api/@connectum/interceptors/logger/functions/createLoggerInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [logger](../index.md) / createLoggerInterceptor # Function: createLoggerInterceptor() > **createLoggerInterceptor**(`options?`): `Interceptor` Defined in: [logger.ts:86](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/logger.ts#L86) Create logger interceptor Logs all RPC requests and responses with timing information. Supports both unary and streaming RPCs. ## Parameters ### options? [`LoggerOptions`](../../interfaces/LoggerOptions.md) = `{}` Logger options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createServer } from '@connectum/core'; import { createLoggerInterceptor } from '@connectum/interceptors'; import { myRoutes } from './routes.js'; const server = createServer({ services: [myRoutes], interceptors: [ createLoggerInterceptor({ level: 'debug', skipHealthCheck: true, }), ], }); await server.start(); ``` ```typescript import { createConnectTransport } from '@connectrpc/connect-node'; import { createLoggerInterceptor } from '@connectum/interceptors'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [ createLoggerInterceptor({ level: 'debug' }), ], }); ``` --- --- url: >- /en/api/@connectum/interceptors/method-filter/functions/createMethodFilterInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [method-filter](../index.md) / createMethodFilterInterceptor # Function: createMethodFilterInterceptor() > **createMethodFilterInterceptor**(`methods`): `Interceptor` Defined in: [method-filter.ts:130](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/method-filter.ts#L130) Create a method filter interceptor that routes to per-method interceptors based on wildcard pattern matching. Resolution order (all matching patterns execute): 1. Global wildcard `"*"` (executed first) 2. Service wildcard `"Service/*"` (executed second) 3. Exact match `"Service/Method"` (executed last) Within each pattern, interceptors execute in array order. ## Parameters ### methods [`MethodFilterMap`](../../type-aliases/MethodFilterMap.md) Method pattern to interceptors mapping ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createMethodFilterInterceptor } from '@connectum/interceptors'; const perMethodInterceptor = createMethodFilterInterceptor({ "*": [logRequest], "admin.v1.AdminService/*": [requireAdmin], "user.v1.UserService/DeleteUser": [requireAdmin, auditLog], }); const server = createServer({ services: [routes], interceptors: [perMethodInterceptor], }); ``` ```typescript createMethodFilterInterceptor({ "catalog.v1.CatalogService/GetProduct": [ createTimeoutInterceptor({ duration: 5_000 }), ], "report.v1.ReportService/*": [ createTimeoutInterceptor({ duration: 30_000 }), createCircuitBreakerInterceptor({ threshold: 3 }), ], }); ``` --- --- url: /en/api/@connectum/auth/testing/functions/createMockAuthContext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / createMockAuthContext # Function: createMockAuthContext() > **createMockAuthContext**(`overrides?`): [`AuthContext`](../../interfaces/AuthContext.md) Defined in: [packages/auth/src/testing/mock-context.ts:39](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/testing/mock-context.ts#L39) Create a mock AuthContext for testing. Merges provided overrides with sensible test defaults. ## Parameters ### overrides? `Partial`<[`AuthContext`](../../interfaces/AuthContext.md)> Partial AuthContext to override defaults ## Returns [`AuthContext`](../../interfaces/AuthContext.md) Complete AuthContext ## Example ```typescript import { createMockAuthContext } from '@connectum/auth/testing'; const ctx = createMockAuthContext({ subject: 'admin-1', roles: ['admin'] }); assert.strictEqual(ctx.subject, 'admin-1'); assert.deepStrictEqual(ctx.roles, ['admin']); assert.strictEqual(ctx.type, 'test'); // default preserved ``` --- --- url: >- /en/api/@connectum/otel/client-interceptor/functions/createOtelClientInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [client-interceptor](../index.md) / createOtelClientInterceptor # Function: createOtelClientInterceptor() > **createOtelClientInterceptor**(`options`): `Interceptor` Defined in: [packages/otel/src/client-interceptor.ts:58](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/client-interceptor.ts#L58) Creates a ConnectRPC interceptor that instruments outgoing RPC calls with OpenTelemetry tracing and/or metrics. The interceptor follows OpenTelemetry semantic conventions for RPC: * Creates client spans with standard RPC attributes * Injects trace context into outgoing request headers for propagation * Records call duration, request size, and response size metrics * Handles both unary and streaming calls ## Parameters ### options [`OtelClientInterceptorOptions`](../../interfaces/OtelClientInterceptorOptions.md) Configuration options for the client interceptor ## Returns `Interceptor` A ConnectRPC Interceptor function ## Example ```typescript import { createOtelClientInterceptor } from '@connectum/otel'; import { createConnectTransport } from '@connectrpc/connect-node'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [createOtelClientInterceptor({ serverAddress: 'localhost', serverPort: 5000, filter: ({ service }) => !service.includes("Health"), })], }); ``` --- --- url: /en/api/@connectum/otel/interceptor/functions/createOtelInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [interceptor](../index.md) / createOtelInterceptor # Function: createOtelInterceptor() > **createOtelInterceptor**(`options?`): `Interceptor` Defined in: [packages/otel/src/interceptor.ts:51](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/interceptor.ts#L51) Creates a ConnectRPC interceptor that instruments RPC calls with OpenTelemetry tracing and/or metrics. The interceptor follows OpenTelemetry semantic conventions for RPC: * Creates server spans with standard RPC attributes * Records call duration, request size, and response size metrics * Supports context propagation with configurable trust mode * Handles both unary and streaming calls ## Parameters ### options? [`OtelInterceptorOptions`](../../interfaces/OtelInterceptorOptions.md) = `{}` Configuration options for the interceptor ## Returns `Interceptor` A ConnectRPC Interceptor function ## Example ```typescript import { createOtelInterceptor } from '@connectum/otel'; import { createServer } from '@connectum/core'; const server = createServer({ services: [routes], interceptors: [createOtelInterceptor({ serverPort: 5000, filter: ({ service }) => !service.includes("Health"), })], }); ``` --- --- url: /en/api/@connectum/auth/functions/createProtoAuthzInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createProtoAuthzInterceptor # Function: createProtoAuthzInterceptor() > **createProtoAuthzInterceptor**(`options?`): `Interceptor` Defined in: [packages/auth/src/proto/proto-authz-interceptor.ts:125](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/proto/proto-authz-interceptor.ts#L125) Create a proto-based authorization interceptor. Uses protobuf custom options (connectum.auth.v1) for declarative authorization rules defined in .proto files. When proto options do not resolve the decision, falls back to programmatic rules and an authorize callback. Authorization decision flow: ``` 1. resolveMethodAuth(req.method) -- read proto options 2. public = true --> skip (allow without authn) 3. Get auth context -- lazy: don't throw yet 4. requires defined, no context --> throw Unauthenticated 4b. requires defined, has context --> satisfiesRequirements? allow : deny 5. policy = "allow" --> allow 6. policy = "deny" --> deny 7. Evaluate programmatic rules -- unconditional rules work without context 8. Fallback: authorize callback --> requires auth context 9. Apply defaultPolicy --> deny without context = Unauthenticated ``` IMPORTANT: This interceptor MUST run AFTER an authentication interceptor in the chain (except for methods marked as `public` in proto options or matched by unconditional programmatic rules). ## Parameters ### options? [`ProtoAuthzInterceptorOptions`](../interfaces/ProtoAuthzInterceptorOptions.md) = `{}` Proto authorization interceptor options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createProtoAuthzInterceptor } from '@connectum/auth'; const authz = createProtoAuthzInterceptor(); // Proto options in .proto files control authorization ``` ```typescript import { createProtoAuthzInterceptor } from '@connectum/auth'; const authz = createProtoAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'admin-only', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, ], authorize: (ctx, req) => ctx.roles.includes('superadmin'), }); ``` --- --- url: /en/api/@connectum/interceptors/retry/functions/createRetryInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [retry](../index.md) / createRetryInterceptor # Function: createRetryInterceptor() > **createRetryInterceptor**(`options?`): `Interceptor` Defined in: [retry.ts:44](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/retry.ts#L44) Create retry interceptor Automatically retries failed unary RPC calls with exponential backoff. Only retries on configurable error codes (Unavailable and ResourceExhausted by default). ## Parameters ### options? [`RetryOptions`](../../interfaces/RetryOptions.md) = `{}` Retry options ## Returns `Interceptor` ConnectRPC interceptor ## Example ```typescript import { createServer } from '@connectum/core'; import { createRetryInterceptor } from '@connectum/interceptors'; const server = createServer({ services: [myRoutes], interceptors: [ createRetryInterceptor({ maxRetries: 3, initialDelay: 200, maxDelay: 5000, retryableCodes: [Code.Unavailable, Code.ResourceExhausted], }), ], }); await server.start(); ``` --- --- url: /en/api/@connectum/otel/metrics/functions/createRpcClientMetrics.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [metrics](../index.md) / createRpcClientMetrics # Function: createRpcClientMetrics() > **createRpcClientMetrics**(`meter`): [`RpcClientMetrics`](../interfaces/RpcClientMetrics.md) Defined in: [packages/otel/src/metrics.ts:106](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L106) Creates RPC client metric instruments from the given meter All metrics follow OpenTelemetry semantic conventions for RPC: * `rpc.client.call.duration` -- call duration in seconds * `rpc.client.request.size` -- request message size in bytes * `rpc.client.response.size` -- response message size in bytes ## Parameters ### meter [`Meter`](../../interfaces/Meter.md) OpenTelemetry Meter instance to create histograms from ## Returns [`RpcClientMetrics`](../interfaces/RpcClientMetrics.md) Object containing all RPC client metric instruments ## Example ```typescript import { metrics } from '@opentelemetry/api'; import { createRpcClientMetrics } from '@connectum/otel'; const meter = metrics.getMeter('my-client'); const rpcMetrics = createRpcClientMetrics(meter); rpcMetrics.callDuration.record(0.045, { 'rpc.method': 'GetUser' }); ``` --- --- url: /en/api/@connectum/otel/metrics/functions/createRpcServerMetrics.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [metrics](../index.md) / createRpcServerMetrics # Function: createRpcServerMetrics() > **createRpcServerMetrics**(`meter`): [`RpcServerMetrics`](../interfaces/RpcServerMetrics.md) Defined in: [packages/otel/src/metrics.ts:65](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L65) Creates RPC server metric instruments from the given meter All metrics follow OpenTelemetry semantic conventions for RPC: * `rpc.server.call.duration` -- call duration in seconds * `rpc.server.request.size` -- request message size in bytes * `rpc.server.response.size` -- response message size in bytes ## Parameters ### meter [`Meter`](../../interfaces/Meter.md) OpenTelemetry Meter instance to create histograms from ## Returns [`RpcServerMetrics`](../interfaces/RpcServerMetrics.md) Object containing all RPC server metric instruments ## Example ```typescript import { metrics } from '@opentelemetry/api'; import { createRpcServerMetrics } from '@connectum/otel'; const meter = metrics.getMeter('my-service'); const rpcMetrics = createRpcServerMetrics(meter); rpcMetrics.callDuration.record(0.123, { 'rpc.method': 'GetUser' }); ``` --- --- url: >- /en/api/@connectum/interceptors/serializer/functions/createSerializerInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [serializer](../index.md) / createSerializerInterceptor # Function: createSerializerInterceptor() > **createSerializerInterceptor**(`options?`): `Interceptor` Defined in: [serializer.ts:84](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/serializer.ts#L84) Create serializer interceptor Automatically serializes/deserializes messages to/from JSON. Skips gRPC services by default (they use protobuf binary format). ## Parameters ### options? [`SerializerOptions`](../../interfaces/SerializerOptions.md) = `{}` Serializer options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createServer } from '@connectum/core'; import { createSerializerInterceptor } from '@connectum/interceptors'; import { myRoutes } from './routes.js'; const server = createServer({ services: [myRoutes], interceptors: [ createSerializerInterceptor({ skipGrpcServices: true, alwaysEmitImplicit: true, ignoreUnknownFields: true, }), ], }); await server.start(); ``` ```typescript import { createConnectTransport } from '@connectrpc/connect-node'; import { createSerializerInterceptor } from '@connectum/interceptors'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [ createSerializerInterceptor({ alwaysEmitImplicit: true }), ], }); ``` --- --- url: /en/api/@connectum/core/functions/createServer.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / createServer # Function: createServer() > **createServer**(`options`): [`Server`](../types/interfaces/Server.md) Defined in: [packages/core/src/Server.ts:280](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/Server.ts#L280) Create a new server instance Returns an unstarted server. Call server.start() to begin accepting connections. ## Parameters ### options [`CreateServerOptions`](../types/interfaces/CreateServerOptions.md) Server configuration options ## Returns [`Server`](../types/interfaces/Server.md) Unstarted server instance ## Example ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [myRoutes], protocols: [Healthcheck({ httpEnabled: true }), Reflection()], shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` --- --- url: /en/api/@connectum/auth/functions/createSessionAuthInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createSessionAuthInterceptor # Function: createSessionAuthInterceptor() > **createSessionAuthInterceptor**(`options`): `Interceptor` Defined in: [packages/auth/src/session-auth-interceptor.ts:60](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/session-auth-interceptor.ts#L60) Create a session-based authentication interceptor. Two-step authentication: 1. Extract token from request 2. Verify session via user-provided callback (receives full headers for cookie support) 3. Map session data to AuthContext via user-provided mapper ## Parameters ### options [`SessionAuthInterceptorOptions`](../interfaces/SessionAuthInterceptorOptions.md) Session auth configuration ## Returns `Interceptor` ConnectRPC interceptor ## Example ```typescript import { createSessionAuthInterceptor } from '@connectum/auth'; const sessionAuth = createSessionAuthInterceptor({ verifySession: (token, headers) => auth.api.getSession({ headers }), mapSession: (s) => ({ subject: s.user.id, name: s.user.name, roles: [], scopes: [], claims: s.user, type: 'session', }), cache: { ttl: 60_000 }, }); ``` --- --- url: /en/api/@connectum/auth/testing/functions/createTestJwt.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / createTestJwt # Function: createTestJwt() > **createTestJwt**(`payload`, `options?`): `Promise`<`string`> Defined in: [packages/auth/src/testing/test-jwt.ts:49](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/testing/test-jwt.ts#L49) Create a signed test JWT for integration testing. Uses HS256 algorithm with a deterministic test key. NOT for production use. ## Parameters ### payload `Record`<`string`, `unknown`> JWT claims ### options? Signing options #### audience? `string` #### expiresIn? `string` #### issuer? `string` ## Returns `Promise`<`string`> Signed JWT string ## Example ```typescript import { createTestJwt, TEST_JWT_SECRET } from '@connectum/auth/testing'; const token = await createTestJwt({ sub: 'user-123', roles: ['admin'], scope: 'read write', }); // Use with createJwtAuthInterceptor in tests const auth = createJwtAuthInterceptor({ secret: TEST_JWT_SECRET }); ``` --- --- url: /en/api/@connectum/interceptors/timeout/functions/createTimeoutInterceptor.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [timeout](../index.md) / createTimeoutInterceptor # Function: createTimeoutInterceptor() > **createTimeoutInterceptor**(`options?`): `Interceptor` Defined in: [timeout.ts:55](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/timeout.ts#L55) Create timeout interceptor Prevents requests from hanging indefinitely by enforcing a timeout. Requests that exceed the timeout are cancelled and throw DeadlineExceeded error. ## Parameters ### options? [`TimeoutOptions`](../../interfaces/TimeoutOptions.md) = `{}` Timeout options ## Returns `Interceptor` ConnectRPC interceptor ## Examples ```typescript import { createServer } from '@connectum/core'; import { createTimeoutInterceptor } from '@connectum/interceptors'; import { myRoutes } from './routes.js'; const server = createServer({ services: [myRoutes], interceptors: [ createTimeoutInterceptor({ duration: 30000, // 30 second timeout skipStreaming: true, // Skip streaming calls }), ], }); await server.start(); ``` ```typescript import { createConnectTransport } from '@connectrpc/connect-node'; import { createTimeoutInterceptor } from '@connectum/interceptors'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [ createTimeoutInterceptor({ duration: 10000 }), ], }); ``` --- --- url: /en/api/@connectum/otel/shared/functions/estimateMessageSize.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [shared](../index.md) / estimateMessageSize # Function: estimateMessageSize() > **estimateMessageSize**(`message`): `number` Defined in: [packages/otel/src/shared.ts:49](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L49) Estimates the serialized size of a protobuf message in bytes. If the message exposes a `toBinary()` method (standard for protobuf-es messages), returns the byte length of the serialized form. Otherwise returns 0. Results are cached per message object using a WeakMap. ## Parameters ### message `unknown` The message to estimate size for ## Returns `number` Size in bytes, or 0 if size cannot be determined --- --- url: /en/api/@connectum/cli/commands/proto-sync/functions/executeProtoSync.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/cli](../../../index.md) / [commands/proto-sync](../index.md) / executeProtoSync # Function: executeProtoSync() > **executeProtoSync**(`options`): `Promise`<`void`> Defined in: [commands/proto-sync.ts:41](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/commands/proto-sync.ts#L41) Execute the proto sync pipeline. ## Parameters ### options [`ProtoSyncOptions`](../interfaces/ProtoSyncOptions.md) Proto sync configuration ## Returns `Promise`<`void`> --- --- url: >- /en/api/@connectum/cli/utils/reflection/functions/fetchFileDescriptorSetBinary.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/cli](../../../index.md) / [utils/reflection](../index.md) / fetchFileDescriptorSetBinary # Function: fetchFileDescriptorSetBinary() > **fetchFileDescriptorSetBinary**(`url`): `Promise`<`Uint8Array`<`ArrayBufferLike`>> Defined in: [utils/reflection.ts:72](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/utils/reflection.ts#L72) Fetch FileDescriptorSet as binary (.binpb) from a running server via reflection. The binary output can be passed directly to `buf generate` as input. ## Parameters ### url `string` Server URL (e.g., "http://localhost:5000") ## Returns `Promise`<`Uint8Array`<`ArrayBufferLike`>> Binary FileDescriptorSet (.binpb format) ## Example ```typescript const binpb = await fetchFileDescriptorSetBinary("http://localhost:5000"); writeFileSync("/tmp/descriptors.binpb", binpb); // Then: buf generate /tmp/descriptors.binpb --output ./gen ``` --- --- url: /en/api/@connectum/cli/utils/reflection/functions/fetchReflectionData.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/cli](../../../index.md) / [utils/reflection](../index.md) / fetchReflectionData # Function: fetchReflectionData() > **fetchReflectionData**(`url`): `Promise`<[`ReflectionResult`](../interfaces/ReflectionResult.md)> Defined in: [utils/reflection.ts:42](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/utils/reflection.ts#L42) Fetch service and file descriptor information from a running server via reflection. Uses gRPC Server Reflection Protocol (v1 with v1alpha fallback). ## Parameters ### url `string` Server URL (e.g., "http://localhost:5000") ## Returns `Promise`<[`ReflectionResult`](../interfaces/ReflectionResult.md)> ReflectionResult with services, registry, and file names ## Example ```typescript const result = await fetchReflectionData("http://localhost:5000"); console.log(result.services); // ["grpc.health.v1.Health", ...] ``` --- --- url: /en/api/@connectum/auth/functions/getAuthContext.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / getAuthContext # Function: getAuthContext() > **getAuthContext**(): [`AuthContext`](../interfaces/AuthContext.md) | `undefined` Defined in: [packages/auth/src/context.ts:44](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/context.ts#L44) Get the current auth context. Returns the AuthContext set by the auth interceptor in the current async context. Returns undefined if no auth interceptor is active or the current method was skipped. ## Returns [`AuthContext`](../interfaces/AuthContext.md) | `undefined` Current auth context or undefined ## Example ```typescript import { getAuthContext } from '@connectum/auth'; const handler = { async getUser(req) { const auth = getAuthContext(); if (!auth) throw new ConnectError('Not authenticated', Code.Unauthenticated); return { user: await db.getUser(auth.subject) }; }, }; ``` --- --- url: /en/api/@connectum/otel/functions/getBatchSpanProcessorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / getBatchSpanProcessorOptions # Function: getBatchSpanProcessorOptions() > **getBatchSpanProcessorOptions**(): [`BatchSpanProcessorOptions`](../interfaces/BatchSpanProcessorOptions.md) Defined in: [packages/otel/src/config.ts:100](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L100) Gets batch span processor options from environment variables Environment variables: * OTEL\_BSP\_MAX\_EXPORT\_BATCH\_SIZE: Max number of spans to export in a single batch (default: 100) * OTEL\_BSP\_MAX\_QUEUE\_SIZE: Max queue size - if reached, new spans are dropped (default: 1000) * OTEL\_BSP\_SCHEDULE\_DELAY: Time to wait before automatically exporting spans in ms (default: 1000) * OTEL\_BSP\_EXPORT\_TIMEOUT: Max time allowed for a single export operation in ms (default: 10000) ## Returns [`BatchSpanProcessorOptions`](../interfaces/BatchSpanProcessorOptions.md) Batch span processor options --- --- url: /en/api/@connectum/otel/functions/getCollectorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / getCollectorOptions # Function: getCollectorOptions() > **getCollectorOptions**(): [`CollectorOptions`](../interfaces/CollectorOptions.md) Defined in: [packages/otel/src/config.ts:81](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L81) Gets collector endpoint options from environment variables Environment variables: * OTEL\_EXPORTER\_OTLP\_ENDPOINT: Collector endpoint URL ## Returns [`CollectorOptions`](../interfaces/CollectorOptions.md) Collector options object --- --- url: /en/api/@connectum/otel/logger/functions/getLogger.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [logger](../index.md) / getLogger # Function: getLogger() > **getLogger**(`name?`, `options?`): [`Logger`](../interfaces/Logger.md) Defined in: [packages/otel/src/logger.ts:28](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L28) ## Parameters ### name? `string` ### options? [`LoggerOptions`](../interfaces/LoggerOptions.md) ## Returns [`Logger`](../interfaces/Logger.md) --- --- url: /en/api/@connectum/otel/meter/functions/getMeter.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [meter](../index.md) / getMeter # Function: getMeter() > **getMeter**(): [`Meter`](../../interfaces/Meter.md) Defined in: [packages/otel/src/meter.ts:14](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/meter.ts#L14) Returns the global Meter instance. Lazily initializes the OTel provider on first call. ## Returns [`Meter`](../../interfaces/Meter.md) --- --- url: /en/api/@connectum/otel/functions/getOTLPSettings.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / getOTLPSettings # Function: getOTLPSettings() > **getOTLPSettings**(): [`OTLPSettings`](../interfaces/OTLPSettings.md) Defined in: [packages/otel/src/config.ts:65](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L65) Gets OTLP exporter settings from environment variables Environment variables: * OTEL\_TRACES\_EXPORTER: Trace exporter type (console|otlp/http|otlp/grpc|none) * OTEL\_METRICS\_EXPORTER: Metric exporter type (console|otlp/http|otlp/grpc|none) * OTEL\_LOGS\_EXPORTER: Logs exporter type (console|otlp/http|otlp/grpc|none) ## Returns [`OTLPSettings`](../interfaces/OTLPSettings.md) OTLP settings object --- --- url: /en/api/@connectum/otel/provider/functions/getProvider.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [provider](../index.md) / getProvider # Function: getProvider() > **getProvider**(): `OtelProvider` Defined in: [packages/otel/src/provider.ts:276](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/provider.ts#L276) Get the current OpenTelemetry provider. If not yet initialized, lazily creates a provider with default (environment-based) options. ## Returns `OtelProvider` The active OtelProvider instance --- --- url: /en/api/@connectum/auth/functions/getPublicMethods.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / getPublicMethods # Function: getPublicMethods() > **getPublicMethods**(`services`): `string`\[] Defined in: [packages/auth/src/proto/reader.ts:165](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/proto/reader.ts#L165) Get the list of public method patterns from a set of service descriptors. Iterates over all methods in the given services, resolves their auth configuration, and returns patterns for methods marked as `public`. The returned patterns follow the `"service.typeName/method.name"` format used by `skipMethods` in auth interceptors. ## Parameters ### services readonly `DescService`\[] Service descriptors to scan ## Returns `string`\[] Array of method patterns in `"ServiceTypeName/MethodName"` format ## Example ```typescript import { getPublicMethods } from '@connectum/auth/proto'; const publicMethods = getPublicMethods([GreeterService, HealthService]); // ["greet.v1.GreeterService/SayHello", "grpc.health.v1.Health/Check"] const authn = createAuthInterceptor({ skipMethods: publicMethods, verifyCredentials: myVerifier, }); ``` --- --- url: /en/api/@connectum/otel/functions/getServiceMetadata.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / getServiceMetadata # Function: getServiceMetadata() > **getServiceMetadata**(): `object` Defined in: [packages/otel/src/config.ts:116](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L116) Gets service metadata from environment variables Uses OTEL\_SERVICE\_NAME as primary source, falls back to npm\_package\_name. ## Returns `object` Service name and version ### name > **name**: `string` ### version > **version**: `string` --- --- url: /en/api/@connectum/core/functions/getTLSPath.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / getTLSPath # Function: getTLSPath() > **getTLSPath**(): `string` Defined in: [packages/core/src/TLSConfig.ts:20](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/TLSConfig.ts#L20) Get TLS directory path Resolves TLS directory from environment variable or default location. ## Returns `string` TLS directory path --- --- url: /en/api/@connectum/otel/tracer/functions/getTracer.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [tracer](../index.md) / getTracer # Function: getTracer() > **getTracer**(): [`Tracer`](../../interfaces/Tracer.md) Defined in: [packages/otel/src/tracer.ts:14](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/tracer.ts#L14) Returns the global Tracer instance. Lazily initializes the OTel provider on first call. ## Returns [`Tracer`](../../interfaces/Tracer.md) --- --- url: /en/api/@connectum/healthcheck/functions/Healthcheck.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / Healthcheck # Function: Healthcheck() > **Healthcheck**(`options?`): `ProtocolRegistration` Defined in: [Healthcheck.ts:83](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/Healthcheck.ts#L83) Create healthcheck protocol registration Returns a ProtocolRegistration directly (not `{ protocol, manager }`). Pass to createServer({ protocols: \[...] }). Use the singleton `healthcheckManager` export to control health status. ## Parameters ### options? [`HealthcheckOptions`](../interfaces/HealthcheckOptions.md) = `{}` Healthcheck configuration options ## Returns `ProtocolRegistration` ProtocolRegistration for createServer ## Example ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; const server = createServer({ services: [myRoutes], protocols: [Healthcheck({ httpEnabled: true })], }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` --- --- url: /en/api/@connectum/otel/provider/functions/initProvider.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [provider](../index.md) / initProvider # Function: initProvider() > **initProvider**(`options?`): `void` Defined in: [packages/otel/src/provider.ts:261](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/provider.ts#L261) Initialize the OpenTelemetry provider with explicit options. Must be called before any telemetry is emitted if custom configuration is needed. Throws if already initialized -- call [shutdownProvider](shutdownProvider.md) first to re-initialize. ## Parameters ### options? [`ProviderOptions`](../interfaces/ProviderOptions.md) Optional provider configuration overrides ## Returns `void` ## Throws Error if provider is already initialized --- --- url: /en/api/@connectum/core/functions/isSanitizableError.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / isSanitizableError # Function: isSanitizableError() > **isSanitizableError**(`err`): `err is Error & SanitizableError & { code: number }` Defined in: [packages/core/src/errors.ts:28](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/errors.ts#L28) Type guard for SanitizableError. Checks if the value is an object with clientMessage (string) and serverDetails (non-null object) properties, plus a numeric code. ## Parameters ### err `unknown` ## Returns `err is Error & SanitizableError & { code: number }` --- --- url: /en/api/@connectum/auth/functions/matchesMethodPattern.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / matchesMethodPattern # Function: matchesMethodPattern() > **matchesMethodPattern**(`serviceName`, `methodName`, `patterns`): `boolean` Defined in: [packages/auth/src/method-match.ts:23](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/method-match.ts#L23) Check if a method matches any of the given patterns. Patterns: * "\*" — matches all methods * "Service/\*" — matches all methods of a service * "Service/Method" — matches exact method ## Parameters ### serviceName `string` Fully-qualified service name (e.g., "user.v1.UserService") ### methodName `string` Method name (e.g., "GetUser") ### patterns readonly `string`\[] Readonly array of match patterns ## Returns `boolean` true if the method matches any pattern --- --- url: /en/api/@connectum/auth/functions/parseAuthHeaders.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / parseAuthHeaders # Function: parseAuthHeaders() > **parseAuthHeaders**(`headers`): [`AuthContext`](../interfaces/AuthContext.md) | `undefined` Defined in: [packages/auth/src/headers.ts:91](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/headers.ts#L91) Parse AuthContext from request headers. Deserializes auth context from standard headers set by an upstream service or gateway. Returns undefined if required headers are missing. WARNING: Only use this in trusted environments (behind mTLS, mesh, etc.). For untrusted environments, use createTrustedHeadersReader() instead. ## Parameters ### headers `Headers` Request headers to parse ## Returns [`AuthContext`](../interfaces/AuthContext.md) | `undefined` Parsed AuthContext or undefined if headers are missing ## Example ```typescript import { parseAuthHeaders } from '@connectum/auth'; const context = parseAuthHeaders(req.header); if (context) { console.log(`Authenticated as ${context.subject}`); } ``` --- --- url: /en/api/@connectum/core/functions/parseEnvConfig.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / parseEnvConfig # Function: parseEnvConfig() > **parseEnvConfig**(`env?`): `object` Defined in: [packages/core/src/config/envSchema.ts:145](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L145) Parse and validate environment configuration ## Parameters ### env? `Record`<`string`, `string` | `undefined`> = `process.env` ## Returns ### GRACEFUL\_SHUTDOWN\_ENABLED > **GRACEFUL\_SHUTDOWN\_ENABLED**: `boolean` Enable graceful shutdown #### Default ```ts true ``` ### GRACEFUL\_SHUTDOWN\_TIMEOUT\_MS > **GRACEFUL\_SHUTDOWN\_TIMEOUT\_MS**: `number` Graceful shutdown timeout in milliseconds #### Default ```ts 30000 ``` ### HTTP\_HEALTH\_ENABLED > **HTTP\_HEALTH\_ENABLED**: `boolean` = `BooleanFromStringSchema` Enable HTTP health endpoints (/healthz, /readyz) When disabled, only gRPC healthcheck is available #### Default ```ts false ``` ### HTTP\_HEALTH\_PATH > **HTTP\_HEALTH\_PATH**: `string` HTTP health endpoint path #### Default ```ts '/healthz' ``` ### LISTEN > **LISTEN**: `string` Listen address #### Default ```ts '0.0.0.0' ``` ### LOG\_BACKEND > **LOG\_BACKEND**: `"otel"` | `"pino"` | `"console"` = `LoggerBackendSchema` Logger backend #### Default ```ts 'otel' ``` ### LOG\_FORMAT > **LOG\_FORMAT**: `"json"` | `"pretty"` = `LogFormatSchema` Log format (json for production, pretty for development) #### Default ```ts 'json' ``` ### LOG\_LEVEL > **LOG\_LEVEL**: `"error"` | `"debug"` | `"info"` | `"warn"` = `LogLevelSchema` Log level #### Default ```ts 'info' ``` ### NODE\_ENV > **NODE\_ENV**: `"test"` | `"production"` | `"development"` = `NodeEnvSchema` Node environment #### Default ```ts 'development' ``` ### OTEL\_EXPORTER\_OTLP\_ENDPOINT? > `optional` **OTEL\_EXPORTER\_OTLP\_ENDPOINT**: `string` OpenTelemetry exporter endpoint ### OTEL\_SERVICE\_NAME? > `optional` **OTEL\_SERVICE\_NAME**: `string` OpenTelemetry service name #### Default ```ts 'connectum-service' ``` ### PORT > **PORT**: `number` Server port #### Default ```ts 5000 ``` ## Example ```typescript const config = parseEnvConfig(); // or with custom env const config = parseEnvConfig({ PORT: '8080' }); ``` --- --- url: /en/api/@connectum/healthcheck/functions/parseServiceFromUrl.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / parseServiceFromUrl # Function: parseServiceFromUrl() > **parseServiceFromUrl**(`url`, `host`): `string` | `undefined` Defined in: [httpHandler.ts:110](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/httpHandler.ts#L110) Parse service name from URL query string ## Parameters ### url `string` | `undefined` ### host `string` | `undefined` ## Returns `string` | `undefined` ## Example ```typescript parseServiceFromUrl('/healthz?service=my.service.v1.MyService', req.headers.host) // returns 'my.service.v1.MyService' ``` --- --- url: /en/api/@connectum/core/functions/readTLSCertificates.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / readTLSCertificates # Function: readTLSCertificates() > **readTLSCertificates**(`options?`): `object` Defined in: [packages/core/src/TLSConfig.ts:36](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/TLSConfig.ts#L36) Read TLS certificates from configuration ## Parameters ### options? [`TLSOptions`](../types/interfaces/TLSOptions.md) = `{}` TLS options ## Returns `object` TLS key and cert buffers ### cert > **cert**: `Buffer` ### key > **key**: `Buffer` --- --- url: /en/api/@connectum/reflection/functions/Reflection.md --- [Connectum API Reference](../../../index.md) / [@connectum/reflection](../index.md) / Reflection # Function: Reflection() > **Reflection**(): `ProtocolRegistration` Defined in: [Reflection.ts:42](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/reflection/src/Reflection.ts#L42) Create reflection protocol registration Returns a ProtocolRegistration that implements gRPC Server Reflection Protocol (v1 + v1alpha). Pass it to createServer({ protocols: \[...] }). ## Returns `ProtocolRegistration` ProtocolRegistration for server reflection ## Example ```typescript import { createServer } from '@connectum/core'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [myRoutes], protocols: [Reflection()], }); await server.start(); // Now clients can discover services via gRPC reflection ``` --- --- url: /en/api/@connectum/auth/functions/requireAuthContext.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / requireAuthContext # Function: requireAuthContext() > **requireAuthContext**(): [`AuthContext`](../interfaces/AuthContext.md) Defined in: [packages/auth/src/context.ts:57](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/context.ts#L57) Get the current auth context or throw. Like getAuthContext() but throws ConnectError(Code.Unauthenticated) if no auth context is available. Use when auth is mandatory. ## Returns [`AuthContext`](../interfaces/AuthContext.md) Current auth context (never undefined) ## Throws ConnectError with Code.Unauthenticated if no context --- --- url: /en/api/@connectum/auth/functions/resolveMethodAuth.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / resolveMethodAuth # Function: resolveMethodAuth() > **resolveMethodAuth**(`method`): [`ResolvedMethodAuth`](../interfaces/ResolvedMethodAuth.md) Defined in: [packages/auth/src/proto/reader.ts:65](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/proto/reader.ts#L65) Resolve the effective authorization configuration for an RPC method. Merges service-level defaults (`service_auth`) with method-level overrides (`method_auth`). Method-level settings take priority over service-level ones. Results are cached in a `WeakMap` keyed by `DescMethod` (singleton per method, so 100% cache hit after the first call for each method). Priority (method overrides service): ``` method.public -> service.public -> false method.requires -> service.default_requires -> undefined method.policy -> service.default_policy -> undefined ``` ## Parameters ### method `DescMethod` The protobuf method descriptor ## Returns [`ResolvedMethodAuth`](../interfaces/ResolvedMethodAuth.md) Resolved authorization configuration --- --- url: /en/api/@connectum/core/functions/safeParseEnvConfig.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / safeParseEnvConfig # Function: safeParseEnvConfig() > **safeParseEnvConfig**(`env?`): `ZodSafeParseResult`<{ `GRACEFUL_SHUTDOWN_ENABLED`: `boolean`; `GRACEFUL_SHUTDOWN_TIMEOUT_MS`: `number`; `HTTP_HEALTH_ENABLED`: `boolean`; `HTTP_HEALTH_PATH`: `string`; `LISTEN`: `string`; `LOG_BACKEND`: `"otel"` | `"pino"` | `"console"`; `LOG_FORMAT`: `"json"` | `"pretty"`; `LOG_LEVEL`: `"error"` | `"debug"` | `"info"` | `"warn"`; `NODE_ENV`: `"test"` | `"production"` | `"development"`; `OTEL_EXPORTER_OTLP_ENDPOINT?`: `string`; `OTEL_SERVICE_NAME?`: `string`; `PORT`: `number`; }> Defined in: [packages/core/src/config/envSchema.ts:162](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L162) Safely parse environment configuration (returns result object) ## Parameters ### env? `Record`<`string`, `string` | `undefined`> = `process.env` ## Returns `ZodSafeParseResult`<{ `GRACEFUL_SHUTDOWN_ENABLED`: `boolean`; `GRACEFUL_SHUTDOWN_TIMEOUT_MS`: `number`; `HTTP_HEALTH_ENABLED`: `boolean`; `HTTP_HEALTH_PATH`: `string`; `LISTEN`: `string`; `LOG_BACKEND`: `"otel"` | `"pino"` | `"console"`; `LOG_FORMAT`: `"json"` | `"pretty"`; `LOG_LEVEL`: `"error"` | `"debug"` | `"info"` | `"warn"`; `NODE_ENV`: `"test"` | `"production"` | `"development"`; `OTEL_EXPORTER_OTLP_ENDPOINT?`: `string`; `OTEL_SERVICE_NAME?`: `string`; `PORT`: `number`; }> ## Example ```typescript const result = safeParseEnvConfig(); if (result.success) { console.log(result.data.PORT); } else { console.error(result.error.format()); } ``` --- --- url: /en/api/@connectum/auth/functions/setAuthHeaders.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / setAuthHeaders # Function: setAuthHeaders() > **setAuthHeaders**(`headers`, `context`, `propagatedClaims?`): `void` Defined in: [packages/auth/src/headers.ts:35](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/headers.ts#L35) Serialize AuthContext to request headers. Sets standard auth headers on the provided Headers object. Used by auth interceptors when propagateHeaders is enabled. ## Parameters ### headers `Headers` Headers object to set auth headers on ### context [`AuthContext`](../interfaces/AuthContext.md) Auth context to serialize ### propagatedClaims? `string`\[] Optional list of claim keys to propagate (all if undefined) ## Returns `void` --- --- url: /en/api/@connectum/otel/provider/functions/shutdownProvider.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [provider](../index.md) / shutdownProvider # Function: shutdownProvider() > **shutdownProvider**(): `Promise`<`void`> Defined in: [packages/otel/src/provider.ts:289](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/provider.ts#L289) Gracefully shutdown the provider and release resources. After shutdown, subsequent calls to [getProvider](getProvider.md) will create a fresh provider. If no provider exists, this is a no-op. ## Returns `Promise`<`void`> --- --- url: /en/api/@connectum/otel/traceAll/functions/traceAll.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [traceAll](../index.md) / traceAll # Function: traceAll() > **traceAll**<`T`>(`target`, `options?`): `T` Defined in: [packages/otel/src/traceAll.ts:36](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/traceAll.ts#L36) Wraps all methods of an object in OpenTelemetry spans using ES6 Proxy. Creates a Proxy that intercepts method calls and wraps each in a span. Method wrappers are created lazily (on first access, not at Proxy creation). Does NOT mutate the original object or its prototype. ## Type Parameters ### T `T` *extends* `object` ## Parameters ### target `T` The object whose methods to trace ### options? [`TraceAllOptions`](../../interfaces/TraceAllOptions.md) Tracing options ## Returns `T` A Proxy with traced methods ## Example ```typescript const service = traceAll(new UserService(), { prefix: "UserService", exclude: ["internalHelper"], }); ``` --- --- url: /en/api/@connectum/otel/traced/functions/traced.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [traced](../index.md) / traced # Function: traced() > **traced**<`T`>(`fn`, `options?`): `T` Defined in: [packages/otel/src/traced.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/traced.ts#L31) Wraps a function in an OpenTelemetry span. The wrapper preserves the original function's type signature. Supports both sync and async functions. ## Type Parameters ### T `T` *extends* (...`args`) => `any` ## Parameters ### fn `T` The function to wrap ### options? [`TracedOptions`](../../interfaces/TracedOptions.md) Tracing options ## Returns `T` Wrapped function with the same type signature ## Example ```typescript const findUser = traced(async (id: string) => { return await db.users.findById(id); }, { name: "UserService.findUser" }); ``` --- --- url: /en/api/@connectum/auth/testing/functions/withAuthContext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / withAuthContext # Function: withAuthContext() > **withAuthContext**<`T`>(`context`, `fn`): `Promise`<`T`> Defined in: [packages/auth/src/testing/with-context.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/testing/with-context.ts#L31) Run a function with a pre-set AuthContext. Sets the provided AuthContext in AsyncLocalStorage for the duration of the callback. Useful for testing handlers that call getAuthContext(). ## Type Parameters ### T `T` ## Parameters ### context [`AuthContext`](../../interfaces/AuthContext.md) Auth context to set ### fn () => `T` | `Promise`<`T`> Function to execute within the context ## Returns `Promise`<`T`> Return value of fn ## Example ```typescript import { withAuthContext, createMockAuthContext } from '@connectum/auth/testing'; import { getAuthContext } from '@connectum/auth'; await withAuthContext(createMockAuthContext({ subject: 'test-user' }), async () => { const ctx = getAuthContext(); assert.strictEqual(ctx?.subject, 'test-user'); }); ``` --- --- url: /en/api/@connectum/otel/shared/functions/wrapAsyncIterable.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [shared](../index.md) / wrapAsyncIterable # Function: wrapAsyncIterable() > **wrapAsyncIterable**<`T`>(`iterable`, `span`, `direction`, `recordMessages`, `endSpanOnComplete?`): `AsyncGenerator`<`T`> Defined in: [packages/otel/src/shared.ts:81](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L81) Wraps an AsyncIterable to track streaming messages with OTel span events. Captures the span via closure (not AsyncLocalStorage) to avoid the Node.js ALS context loss in async generators (nodejs/node#42237). When `endSpanOnComplete` is true, the span lifecycle is managed by the generator itself: the span is ended in the `finally` block, which runs on normal completion, error, or early break (generator.return()). ## Type Parameters ### T `T` ## Parameters ### iterable `AsyncIterable`<`T`> The source async iterable (streaming messages) ### span [`Span`](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.Span.html) The OTel span to record events on ### direction 'SENT' for outgoing, 'RECEIVED' for incoming messages `"SENT"` | `"RECEIVED"` ### recordMessages `boolean` Whether to record individual message events ### endSpanOnComplete? `boolean` = `false` Whether to end the span when the stream completes ## Returns `AsyncGenerator`<`T`> A new AsyncGenerator that yields the same messages with span events --- --- url: /en/guide/auth/gateway.md --- # Gateway Authentication `createGatewayAuthInterceptor` reads pre-authenticated identity from headers injected by an API gateway (Kong, Envoy, Traefik, etc.). The gateway has already verified the token -- the service only needs to extract the identity. ## Configuration ```typescript import { createGatewayAuthInterceptor } from '@connectum/auth'; const gatewayAuth = createGatewayAuthInterceptor({ headerMapping: { subject: 'x-user-id', name: 'x-user-name', roles: 'x-user-roles', }, trustSource: { header: 'x-gateway-secret', expectedValues: [process.env.GATEWAY_SECRET], }, }); ``` ### Options | Option | Type | Required | Description | |--------|------|----------|-------------| | `headerMapping` | `object` | Yes | Maps `AuthContext` fields to request header names | | `trustSource` | `object` | No | Verifies the request came from a trusted gateway | ### headerMapping The `headerMapping` object maps `AuthContext` fields to the header names your gateway uses: | Field | Expected header value | Example | |-------|----------------------|---------| | `subject` | User ID (string) | `x-user-id: user-123` | | `name` | Display name (string) | `x-user-name: John Doe` | | `roles` | Comma-separated roles | `x-user-roles: admin,editor` | | `scopes` | Comma-separated scopes | `x-user-scopes: read,write` | ### trustSource Check The `trustSource` check verifies that the request actually came from a trusted gateway, not a direct client spoofing headers: ```typescript trustSource: { header: 'x-gateway-secret', expectedValues: [process.env.GATEWAY_SECRET], } ``` If the header is missing or the value does not match any of the `expectedValues`, the interceptor throws `Unauthenticated`. ## Header Stripping Mapped headers and the trust header are **always stripped** from the request after extraction. This prevents downstream services or handlers from seeing (and potentially trusting) these headers if the request is forwarded. ## Full Example ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { createGatewayAuthInterceptor, createAuthzInterceptor } from '@connectum/auth'; const gatewayAuth = createGatewayAuthInterceptor({ headerMapping: { subject: 'x-user-id', name: 'x-user-name', roles: 'x-user-roles', }, trustSource: { header: 'x-gateway-secret', expectedValues: [process.env.GATEWAY_SECRET], }, }); const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'admin', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, ], }); const server = createServer({ services: [routes], interceptors: [...createDefaultInterceptors(), gatewayAuth, authz], }); await server.start(); ``` ## Related * [Auth Overview](/en/guide/auth) -- all authentication strategies * [JWT Authentication](/en/guide/auth/jwt) -- direct token verification * [Auth Context](/en/guide/auth/context) -- accessing identity in handlers * [@connectum/auth](/en/packages/auth) -- Package Guide * [@connectum/auth API](/en/api/@connectum/auth/) -- Full API Reference --- --- url: /en/guide/server/graceful-shutdown.md --- # Graceful Shutdown Connectum provides built-in graceful shutdown support that handles signal interception, connection draining, shutdown hooks with dependency ordering, and integration with Kubernetes lifecycle. ## Quick Setup ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true })], shutdown: { autoShutdown: true, // Handle SIGTERM/SIGINT automatically timeout: 30000, // 30 seconds to drain connections }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); server.on('stopping', () => { healthcheckManager.update(ServingStatus.NOT_SERVING); }); await server.start(); ``` ## Shutdown Options The `shutdown` option in `createServer()` accepts a `ShutdownOptions` object: ```typescript interface ShutdownOptions { /** Timeout in ms for graceful shutdown (default: 30000) */ timeout?: number; /** Signals to listen for (default: ['SIGTERM', 'SIGINT']) */ signals?: NodeJS.Signals[]; /** Auto-handle signals (default: false) */ autoShutdown?: boolean; /** Force close HTTP/2 sessions on timeout (default: true) */ forceCloseOnTimeout?: boolean; } ``` | Option | Default | Description | |--------|---------|-------------| | `timeout` | `30000` | Maximum time (ms) to wait for in-flight requests | | `signals` | `['SIGTERM', 'SIGINT']` | OS signals that trigger shutdown | | `autoShutdown` | `false` | Automatically install signal handlers | | `forceCloseOnTimeout` | `true` | Destroy HTTP/2 sessions if timeout exceeded | ## Shutdown Sequence When `server.stop()` is called (or a signal is received with `autoShutdown: true`), the following sequence executes: ``` 1. STOPPING event -- Notify listeners (update health check to NOT_SERVING) 2. Abort signal -- Signal streaming RPCs and long-running operations 3. Transport close -- Send GOAWAY, stop accepting new connections 4. Timeout race -- Wait for in-flight requests OR timeout 5. Force close -- If timeout + forceCloseOnTimeout: destroy all HTTP/2 sessions 6. Shutdown hooks -- Execute registered hooks in dependency order 7. Dispose -- Clean up internal state 8. STOP event -- Server is fully stopped ``` ## Shutdown Hooks Shutdown hooks allow you to run cleanup logic during shutdown with dependency ordering. Register them via `server.onShutdown()`. ### Anonymous Hooks ```typescript server.onShutdown(async () => { await db.close(); }); server.onShutdown(() => { console.log('Cleanup complete'); }); ``` ### Named Hooks Give hooks names for better logging and dependency management: ```typescript server.onShutdown('database', async () => { await db.close(); console.log('Database connections closed'); }); server.onShutdown('cache', async () => { await redis.quit(); console.log('Cache connections closed'); }); ``` ### Hooks with Dependency Ordering Specify dependencies to control execution order. Dependencies execute **first**: ```typescript // Database must shut down before the server's HTTP layer server.onShutdown('database', async () => { await db.close(); }); // Cache depends on database (database shuts down first) server.onShutdown('cache', ['database'], async () => { await redis.quit(); }); // Message queue depends on both database and cache server.onShutdown('message-queue', ['database', 'cache'], async () => { await mq.disconnect(); }); ``` Execution order: ``` 1. database (no dependencies, runs first) 2. cache (depends on database) 3. message-queue (depends on database + cache) ``` ::: warning Cycle detection The shutdown manager detects dependency cycles at registration time and throws an error: ```typescript server.onShutdown('a', ['b'], () => {}); server.onShutdown('b', ['a'], () => {}); // Throws: dependency cycle detected ``` ::: ### Multiple Handlers per Module You can register multiple handlers for the same named module. They run in parallel: ```typescript server.onShutdown('database', async () => { await primaryDb.close(); }); server.onShutdown('database', async () => { await replicaDb.close(); }); // Both database handlers run in parallel during shutdown ``` ## Automatic vs Manual Shutdown ### Automatic Shutdown With `autoShutdown: true`, the server installs signal handlers automatically: ```typescript const server = createServer({ services: [routes], shutdown: { autoShutdown: true, signals: ['SIGTERM', 'SIGINT'], // default timeout: 30000, }, }); await server.start(); // Server stops cleanly on SIGTERM or SIGINT (Ctrl+C) ``` ### Manual Shutdown Without `autoShutdown`, call `server.stop()` yourself: ```typescript const server = createServer({ services: [routes], // autoShutdown defaults to false }); await server.start(); // Manual shutdown handler process.on('SIGTERM', async () => { console.log('Received SIGTERM'); healthcheckManager.update(ServingStatus.NOT_SERVING); // Optional: wait for load balancers to drain await new Promise(resolve => setTimeout(resolve, 5000)); await server.stop(); process.exit(0); }); ``` ::: tip When to use manual shutdown Manual shutdown is useful when you need to perform actions **before** calling `server.stop()`, such as waiting for load balancer drain or notifying external services. ::: ### Idempotent stop() `server.stop()` is safe to call multiple times. Concurrent calls return the same Promise: ```typescript // Both resolve when the single shutdown completes await Promise.all([ server.stop(), server.stop(), ]); ``` ## Kubernetes Integration ### Recommended Configuration For Kubernetes deployments, combine graceful shutdown with health checks and a pre-stop hook: ```typescript const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true })], shutdown: { autoShutdown: true, timeout: 25000, // Less than Kubernetes terminationGracePeriodSeconds }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); server.on('stopping', () => { healthcheckManager.update(ServingStatus.NOT_SERVING); }); ``` ### Pod Specification ```yaml apiVersion: v1 kind: Pod spec: terminationGracePeriodSeconds: 30 # Must be > shutdown.timeout containers: - name: my-service image: my-service:latest ports: - containerPort: 5000 readinessProbe: httpGet: path: /healthz port: 5000 periodSeconds: 5 lifecycle: preStop: exec: # Give load balancers time to remove this pod command: ["sleep", "5"] ``` ### Shutdown Timeline ``` 0s SIGTERM received (Kubernetes sends SIGTERM) 0s 'stopping' event -> healthcheckManager.update(NOT_SERVING) 0-5s Kubernetes removes pod from service endpoints 5s In-flight requests drain 25s Shutdown timeout (forceCloseOnTimeout: true) 25s Shutdown hooks execute 25s 'stop' event 30s Kubernetes terminationGracePeriodSeconds (hard kill) ``` ::: danger Critical Always set `shutdown.timeout` to a value **less than** Kubernetes `terminationGracePeriodSeconds`. Otherwise, Kubernetes may SIGKILL the process before your shutdown hooks complete. ::: ## Timeout and Force Close Behavior ### With forceCloseOnTimeout: true (default) When the timeout is exceeded, all active HTTP/2 sessions are destroyed: ```typescript shutdown: { timeout: 30000, forceCloseOnTimeout: true, // default } ``` This ensures the server stops within the timeout, even if clients hold connections open. ### With forceCloseOnTimeout: false The server waits indefinitely for all in-flight requests to complete. Shutdown hooks still execute after the timeout: ```typescript shutdown: { timeout: 30000, forceCloseOnTimeout: false, } ``` ::: warning With `forceCloseOnTimeout: false`, the server may hang if a client holds a connection open indefinitely. Use only when you control all clients and can guarantee they will close connections. ::: ## Complete Production Example ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { shutdownProvider } from '@connectum/otel'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: createDefaultInterceptors(), shutdown: { autoShutdown: true, timeout: 25000, forceCloseOnTimeout: true, }, }); // Register shutdown hooks with dependencies server.onShutdown('database', async () => { await db.close(); }); server.onShutdown('cache', async () => { await redis.quit(); }); server.onShutdown('otel', ['database', 'cache'], async () => { await shutdownProvider(); }); // Lifecycle hooks server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); server.on('stopping', () => { healthcheckManager.update(ServingStatus.NOT_SERVING); }); server.on('stop', () => { console.log('Server stopped'); }); server.on('error', (err) => { console.error('Server error:', err); }); await server.start(); ``` ## Related * [Server Overview](/en/guide/server) -- quick start and key concepts * [Lifecycle](/en/guide/server/lifecycle) -- states, events, and the shutdownSignal * [Health Checks & Kubernetes](/en/guide/health-checks) -- configure health monitoring * [Configuration](/en/guide/server/configuration) -- environment variables and TLS * [@connectum/core](/en/packages/core) -- Package Guide * [@connectum/core API](/en/api/@connectum/core/) -- Full API Reference --- --- url: /en/guide/health-checks/protocol.md --- # Health Check Protocol Connectum implements the [gRPC Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) with additional HTTP endpoints for load balancers and Kubernetes integration. ## Configuration The `Healthcheck()` factory function accepts the following options: ```typescript Healthcheck({ httpEnabled: true, // Enable HTTP health endpoints httpPaths: ['/healthz', '/health', '/readyz'], // HTTP endpoint paths watchInterval: 500, // Watch polling interval (ms) manager: customManager, // Custom manager instance }) ``` | Option | Type | Default | Description | |--------|------|---------|-------------| | `httpEnabled` | `boolean` | `false` | Enable HTTP health endpoints | | `httpPaths` | `string[]` | `['/healthz', '/health', '/readyz']` | HTTP endpoint paths | | `watchInterval` | `number` | `500` | Polling interval for Watch streaming (ms) | | `manager` | `HealthcheckManager` | `healthcheckManager` (singleton) | Custom manager for tests or multi-server setups | ## The healthcheckManager Singleton The `healthcheckManager` is a global singleton you import from anywhere in your application to update service health status: ```typescript import { healthcheckManager, ServingStatus } from '@connectum/healthcheck'; // Update overall health status healthcheckManager.update(ServingStatus.SERVING); // Update a specific service's health status healthcheckManager.update(ServingStatus.SERVING, 'my.service.v1.MyService'); // Mark as unhealthy healthcheckManager.update(ServingStatus.NOT_SERVING); ``` ### ServingStatus Values | Status | Value | Description | |--------|-------|-------------| | `UNKNOWN` | `0` | Status is unknown (initial state) | | `SERVING` | `1` | Service is healthy and accepting requests | | `NOT_SERVING` | `2` | Service is unhealthy or draining connections | | `SERVICE_UNKNOWN` | `3` | Requested service is not registered | ### Manager Methods | Method | Description | |--------|-------------| | `update(status, service?)` | Update status. Without `service`, updates all registered services. | | `getStatus(service)` | Get status of a specific service | | `getAllStatuses()` | Get a Map of all service statuses | | `areAllHealthy()` | Check if all services report `SERVING` | | `initialize(serviceNames)` | Register services for tracking (merge with existing state) | | `clear()` | Clear all registered services | ## gRPC Health Check Protocol The package implements three gRPC methods on the `grpc.health.v1.Health` service: ### Health.Check Returns the current health status: ```bash # Check overall health grpcurl -plaintext localhost:5000 grpc.health.v1.Health/Check # Check a specific service grpcurl -plaintext \ -d '{"service": "my.service.v1.MyService"}' \ localhost:5000 grpc.health.v1.Health/Check ``` ### Health.Watch Streams health status changes in real time: ```bash grpcurl -plaintext localhost:5000 grpc.health.v1.Health/Watch ``` Behavior follows the gRPC specification: * Immediately sends the current status * Sends updates only when the status changes * For unknown services, sends `SERVICE_UNKNOWN` (does not terminate the call) * Terminates when the client disconnects ### Health.List Lists all registered services with their statuses: ```bash grpcurl -plaintext localhost:5000 grpc.health.v1.Health/List ``` ## HTTP Health Endpoints When `httpEnabled: true`, the following HTTP endpoints are available: ### Default Endpoints | Path | Description | |------|-------------| | `/healthz` | Overall health status | | `/health` | Overall health status (alias) | | `/readyz` | Readiness status | ### Response Format ```json { "status": "SERVING", "service": "overall", "timestamp": "2026-02-12T10:30:00.000Z" } ``` ### HTTP Status Codes | ServingStatus | HTTP Code | |---------------|-----------| | `SERVING` | `200 OK` | | `NOT_SERVING` | `503 Service Unavailable` | | `UNKNOWN` | `503 Service Unavailable` | | `SERVICE_UNKNOWN` | `404 Not Found` | ### Check a Specific Service via HTTP ```bash curl http://localhost:5000/healthz?service=my.service.v1.MyService ``` ### Custom Paths ```typescript Healthcheck({ httpEnabled: true, httpPaths: ['/healthz', '/ready', '/live'], }) ``` ## Custom Dependency Health Checks Monitor downstream dependencies (databases, external APIs) alongside your service health: ```typescript import { healthcheckManager, ServingStatus } from '@connectum/healthcheck'; // Initialize tracking for your dependencies healthcheckManager.initialize([ 'my.service.v1.MyService', 'dependency.database', 'dependency.cache', ]); // Periodically check database health setInterval(async () => { try { await db.ping(); healthcheckManager.update(ServingStatus.SERVING, 'dependency.database'); } catch { healthcheckManager.update(ServingStatus.NOT_SERVING, 'dependency.database'); } }, 10000); // Check cache health setInterval(async () => { try { await redis.ping(); healthcheckManager.update(ServingStatus.SERVING, 'dependency.cache'); } catch { healthcheckManager.update(ServingStatus.NOT_SERVING, 'dependency.cache'); } }, 10000); ``` Use `areAllHealthy()` for aggregate health status: ```bash curl http://localhost:5000/healthz # Returns 503 if ANY dependency reports NOT_SERVING ``` ## Isolated Manager for Testing Use `createHealthcheckManager()` to create isolated instances for tests or multi-server setups: ```typescript import { describe, it } from 'node:test'; import assert from 'node:assert'; import { Healthcheck, createHealthcheckManager, ServingStatus, } from '@connectum/healthcheck'; import { createServer } from '@connectum/core'; describe('health check', () => { it('should track service status', () => { const manager = createHealthcheckManager(); manager.initialize(['my.service.v1.MyService']); manager.update(ServingStatus.SERVING, 'my.service.v1.MyService'); assert.ok(manager.areAllHealthy()); manager.update(ServingStatus.NOT_SERVING, 'my.service.v1.MyService'); assert.ok(!manager.areAllHealthy()); }); it('should work with createServer', async () => { const manager = createHealthcheckManager(); const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true, manager })], }); server.on('ready', () => { manager.update(ServingStatus.SERVING); }); await server.start(); // ... test assertions ... await server.stop(); }); }); ``` ## Related * [Health Checks Overview](/en/guide/health-checks) -- back to overview * [Kubernetes Integration](/en/guide/health-checks/kubernetes) -- probes, graceful shutdown, shutdown timeline * [@connectum/healthcheck](/en/packages/healthcheck) -- Package Guide * [@connectum/healthcheck API](/en/api/@connectum/healthcheck/) -- Full API Reference --- --- url: /en/guide/health-checks.md --- # Health Checks gRPC Health Checking Protocol implementation with HTTP endpoints for Kubernetes liveness and readiness probes. ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true })], }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ## Installation ```bash pnpm add @connectum/healthcheck ``` ## Key Concepts | Concept | Description | |---------|-------------| | **ServingStatus** | `UNKNOWN` (0), `SERVING` (1), `NOT_SERVING` (2), `SERVICE_UNKNOWN` (3) | | **gRPC Protocol** | `Health/Check`, `Health/Watch`, `Health/List` on `grpc.health.v1.Health` | | **HTTP Endpoints** | `/healthz`, `/health`, `/readyz` -- returns JSON with status and HTTP 200/503/404 | | **healthcheckManager** | Global singleton to update service status from anywhere in your app | | **Dependency Checks** | Track downstream services (database, cache) alongside your service health | ## Learn More * [Protocol Details](/en/guide/health-checks/protocol) -- gRPC methods, HTTP endpoints, configuration options, dependency checks * [Kubernetes Integration](/en/guide/health-checks/kubernetes) -- HTTP/gRPC probes, graceful shutdown integration, shutdown timeline * [@connectum/healthcheck](/en/packages/healthcheck) -- Package Guide * [@connectum/healthcheck API](/en/api/@connectum/healthcheck/) -- Full API Reference --- --- url: /en/api/@connectum/otel/interceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / interceptor # interceptor ConnectRPC OpenTelemetry interceptor Creates a ConnectRPC interceptor that instruments RPC calls with OpenTelemetry tracing and metrics following semantic conventions. ## See * https://opentelemetry.io/docs/specs/semconv/rpc/connect-rpc/ * https://opentelemetry.io/docs/specs/semconv/rpc/rpc-metrics/ ## Functions * [createOtelInterceptor](functions/createOtelInterceptor.md) --- --- url: /en/guide/interceptors.md --- # Interceptors Interceptors are the primary mechanism for cross-cutting concerns in Connectum. They wrap RPC calls at the transport level -- adding error handling, timeouts, retries, validation, and more -- without touching business logic. ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, interceptors: createDefaultInterceptors({ timeout: { duration: 10_000 }, retry: { maxRetries: 5 }, }), shutdown: { autoShutdown: true }, }); await server.start(); ``` ## Key Concepts ### How Interceptors Work A ConnectRPC interceptor is a function that receives `next` and returns a handler. The handler gets control before and after each request, forming a layered pipeline: ``` Request -> interceptor1 -> interceptor2 -> ... -> handler Response <- interceptor1 <- interceptor2 <- ... <- handler ``` ### Built-in Chain `createDefaultInterceptors()` provides 8 production-ready interceptors in a fixed order: | # | Interceptor | Purpose | Default | |---|-------------|---------|---------| | 1 | **errorHandler** | Normalizes errors into ConnectError | Enabled | | 2 | **timeout** | Limits request execution time | Enabled (30s) | | 3 | **bulkhead** | Limits concurrent requests | Enabled (capacity 10, queue 10) | | 4 | **circuitBreaker** | Prevents cascading failures | Enabled (threshold 5) | | 5 | **retry** | Retries transient failures with exponential backoff | Enabled (3 retries) | | 6 | **fallback** | Graceful degradation | Disabled | | 7 | **validation** | Validates via @connectrpc/validate | Enabled | | 8 | **serializer** | JSON serialization for protobuf | Enabled | ### Per-Method Routing Three approaches for applying interceptors selectively: | Scenario | Approach | |----------|----------| | Interceptor bound to a specific service router | ConnectRPC native (`router.service()`) | | Declarative routing by pattern | `createMethodFilterInterceptor` | | Dynamic logic, filtering by request content | Custom interceptor | ## Learn More * [Built-in Interceptors](/en/guide/interceptors/built-in) -- detailed chain reference, customization, and standalone usage * [Custom Interceptors](/en/guide/interceptors/custom) -- factory pattern, error handling, testing * [Method Filtering](/en/guide/interceptors/method-filtering) -- per-service and per-method routing * [Auth & Authz](/en/guide/auth) -- authentication and authorization interceptors * [@connectum/interceptors](/en/packages/interceptors) -- Package Guide * [@connectum/interceptors API](/en/api/@connectum/interceptors/) -- Full API Reference --- --- url: /en/api/@connectum/auth/interfaces/AuthContext.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AuthContext # Interface: AuthContext Defined in: [packages/auth/src/types.ts:22](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L22) Authenticated user context Represents the result of authentication. Set by auth interceptor, accessible via getAuthContext() in handlers and downstream interceptors. ## Properties ### claims > `readonly` **claims**: `Readonly`<`Record`<`string`, `unknown`>> Defined in: [packages/auth/src/types.ts:32](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L32) Raw claims from the credential (JWT claims, API key metadata, etc.) *** ### expiresAt? > `readonly` `optional` **expiresAt**: `Date` Defined in: [packages/auth/src/types.ts:36](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L36) Credential expiration time *** ### name? > `readonly` `optional` **name**: `string` Defined in: [packages/auth/src/types.ts:26](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L26) Human-readable display name *** ### roles > `readonly` **roles**: readonly `string`\[] Defined in: [packages/auth/src/types.ts:28](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L28) Assigned roles (e.g., \["admin", "user"]) *** ### scopes > `readonly` **scopes**: readonly `string`\[] Defined in: [packages/auth/src/types.ts:30](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L30) Granted scopes (e.g., \["read", "write"]) *** ### subject > `readonly` **subject**: `string` Defined in: [packages/auth/src/types.ts:24](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L24) Authenticated subject identifier (user ID, service account, etc.) *** ### type > `readonly` **type**: `string` Defined in: [packages/auth/src/types.ts:34](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L34) Credential type identifier (e.g., "jwt", "api-key", "mtls") --- --- url: /en/api/@connectum/auth/interfaces/AuthInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AuthInterceptorOptions # Interface: AuthInterceptorOptions Defined in: [packages/auth/src/types.ts:115](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L115) Generic auth interceptor options ## Properties ### cache? > `optional` **cache**: [`CacheOptions`](CacheOptions.md) Defined in: [packages/auth/src/types.ts:151](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L151) LRU cache for credentials verification results. Caches AuthContext by credential string to reduce verification overhead. *** ### extractCredentials()? > `optional` **extractCredentials**: (`req`) => `string` | `Promise`<`string` | `null`> | `null` Defined in: [packages/auth/src/types.ts:123](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L123) Extract credentials from request. Default: extracts Bearer token from Authorization header. #### Parameters ##### req Request with headers ###### header `Headers` #### Returns `string` | `Promise`<`string` | `null`> | `null` Credential string or null if no credentials found *** ### propagatedClaims? > `optional` **propagatedClaims**: `string`\[] Defined in: [packages/auth/src/types.ts:158](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L158) Filter which claims are propagated in headers (SEC-001). When set, only listed claim keys are included in x-auth-claims header. When not set, all claims are propagated. *** ### propagateHeaders? > `optional` **propagateHeaders**: `boolean` Defined in: [packages/auth/src/types.ts:145](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L145) Propagate auth context as headers for downstream services. #### Default ```ts false ``` *** ### skipMethods? > `optional` **skipMethods**: `string`\[] Defined in: [packages/auth/src/types.ts:139](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L139) Methods to skip authentication for. Patterns: "Service/Method" or "Service/\*" #### Default ```ts [] (health and reflection methods are NOT auto-skipped) ``` *** ### verifyCredentials() > **verifyCredentials**: (`credentials`) => [`AuthContext`](AuthContext.md) | `Promise`<[`AuthContext`](AuthContext.md)> Defined in: [packages/auth/src/types.ts:132](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L132) Verify credentials and return auth context. REQUIRED. Must throw on invalid credentials. #### Parameters ##### credentials `string` Extracted credential string #### Returns [`AuthContext`](AuthContext.md) | `Promise`<[`AuthContext`](AuthContext.md)> AuthContext for valid credentials --- --- url: /en/api/@connectum/auth/interfaces/AuthzDeniedDetails.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AuthzDeniedDetails # Interface: AuthzDeniedDetails Defined in: [packages/auth/src/errors.ts:14](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L14) Details for authorization denied errors. ## Properties ### requiredRoles? > `readonly` `optional` **requiredRoles**: readonly `string`\[] Defined in: [packages/auth/src/errors.ts:16](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L16) *** ### requiredScopes? > `readonly` `optional` **requiredScopes**: readonly `string`\[] Defined in: [packages/auth/src/errors.ts:17](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L17) *** ### ruleName > `readonly` **ruleName**: `string` Defined in: [packages/auth/src/errors.ts:15](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/errors.ts#L15) --- --- url: /en/api/@connectum/auth/interfaces/AuthzInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AuthzInterceptorOptions # Interface: AuthzInterceptorOptions Defined in: [packages/auth/src/types.ts:244](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L244) Authorization interceptor options ## Properties ### authorize()? > `optional` **authorize**: (`context`, `req`) => `boolean` | `Promise`<`boolean`> Defined in: [packages/auth/src/types.ts:266](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L266) Programmatic authorization callback. Called after rule evaluation if no rule matched, or always if no rules are defined. #### Parameters ##### context [`AuthContext`](AuthContext.md) Authenticated user context ##### req Request info (service and method names) ###### method `string` ###### service `string` #### Returns `boolean` | `Promise`<`boolean`> true if authorized, false otherwise *** ### defaultPolicy? > `optional` **defaultPolicy**: [`AuthzEffect`](../type-aliases/AuthzEffect.md) Defined in: [packages/auth/src/types.ts:249](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L249) Default policy when no rule matches. #### Default ```ts "deny" ``` *** ### rules? > `optional` **rules**: [`AuthzRule`](AuthzRule.md)\[] Defined in: [packages/auth/src/types.ts:255](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L255) Declarative authorization rules. Evaluated in order; first matching rule wins. *** ### skipMethods? > `optional` **skipMethods**: `string`\[] Defined in: [packages/auth/src/types.ts:272](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L272) Methods to skip authorization for. #### Default ```ts [] ``` --- --- url: /en/api/@connectum/auth/interfaces/AuthzRule.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AuthzRule # Interface: AuthzRule Defined in: [packages/auth/src/types.ts:81](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L81) Authorization rule definition. When a rule has `requires`, the match semantics are: * **roles**: "any-of" -- the user must have **at least one** of the listed roles. * **scopes**: "all-of" -- the user must have **every** listed scope. ## Properties ### effect > `readonly` **effect**: [`AuthzEffect`](../type-aliases/AuthzEffect.md) Defined in: [packages/auth/src/types.ts:87](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L87) Effect when rule matches *** ### methods > `readonly` **methods**: readonly `string`\[] Defined in: [packages/auth/src/types.ts:85](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L85) Method patterns to match (e.g., "admin.v1.AdminService/\*", "user.v1.UserService/DeleteUser") *** ### name > `readonly` **name**: `string` Defined in: [packages/auth/src/types.ts:83](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L83) Rule name for logging/debugging *** ### requires? > `readonly` `optional` **requires**: `object` Defined in: [packages/auth/src/types.ts:94](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L94) Required roles/scopes for this rule. * `roles` uses "any-of" semantics: user needs at least one of the listed roles. * `scopes` uses "all-of" semantics: user needs every listed scope. #### roles? > `readonly` `optional` **roles**: readonly `string`\[] #### scopes? > `readonly` `optional` **scopes**: readonly `string`\[] --- --- url: /en/api/@connectum/otel/shared/interfaces/BaseAttributeParams.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [shared](../index.md) / BaseAttributeParams # Interface: BaseAttributeParams Defined in: [packages/otel/src/shared.ts:145](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L145) Parameters for building base RPC attributes. ## Properties ### method > **method**: `string` Defined in: [packages/otel/src/shared.ts:147](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L147) *** ### serverAddress > **serverAddress**: `string` Defined in: [packages/otel/src/shared.ts:148](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L148) *** ### serverPort? > `optional` **serverPort**: `number` Defined in: [packages/otel/src/shared.ts:149](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L149) *** ### service > **service**: `string` Defined in: [packages/otel/src/shared.ts:146](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/shared.ts#L146) --- --- url: /en/api/@connectum/otel/interfaces/BatchSpanProcessorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / BatchSpanProcessorOptions # Interface: BatchSpanProcessorOptions Defined in: [packages/otel/src/config.ts:48](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L48) Batch span processor options ## Properties ### exportTimeoutMillis > **exportTimeoutMillis**: `number` Defined in: [packages/otel/src/config.ts:52](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L52) *** ### maxExportBatchSize > **maxExportBatchSize**: `number` Defined in: [packages/otel/src/config.ts:49](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L49) *** ### maxQueueSize > **maxQueueSize**: `number` Defined in: [packages/otel/src/config.ts:50](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L50) *** ### scheduledDelayMillis > **scheduledDelayMillis**: `number` Defined in: [packages/otel/src/config.ts:51](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L51) --- --- url: /en/api/@connectum/interceptors/interfaces/BulkheadOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / BulkheadOptions # Interface: BulkheadOptions Defined in: [types.ts:166](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L166) Bulkhead interceptor options ## Properties ### capacity? > `optional` **capacity**: `number` Defined in: [types.ts:171](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L171) Maximum number of concurrent requests #### Default ```ts 10 ``` *** ### queueSize? > `optional` **queueSize**: `number` Defined in: [types.ts:177](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L177) Maximum queue size for pending requests #### Default ```ts 10 ``` *** ### skipStreaming? > `optional` **skipStreaming**: `boolean` Defined in: [types.ts:183](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L183) Skip bulkhead for streaming calls #### Default ```ts true ``` --- --- url: /en/api/@connectum/auth/interfaces/CacheOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / CacheOptions # Interface: CacheOptions Defined in: [packages/auth/src/types.ts:105](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L105) LRU cache configuration for credentials verification ## Properties ### maxSize? > `readonly` `optional` **maxSize**: `number` Defined in: [packages/auth/src/types.ts:109](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L109) Maximum number of cached entries *** ### ttl > `readonly` **ttl**: `number` Defined in: [packages/auth/src/types.ts:107](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L107) Cache entry time-to-live in milliseconds --- --- url: /en/api/@connectum/interceptors/interfaces/CircuitBreakerOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / CircuitBreakerOptions # Interface: CircuitBreakerOptions Defined in: [types.ts:126](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L126) Circuit breaker interceptor options ## Properties ### halfOpenAfter? > `optional` **halfOpenAfter**: `number` Defined in: [types.ts:137](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L137) Time in milliseconds to wait before attempting to close circuit #### Default ```ts 30000 (30 seconds) ``` *** ### skipStreaming? > `optional` **skipStreaming**: `boolean` Defined in: [types.ts:143](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L143) Skip circuit breaker for streaming calls #### Default ```ts true ``` *** ### threshold? > `optional` **threshold**: `number` Defined in: [types.ts:131](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L131) Number of consecutive failures before opening circuit #### Default ```ts 5 ``` --- --- url: /en/api/@connectum/otel/interfaces/CollectorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / CollectorOptions # Interface: CollectorOptions Defined in: [packages/otel/src/config.ts:40](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L40) Collector endpoint options ## Properties ### concurrencyLimit > **concurrencyLimit**: `number` Defined in: [packages/otel/src/config.ts:41](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L41) *** ### url > **url**: `string` | `undefined` Defined in: [packages/otel/src/config.ts:42](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L42) --- --- url: /en/api/@connectum/core/types/interfaces/CreateServerOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / CreateServerOptions # Interface: CreateServerOptions Defined in: [packages/core/src/types.ts:192](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L192) Server configuration options for createServer() ## Properties ### allowHTTP1? > `optional` **allowHTTP1**: `boolean` Defined in: [packages/core/src/types.ts:252](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L252) Allow HTTP/1.1 connections. With TLS: enables ALPN negotiation (both HTTP/1.1 and HTTP/2). Without TLS: creates HTTP/1.1 server (http.createServer). Set to false without TLS for h2c-only (http2.createServer). #### Default ```ts true ``` *** ### handshakeTimeout? > `optional` **handshakeTimeout**: `number` Defined in: [packages/core/src/types.ts:258](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L258) Handshake timeout in milliseconds #### Default ```ts 30000 ``` *** ### host? > `optional` **host**: `string` Defined in: [packages/core/src/types.ts:208](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L208) Server host to bind #### Default ```ts "0.0.0.0" ``` *** ### http2Options? > `optional` **http2Options**: `SecureServerOptions`<*typeof* `IncomingMessage`, *typeof* `ServerResponse`, *typeof* `Http2ServerRequest`, *typeof* `Http2ServerResponse`> Defined in: [packages/core/src/types.ts:263](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L263) Additional HTTP/2 server options *** ### interceptors? > `optional` **interceptors**: `Interceptor`\[] Defined in: [packages/core/src/types.ts:241](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L241) ConnectRPC interceptors. When omitted or `[]`, no interceptors are applied. Use `createDefaultInterceptors()` from `@connectum/interceptors` to get the default chain. *** ### port? > `optional` **port**: `number` Defined in: [packages/core/src/types.ts:202](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L202) Server port #### Default ```ts 5000 ``` *** ### protocols? > `optional` **protocols**: [`ProtocolRegistration`](ProtocolRegistration.md)\[] Defined in: [packages/core/src/types.ts:229](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L229) Protocol registrations (healthcheck, reflection, custom) #### Example ```typescript import { Healthcheck } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true }), Reflection()], }); ``` *** ### services > **services**: [`ServiceRoute`](../type-aliases/ServiceRoute.md)\[] Defined in: [packages/core/src/types.ts:196](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L196) Service routes to register *** ### shutdown? > `optional` **shutdown**: [`ShutdownOptions`](ShutdownOptions.md) Defined in: [packages/core/src/types.ts:234](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L234) Graceful shutdown configuration *** ### tls? > `optional` **tls**: [`TLSOptions`](TLSOptions.md) Defined in: [packages/core/src/types.ts:213](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L213) TLS configuration --- --- url: >- /en/api/@connectum/interceptors/defaults/interfaces/DefaultInterceptorOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [defaults](../index.md) / DefaultInterceptorOptions # Interface: DefaultInterceptorOptions Defined in: [defaults.ts:33](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L33) Configuration options for the default interceptor chain. Each interceptor can be: * `false` to disable it entirely * `true` to enable with default options * An options object to enable with custom configuration All interceptors are enabled by default except fallback (which requires a handler function). ## Properties ### bulkhead? > `optional` **bulkhead**: `boolean` | [`BulkheadOptions`](../../interfaces/BulkheadOptions.md) Defined in: [defaults.ts:53](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L53) Bulkhead interceptor. Limits concurrent requests to prevent resource exhaustion. #### Default ```ts true (10/10) ``` *** ### circuitBreaker? > `optional` **circuitBreaker**: `boolean` | [`CircuitBreakerOptions`](../../interfaces/CircuitBreakerOptions.md) Defined in: [defaults.ts:60](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L60) Circuit breaker interceptor. Prevents cascading failures by breaking circuit on consecutive errors. #### Default ```ts true (5 failures) ``` *** ### errorHandler? > `optional` **errorHandler**: `boolean` | [`ErrorHandlerOptions`](../../interfaces/ErrorHandlerOptions.md) Defined in: [defaults.ts:39](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L39) Error handler interceptor (first in chain). Transforms errors into ConnectError with proper codes. #### Default ```ts true ``` *** ### fallback? > `optional` **fallback**: `boolean` | [`FallbackOptions`](../../interfaces/FallbackOptions.md)<`unknown`> Defined in: [defaults.ts:75](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L75) Fallback interceptor. Provides graceful degradation when service fails. Disabled by default — requires a handler function. #### Default ```ts false ``` *** ### retry? > `optional` **retry**: `boolean` | [`RetryOptions`](../../interfaces/RetryOptions.md) Defined in: [defaults.ts:67](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L67) Retry interceptor. Retries transient failures with exponential backoff. #### Default ```ts true (3 retries) ``` *** ### serializer? > `optional` **serializer**: `boolean` | [`SerializerOptions`](../../interfaces/SerializerOptions.md) Defined in: [defaults.ts:89](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L89) Serializer interceptor (last in chain). Auto JSON serialization for ConnectRPC responses. #### Default ```ts true ``` *** ### timeout? > `optional` **timeout**: `boolean` | [`TimeoutOptions`](../../interfaces/TimeoutOptions.md) Defined in: [defaults.ts:46](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L46) Timeout interceptor. Enforces request deadline before any processing. #### Default ```ts true (30s) ``` *** ### validation? > `optional` **validation**: `boolean` Defined in: [defaults.ts:82](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/defaults.ts#L82) Validation interceptor. Validates request messages using @connectrpc/validate. #### Default ```ts true ``` --- --- url: /en/api/@connectum/interceptors/interfaces/ErrorHandlerOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / ErrorHandlerOptions # Interface: ErrorHandlerOptions Defined in: [types.ts:21](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L21) Error handler interceptor options ## Properties ### includeStackTrace? > `optional` **includeStackTrace**: `boolean` Defined in: [types.ts:33](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L33) Include stack trace in logs #### Default ```ts process.env.NODE_ENV !== "production" ``` *** ### ~~logErrors?~~ > `optional` **logErrors**: `boolean` Defined in: [types.ts:27](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L27) Log errors to console. #### Default ```ts process.env.NODE_ENV !== "production" ``` #### Deprecated Use onError callback instead *** ### onError()? > `optional` **onError**: (`info`) => `void` Defined in: [types.ts:39](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L39) Callback for error logging. Replaces console.error when provided. Receives rich error info including serverDetails from SanitizableError. #### Parameters ##### info ###### code `number` ###### error `Error` ###### serverDetails? `Readonly`<`Record`<`string`, `unknown`>> ###### stack? `string` #### Returns `void` --- --- url: /en/api/@connectum/interceptors/interfaces/FallbackOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / FallbackOptions # Interface: FallbackOptions\ Defined in: [types.ts:189](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L189) Fallback interceptor options ## Type Parameters ### T `T` = `unknown` ## Properties ### handler() > **handler**: (`error`) => `T` | `Promise`<`T`> Defined in: [types.ts:193](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L193) Fallback function to call on error #### Parameters ##### error `Error` #### Returns `T` | `Promise`<`T`> *** ### skipStreaming? > `optional` **skipStreaming**: `boolean` Defined in: [types.ts:199](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L199) Skip fallback for streaming calls #### Default ```ts true ``` --- --- url: /en/api/@connectum/auth/interfaces/GatewayAuthInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / GatewayAuthInterceptorOptions # Interface: GatewayAuthInterceptorOptions Defined in: [packages/auth/src/types.ts:301](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L301) Gateway auth interceptor options. For services behind an API gateway that has already performed authentication. Extracts auth context from gateway-injected headers. ## Properties ### defaultType? > `readonly` `optional` **defaultType**: `string` Defined in: [packages/auth/src/types.ts:318](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L318) Default credential type when not provided by gateway *** ### headerMapping > `readonly` **headerMapping**: [`GatewayHeaderMapping`](GatewayHeaderMapping.md) Defined in: [packages/auth/src/types.ts:303](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L303) Mapping from AuthContext fields to gateway header names *** ### propagateHeaders? > `readonly` `optional` **propagateHeaders**: `boolean` Defined in: [packages/auth/src/types.ts:316](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L316) Propagate auth context as headers for downstream services *** ### skipMethods? > `readonly` `optional` **skipMethods**: `string`\[] Defined in: [packages/auth/src/types.ts:314](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L314) Methods to skip authentication for *** ### stripHeaders? > `readonly` `optional` **stripHeaders**: `string`\[] Defined in: [packages/auth/src/types.ts:312](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L312) Headers to strip from the request after extraction (prevent spoofing) *** ### trustSource > `readonly` **trustSource**: `object` Defined in: [packages/auth/src/types.ts:305](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L305) Trust verification: check that request came from a trusted gateway #### expectedValues > `readonly` **expectedValues**: `string`\[] Accepted values for the trust header #### header > `readonly` **header**: `string` Header set by the gateway to prove trust --- --- url: /en/api/@connectum/auth/interfaces/GatewayHeaderMapping.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / GatewayHeaderMapping # Interface: GatewayHeaderMapping Defined in: [packages/auth/src/types.ts:280](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L280) Header name mapping for gateway auth context extraction. Maps AuthContext fields to custom header names used by the API gateway. ## Properties ### claims? > `readonly` `optional` **claims**: `string` Defined in: [packages/auth/src/types.ts:292](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L292) Header containing JSON-encoded claims *** ### name? > `readonly` `optional` **name**: `string` Defined in: [packages/auth/src/types.ts:284](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L284) Header containing the display name *** ### roles? > `readonly` `optional` **roles**: `string` Defined in: [packages/auth/src/types.ts:286](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L286) Header containing JSON-encoded roles array *** ### scopes? > `readonly` `optional` **scopes**: `string` Defined in: [packages/auth/src/types.ts:288](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L288) Header containing space-separated scopes *** ### subject > `readonly` **subject**: `string` Defined in: [packages/auth/src/types.ts:282](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L282) Header containing the authenticated subject *** ### type? > `readonly` `optional` **type**: `string` Defined in: [packages/auth/src/types.ts:290](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L290) Header containing credential type --- --- url: /en/api/@connectum/healthcheck/interfaces/HealthcheckOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / HealthcheckOptions # Interface: HealthcheckOptions Defined in: [types.ts:27](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L27) Healthcheck protocol options ## Properties ### httpEnabled? > `optional` **httpEnabled**: `boolean` Defined in: [types.ts:32](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L32) Enable HTTP health endpoints #### Default ```ts false ``` *** ### httpPaths? > `optional` **httpPaths**: `string`\[] Defined in: [types.ts:38](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L38) HTTP health endpoint paths that all respond with health status. #### Default ```ts ["/healthz", "/health", "/readyz"] ``` *** ### manager? > `optional` **manager**: [`HealthcheckManager`](../classes/HealthcheckManager.md) Defined in: [types.ts:51](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L51) Custom HealthcheckManager instance. Useful for testing or running multiple servers in one process. When not provided, uses the default module-level singleton. *** ### watchInterval? > `optional` **watchInterval**: `number` Defined in: [types.ts:44](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L44) Watch interval in milliseconds for streaming health updates #### Default ```ts 500 ``` --- --- url: /en/api/@connectum/auth/interfaces/JwtAuthInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / JwtAuthInterceptorOptions # Interface: JwtAuthInterceptorOptions Defined in: [packages/auth/src/types.ts:164](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L164) JWT auth interceptor options ## Properties ### algorithms? > `optional` **algorithms**: `string`\[] Defined in: [packages/auth/src/types.ts:210](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L210) Allowed algorithms *** ### audience? > `optional` **audience**: `string` | `string`\[] Defined in: [packages/auth/src/types.ts:208](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L208) Expected audience(s) *** ### claimsMapping? > `optional` **claimsMapping**: `object` Defined in: [packages/auth/src/types.ts:215](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L215) Mapping from JWT claims to AuthContext fields. Supports dot-notation paths (e.g., "realm\_access.roles"). #### name? > `optional` **name**: `string` #### roles? > `optional` **roles**: `string` #### scopes? > `optional` **scopes**: `string` #### subject? > `optional` **subject**: `string` *** ### issuer? > `optional` **issuer**: `string` | `string`\[] Defined in: [packages/auth/src/types.ts:206](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L206) Expected issuer(s) *** ### jwksUri? > `optional` **jwksUri**: `string` Defined in: [packages/auth/src/types.ts:166](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L166) JWKS endpoint URL for remote key set *** ### maxTokenAge? > `optional` **maxTokenAge**: `string` | `number` Defined in: [packages/auth/src/types.ts:228](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L228) Maximum token age. Passed to jose jwtVerify options. Number (seconds) or string (e.g., "2h", "7d"). *** ### propagateHeaders? > `optional` **propagateHeaders**: `boolean` Defined in: [packages/auth/src/types.ts:238](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L238) Propagate auth context as headers for downstream services. #### Default ```ts false ``` *** ### publicKey? > `optional` **publicKey**: `CryptoKey` Defined in: [packages/auth/src/types.ts:204](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L204) Asymmetric public key for JWT signature verification. Supported algorithms: * **RSA**: RS256, RS384, RS512 * **RSA-PSS**: PS256, PS384, PS512 * **EC (ECDSA)**: ES256, ES384, ES512 * **EdDSA**: Ed25519, Ed448 Import a PEM-encoded key via Web Crypto API: #### Examples ```typescript const rsaKey = await crypto.subtle.importKey( "spki", pemToArrayBuffer(rsaPem), { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, true, ["verify"], ); ``` ```typescript const ecKey = await crypto.subtle.importKey( "spki", pemToArrayBuffer(ecPem), { name: "ECDSA", namedCurve: "P-256" }, true, ["verify"], ); ``` #### See [jose CryptoKey documentation](https://github.com/panva/jose/blob/main/docs/types/types.CryptoKey.md) *** ### secret? > `optional` **secret**: `string` Defined in: [packages/auth/src/types.ts:168](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L168) HMAC symmetric secret (for HS256/HS384/HS512) *** ### skipMethods? > `optional` **skipMethods**: `string`\[] Defined in: [packages/auth/src/types.ts:233](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L233) Methods to skip authentication for. #### Default ```ts [] ``` --- --- url: /en/api/@connectum/otel/logger/interfaces/Logger.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [logger](../index.md) / Logger # Interface: Logger Defined in: [packages/otel/src/logger.ts:11](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L11) ## Methods ### debug() > **debug**(`message`, `attributes?`): `void` Defined in: [packages/otel/src/logger.ts:15](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L15) #### Parameters ##### message `string` ##### attributes? `AnyValueMap` #### Returns `void` *** ### emit() > **emit**(`record`): `void` Defined in: [packages/otel/src/logger.ts:16](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L16) #### Parameters ##### record `LogRecord` #### Returns `void` *** ### error() > **error**(`message`, `attributes?`): `void` Defined in: [packages/otel/src/logger.ts:14](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L14) #### Parameters ##### message `string` ##### attributes? `AnyValueMap` #### Returns `void` *** ### info() > **info**(`message`, `attributes?`): `void` Defined in: [packages/otel/src/logger.ts:12](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L12) #### Parameters ##### message `string` ##### attributes? `AnyValueMap` #### Returns `void` *** ### warn() > **warn**(`message`, `attributes?`): `void` Defined in: [packages/otel/src/logger.ts:13](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L13) #### Parameters ##### message `string` ##### attributes? `AnyValueMap` #### Returns `void` --- --- url: /en/api/@connectum/interceptors/interfaces/LoggerOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / LoggerOptions # Interface: LoggerOptions Defined in: [types.ts:45](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L45) Logger interceptor options ## Properties ### level? > `optional` **level**: `"error"` | `"debug"` | `"info"` | `"warn"` Defined in: [types.ts:50](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L50) Log level #### Default ```ts "debug" ``` *** ### logger()? > `optional` **logger**: (`message`, ...`args`) => `void` Defined in: [types.ts:62](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L62) Custom logger function #### Parameters ##### message `string` ##### args ...`unknown`\[] #### Returns `void` #### Default ```ts console[level] ``` *** ### skipHealthCheck? > `optional` **skipHealthCheck**: `boolean` Defined in: [types.ts:56](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L56) Skip logging for health check services #### Default ```ts true ``` --- --- url: /en/api/@connectum/otel/logger/interfaces/LoggerOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [logger](../index.md) / LoggerOptions # Interface: LoggerOptions Defined in: [packages/otel/src/logger.ts:7](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L7) ## Properties ### defaultAttributes? > `optional` **defaultAttributes**: `AnyValueMap` Defined in: [packages/otel/src/logger.ts:8](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/logger.ts#L8) --- --- url: /en/api/@connectum/otel/interfaces/Meter.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / Meter # Interface: Meter Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:18 An interface to allow the recording metrics. Metrics are used for recording pre-defined aggregation (`Counter`), or raw values (`Histogram`) in which the aggregation and attributes for the exported metric are deferred. ## Methods ### addBatchObservableCallback() > **addBatchObservableCallback**<`AttributesTypes`>(`callback`, `observables`): `void` Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:98 Sets up a function that will be called whenever a metric collection is initiated. If the function is already in the list of callbacks for this Observable, the function is not added a second time. Only the associated observables can be observed in the callback. Measurements of observables that are not associated observed in the callback are dropped. #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### callback `BatchObservableCallback`<`AttributesTypes`> the batch observable callback ##### observables `Observable`<`AttributesTypes`>\[] the observables associated with this batch observable callback #### Returns `void` *** ### createCounter() > **createCounter**<`AttributesTypes`>(`name`, `options?`): `Counter`<`AttributesTypes`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:38 Creates a new `Counter` metric. Generally, this kind of metric when the value is a quantity, the sum is of primary interest, and the event count and value distribution are not of primary interest. #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### name `string` the name of the metric. ##### options? `MetricOptions` the metric options. #### Returns `Counter`<`AttributesTypes`> *** ### createGauge() > **createGauge**<`AttributesTypes`>(`name`, `options?`): `Gauge`<`AttributesTypes`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:24 Creates and returns a new `Gauge`. #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### name `string` the name of the metric. ##### options? `MetricOptions` the metric options. #### Returns `Gauge`<`AttributesTypes`> *** ### createHistogram() > **createHistogram**<`AttributesTypes`>(`name`, `options?`): `Histogram`<`AttributesTypes`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:30 Creates and returns a new `Histogram`. #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### name `string` the name of the metric. ##### options? `MetricOptions` the metric options. #### Returns `Histogram`<`AttributesTypes`> *** ### createObservableCounter() > **createObservableCounter**<`AttributesTypes`>(`name`, `options?`): `ObservableCounter`<`AttributesTypes`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:74 Creates a new `ObservableCounter` metric. The callback SHOULD be safe to be invoked concurrently. #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### name `string` the name of the metric. ##### options? `MetricOptions` the metric options. #### Returns `ObservableCounter`<`AttributesTypes`> *** ### createObservableGauge() > **createObservableGauge**<`AttributesTypes`>(`name`, `options?`): `ObservableGauge`<`AttributesTypes`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:65 Creates a new `ObservableGauge` metric. The callback SHOULD be safe to be invoked concurrently. #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### name `string` the name of the metric. ##### options? `MetricOptions` the metric options. #### Returns `ObservableGauge`<`AttributesTypes`> *** ### createObservableUpDownCounter() > **createObservableUpDownCounter**<`AttributesTypes`>(`name`, `options?`): `ObservableUpDownCounter`<`AttributesTypes`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:83 Creates a new `ObservableUpDownCounter` metric. The callback SHOULD be safe to be invoked concurrently. #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### name `string` the name of the metric. ##### options? `MetricOptions` the metric options. #### Returns `ObservableUpDownCounter`<`AttributesTypes`> *** ### createUpDownCounter() > **createUpDownCounter**<`AttributesTypes`>(`name`, `options?`): `UpDownCounter`<`AttributesTypes`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:56 Creates a new `UpDownCounter` metric. UpDownCounter is a synchronous instrument and very similar to Counter except that Add(increment) supports negative increments. It is generally useful for capturing changes in an amount of resources used, or any quantity that rises and falls during a request. Example uses for UpDownCounter: #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### name `string` the name of the metric. ##### options? `MetricOptions` the metric options. #### Returns `UpDownCounter`<`AttributesTypes`> *** ### removeBatchObservableCallback() > **removeBatchObservableCallback**<`AttributesTypes`>(`callback`, `observables`): `void` Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:108 Removes a callback previously registered with [Meter.addBatchObservableCallback](#addbatchobservablecallback). The callback to be removed is identified using a combination of the callback itself, and the set of the observables associated with it. #### Type Parameters ##### AttributesTypes `AttributesTypes` *extends* `Attributes` = `Attributes` #### Parameters ##### callback `BatchObservableCallback`<`AttributesTypes`> the batch observable callback ##### observables `Observable`<`AttributesTypes`>\[] the observables associated with this batch observable callback #### Returns `void` --- --- url: /en/api/@connectum/otel/interfaces/OtelBaseOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / OtelBaseOptions # Interface: OtelBaseOptions Defined in: [packages/otel/src/types.ts:29](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L29) Common options shared between server and client OTel interceptors ## Extended by * [`OtelClientInterceptorOptions`](OtelClientInterceptorOptions.md) * [`OtelInterceptorOptions`](OtelInterceptorOptions.md) ## Properties ### attributeFilter? > `optional` **attributeFilter**: [`OtelAttributeFilter`](../type-aliases/OtelAttributeFilter.md) Defined in: [packages/otel/src/types.ts:40](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L40) Filter callback to exclude specific attributes *** ### filter? > `optional` **filter**: [`OtelFilter`](../type-aliases/OtelFilter.md) Defined in: [packages/otel/src/types.ts:37](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L37) Filter callback to skip specific requests *** ### recordMessages? > `optional` **recordMessages**: `boolean` Defined in: [packages/otel/src/types.ts:47](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L47) Include request/response message content in span events. WARNING: May contain sensitive data. #### Default ```ts false ``` *** ### withoutMetrics? > `optional` **withoutMetrics**: `boolean` Defined in: [packages/otel/src/types.ts:34](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L34) Disable metric recording (tracing only) *** ### withoutTracing? > `optional` **withoutTracing**: `boolean` Defined in: [packages/otel/src/types.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L31) Disable span creation (metrics only) --- --- url: /en/api/@connectum/otel/interfaces/OtelClientInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / OtelClientInterceptorOptions # Interface: OtelClientInterceptorOptions Defined in: [packages/otel/src/types.ts:75](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L75) Options for createOtelClientInterceptor() (client-side) ## Extends * [`OtelBaseOptions`](OtelBaseOptions.md) ## Properties ### attributeFilter? > `optional` **attributeFilter**: [`OtelAttributeFilter`](../type-aliases/OtelAttributeFilter.md) Defined in: [packages/otel/src/types.ts:40](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L40) Filter callback to exclude specific attributes #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`attributeFilter`](OtelBaseOptions.md#attributefilter) *** ### filter? > `optional` **filter**: [`OtelFilter`](../type-aliases/OtelFilter.md) Defined in: [packages/otel/src/types.ts:37](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L37) Filter callback to skip specific requests #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`filter`](OtelBaseOptions.md#filter) *** ### recordMessages? > `optional` **recordMessages**: `boolean` Defined in: [packages/otel/src/types.ts:47](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L47) Include request/response message content in span events. WARNING: May contain sensitive data. #### Default ```ts false ``` #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`recordMessages`](OtelBaseOptions.md#recordmessages) *** ### serverAddress > **serverAddress**: `string` Defined in: [packages/otel/src/types.ts:80](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L80) Target server address (required for client spans). Used as `server.address` attribute. *** ### serverPort? > `optional` **serverPort**: `number` Defined in: [packages/otel/src/types.ts:86](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L86) Target server port. Used as `server.port` attribute. *** ### withoutMetrics? > `optional` **withoutMetrics**: `boolean` Defined in: [packages/otel/src/types.ts:34](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L34) Disable metric recording (tracing only) #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`withoutMetrics`](OtelBaseOptions.md#withoutmetrics) *** ### withoutTracing? > `optional` **withoutTracing**: `boolean` Defined in: [packages/otel/src/types.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L31) Disable span creation (metrics only) #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`withoutTracing`](OtelBaseOptions.md#withouttracing) --- --- url: /en/api/@connectum/otel/interfaces/OtelInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / OtelInterceptorOptions # Interface: OtelInterceptorOptions Defined in: [packages/otel/src/types.ts:53](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L53) Options for createOtelInterceptor() (server-side) ## Extends * [`OtelBaseOptions`](OtelBaseOptions.md) ## Properties ### attributeFilter? > `optional` **attributeFilter**: [`OtelAttributeFilter`](../type-aliases/OtelAttributeFilter.md) Defined in: [packages/otel/src/types.ts:40](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L40) Filter callback to exclude specific attributes #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`attributeFilter`](OtelBaseOptions.md#attributefilter) *** ### filter? > `optional` **filter**: [`OtelFilter`](../type-aliases/OtelFilter.md) Defined in: [packages/otel/src/types.ts:37](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L37) Filter callback to skip specific requests #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`filter`](OtelBaseOptions.md#filter) *** ### recordMessages? > `optional` **recordMessages**: `boolean` Defined in: [packages/otel/src/types.ts:47](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L47) Include request/response message content in span events. WARNING: May contain sensitive data. #### Default ```ts false ``` #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`recordMessages`](OtelBaseOptions.md#recordmessages) *** ### serverAddress? > `optional` **serverAddress**: `string` Defined in: [packages/otel/src/types.ts:64](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L64) Override server.address attribute (defaults to os.hostname()) *** ### serverPort? > `optional` **serverPort**: `number` Defined in: [packages/otel/src/types.ts:69](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L69) Opt-in server.port attribute *** ### trustRemote? > `optional` **trustRemote**: `boolean` Defined in: [packages/otel/src/types.ts:59](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L59) Use extracted remote context as parent span. When false, creates a new root span and adds a link to the remote span. #### Default ```ts false ``` *** ### withoutMetrics? > `optional` **withoutMetrics**: `boolean` Defined in: [packages/otel/src/types.ts:34](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L34) Disable metric recording (tracing only) #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`withoutMetrics`](OtelBaseOptions.md#withoutmetrics) *** ### withoutTracing? > `optional` **withoutTracing**: `boolean` Defined in: [packages/otel/src/types.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L31) Disable span creation (metrics only) #### Inherited from [`OtelBaseOptions`](OtelBaseOptions.md).[`withoutTracing`](OtelBaseOptions.md#withouttracing) --- --- url: /en/api/@connectum/otel/interfaces/OTLPSettings.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / OTLPSettings # Interface: OTLPSettings Defined in: [packages/otel/src/config.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L31) OTLP settings for traces, metrics, and logs ## Properties ### logs > **logs**: [`ExporterType`](../type-aliases/ExporterType.md) Defined in: [packages/otel/src/config.ts:34](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L34) *** ### metrics > **metrics**: [`ExporterType`](../type-aliases/ExporterType.md) Defined in: [packages/otel/src/config.ts:33](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L33) *** ### traces > **traces**: [`ExporterType`](../type-aliases/ExporterType.md) Defined in: [packages/otel/src/config.ts:32](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L32) --- --- url: /en/api/@connectum/auth/interfaces/ProtoAuthzInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / ProtoAuthzInterceptorOptions # Interface: ProtoAuthzInterceptorOptions Defined in: [packages/auth/src/types.ts:368](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L368) Proto-based authorization interceptor options. Uses proto custom options (connectum.auth.v1) for declarative authorization rules defined in .proto files. Falls back to programmatic rules and callbacks. ## Properties ### authorize()? > `optional` **authorize**: (`context`, `req`) => `boolean` | `Promise`<`boolean`> Defined in: [packages/auth/src/types.ts:387](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L387) Programmatic authorization callback. Called when neither proto options nor programmatic rules match. #### Parameters ##### context [`AuthContext`](AuthContext.md) Authenticated user context ##### req Request info (service and method names) ###### method `string` ###### service `string` #### Returns `boolean` | `Promise`<`boolean`> true if authorized, false otherwise *** ### defaultPolicy? > `optional` **defaultPolicy**: [`AuthzEffect`](../type-aliases/AuthzEffect.md) Defined in: [packages/auth/src/types.ts:373](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L373) Default policy when no proto option and no rule match. #### Default ```ts "deny" ``` *** ### rules? > `optional` **rules**: [`AuthzRule`](AuthzRule.md)\[] Defined in: [packages/auth/src/types.ts:378](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L378) Additional programmatic rules, evaluated after proto options. Rules are evaluated in order; first matching rule wins. --- --- url: /en/api/@connectum/core/types/interfaces/ProtocolContext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / ProtocolContext # Interface: ProtocolContext Defined in: [packages/core/src/types.ts:51](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L51) Context provided to protocol registration functions Contains information about registered services that protocols may need (e.g., reflection needs DescFile\[], healthcheck needs service names). ## Properties ### registry > `readonly` **registry**: readonly `DescFile`\[] Defined in: [packages/core/src/types.ts:53](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L53) Registered service file descriptors --- --- url: /en/api/@connectum/core/types/interfaces/ProtocolRegistration.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / ProtocolRegistration # Interface: ProtocolRegistration Defined in: [packages/core/src/types.ts:84](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L84) Protocol registration interface Protocols (healthcheck, reflection, custom) implement this interface to register themselves on the server's ConnectRouter. ## Example ```typescript const myProtocol: ProtocolRegistration = { name: "my-protocol", register(router, context) { router.service(MyService, myImpl); }, }; const server = createServer({ services: [routes], protocols: [myProtocol], }); ``` ## Properties ### httpHandler? > `optional` **httpHandler**: [`HttpHandler`](../type-aliases/HttpHandler.md) Defined in: [packages/core/src/types.ts:92](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L92) Optional HTTP handler for fallback routing (e.g., /healthz endpoint) *** ### name > `readonly` **name**: `string` Defined in: [packages/core/src/types.ts:86](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L86) Protocol name for identification (e.g., "healthcheck", "reflection") ## Methods ### register() > **register**(`router`, `context`): `void` Defined in: [packages/core/src/types.ts:89](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L89) Register protocol services on the router #### Parameters ##### router `ConnectRouter` ##### context [`ProtocolContext`](ProtocolContext.md) #### Returns `void` --- --- url: /en/api/@connectum/cli/commands/proto-sync/interfaces/ProtoSyncOptions.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/cli](../../../index.md) / [commands/proto-sync](../index.md) / ProtoSyncOptions # Interface: ProtoSyncOptions Defined in: [commands/proto-sync.ts:25](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/commands/proto-sync.ts#L25) Options for the proto sync pipeline. ## Properties ### dryRun? > `optional` **dryRun**: `boolean` Defined in: [commands/proto-sync.ts:33](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/commands/proto-sync.ts#L33) Show what would be synced without generating *** ### from > **from**: `string` Defined in: [commands/proto-sync.ts:27](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/commands/proto-sync.ts#L27) Server URL (e.g., "http://localhost:5000") *** ### out > **out**: `string` Defined in: [commands/proto-sync.ts:29](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/commands/proto-sync.ts#L29) Output directory for generated types *** ### template? > `optional` **template**: `string` Defined in: [commands/proto-sync.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/commands/proto-sync.ts#L31) Path to custom buf.gen.yaml template --- --- url: /en/api/@connectum/otel/provider/interfaces/ProviderOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [provider](../index.md) / ProviderOptions # Interface: ProviderOptions Defined in: [packages/otel/src/provider.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/provider.ts#L31) Options for initializing the OpenTelemetry provider ## Properties ### serviceName? > `optional` **serviceName**: `string` Defined in: [packages/otel/src/provider.ts:33](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/provider.ts#L33) Override service name (defaults to OTEL\_SERVICE\_NAME or npm\_package\_name) *** ### serviceVersion? > `optional` **serviceVersion**: `string` Defined in: [packages/otel/src/provider.ts:35](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/provider.ts#L35) Override service version (defaults to npm\_package\_version) *** ### settings? > `optional` **settings**: `Partial`<[`OTLPSettings`](../../interfaces/OTLPSettings.md)> Defined in: [packages/otel/src/provider.ts:37](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/provider.ts#L37) Override OTLP exporter settings (defaults to env-based config) --- --- url: /en/api/@connectum/cli/utils/reflection/interfaces/ReflectionResult.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/cli](../../../index.md) / [utils/reflection](../index.md) / ReflectionResult # Interface: ReflectionResult Defined in: [utils/reflection.ts:19](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/utils/reflection.ts#L19) Result of fetching proto descriptors from a running server. ## Properties ### fileNames > **fileNames**: `string`\[] Defined in: [utils/reflection.ts:25](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/utils/reflection.ts#L25) Proto file names in the registry *** ### registry > **registry**: `FileRegistry` Defined in: [utils/reflection.ts:23](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/utils/reflection.ts#L23) FileRegistry containing all discovered file descriptors *** ### services > **services**: `string`\[] Defined in: [utils/reflection.ts:21](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/utils/reflection.ts#L21) List of fully-qualified service names --- --- url: /en/api/@connectum/auth/interfaces/ResolvedMethodAuth.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / ResolvedMethodAuth # Interface: ResolvedMethodAuth Defined in: [packages/auth/src/proto/reader.ts:20](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/proto/reader.ts#L20) Resolved authorization configuration for a single RPC method. Result of merging service-level defaults with method-level overrides. ## Properties ### policy > `readonly` **policy**: `"allow"` | `"deny"` | `undefined` Defined in: [packages/auth/src/proto/reader.ts:24](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/proto/reader.ts#L24) Authorization policy: "allow", "deny", or undefined (use interceptor default). *** ### public > `readonly` **public**: `boolean` Defined in: [packages/auth/src/proto/reader.ts:22](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/proto/reader.ts#L22) Whether the method is public (skip authn + authz). *** ### requires > `readonly` **requires**: { `roles`: readonly `string`\[]; `scopes`: readonly `string`\[]; } | `undefined` Defined in: [packages/auth/src/proto/reader.ts:26](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/proto/reader.ts#L26) Required roles and scopes, or undefined if none specified. --- --- url: /en/api/@connectum/interceptors/interfaces/RetryOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / RetryOptions # Interface: RetryOptions Defined in: [types.ts:91](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L91) Retry interceptor options ## Properties ### initialDelay? > `optional` **initialDelay**: `number` Defined in: [types.ts:102](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L102) Initial delay in milliseconds for exponential backoff #### Default ```ts 200 ``` *** ### maxDelay? > `optional` **maxDelay**: `number` Defined in: [types.ts:108](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L108) Maximum delay in milliseconds for exponential backoff #### Default ```ts 5000 ``` *** ### maxRetries? > `optional` **maxRetries**: `number` Defined in: [types.ts:96](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L96) Maximum number of retries #### Default ```ts 3 ``` *** ### retryableCodes? > `optional` **retryableCodes**: `Code`\[] Defined in: [types.ts:120](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L120) Error codes that trigger a retry #### Default ```ts [Code.Unavailable, Code.ResourceExhausted] ``` *** ### skipStreaming? > `optional` **skipStreaming**: `boolean` Defined in: [types.ts:114](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L114) Skip retry for streaming requests #### Default ```ts true ``` --- --- url: /en/api/@connectum/otel/metrics/interfaces/RpcClientMetrics.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [metrics](../index.md) / RpcClientMetrics # Interface: RpcClientMetrics Defined in: [packages/otel/src/metrics.ts:34](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L34) Pre-configured RPC client metric instruments Contains histograms for call duration, request size, and response size following OpenTelemetry RPC semantic conventions. ## Properties ### callDuration > **callDuration**: `Histogram` Defined in: [packages/otel/src/metrics.ts:36](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L36) Histogram measuring duration of RPC client calls (unit: seconds) *** ### requestSize > **requestSize**: `Histogram` Defined in: [packages/otel/src/metrics.ts:38](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L38) Histogram measuring size of RPC client request messages (unit: bytes) *** ### responseSize > **responseSize**: `Histogram` Defined in: [packages/otel/src/metrics.ts:40](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L40) Histogram measuring size of RPC client response messages (unit: bytes) --- --- url: /en/api/@connectum/otel/metrics/interfaces/RpcServerMetrics.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [metrics](../index.md) / RpcServerMetrics # Interface: RpcServerMetrics Defined in: [packages/otel/src/metrics.ts:19](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L19) Pre-configured RPC server metric instruments Contains histograms for call duration, request size, and response size following OpenTelemetry RPC semantic conventions. ## Properties ### callDuration > **callDuration**: `Histogram` Defined in: [packages/otel/src/metrics.ts:21](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L21) Histogram measuring duration of RPC server calls (unit: seconds) *** ### requestSize > **requestSize**: `Histogram` Defined in: [packages/otel/src/metrics.ts:23](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L23) Histogram measuring size of RPC server request messages (unit: bytes) *** ### responseSize > **responseSize**: `Histogram` Defined in: [packages/otel/src/metrics.ts:25](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/metrics.ts#L25) Histogram measuring size of RPC server response messages (unit: bytes) --- --- url: /en/api/@connectum/core/interfaces/SanitizableError.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / SanitizableError # Interface: SanitizableError Defined in: [packages/core/src/errors.ts:17](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/errors.ts#L17) Sanitizable error interface. Errors implementing this protocol carry rich server-side details but expose only a safe message to clients. ## Properties ### clientMessage > `readonly` **clientMessage**: `string` Defined in: [packages/core/src/errors.ts:18](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/errors.ts#L18) *** ### serverDetails > `readonly` **serverDetails**: `Readonly`<`Record`<`string`, `unknown`>> Defined in: [packages/core/src/errors.ts:19](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/errors.ts#L19) --- --- url: /en/api/@connectum/interceptors/interfaces/SerializerOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / SerializerOptions # Interface: SerializerOptions Defined in: [types.ts:68](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L68) Serializer interceptor options ## Properties ### alwaysEmitImplicit? > `optional` **alwaysEmitImplicit**: `boolean` Defined in: [types.ts:79](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L79) Always emit implicit fields in JSON #### Default ```ts true ``` *** ### ignoreUnknownFields? > `optional` **ignoreUnknownFields**: `boolean` Defined in: [types.ts:85](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L85) Ignore unknown fields when deserializing #### Default ```ts true ``` *** ### skipGrpcServices? > `optional` **skipGrpcServices**: `boolean` Defined in: [types.ts:73](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L73) Skip serialization for gRPC services #### Default ```ts true ``` --- --- url: /en/api/@connectum/core/types/interfaces/Server.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / Server # Interface: Server Defined in: [packages/core/src/types.ts:287](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L287) Server interface with explicit lifecycle control ## Example ```typescript import { createServer } from '@connectum/core'; const server = createServer({ services: [myRoutes], port: 5000 }); server.on('ready', () => console.log('Server ready!')); server.on('error', (err) => console.error('Error:', err)); await server.start(); // Later await server.stop(); ``` ## Extends * `EventEmitter` ## Properties ### address > `readonly` **address**: `AddressInfo` | `null` Defined in: [packages/core/src/types.ts:315](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L315) Current server address Returns null until server is started *** ### interceptors > `readonly` **interceptors**: readonly `Interceptor`\[] Defined in: [packages/core/src/types.ts:443](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L443) Registered interceptors *** ### isRunning > `readonly` **isRunning**: `boolean` Defined in: [packages/core/src/types.ts:320](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L320) Whether server is currently running *** ### protocols > `readonly` **protocols**: readonly [`ProtocolRegistration`](ProtocolRegistration.md)\[] Defined in: [packages/core/src/types.ts:448](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L448) Registered protocols *** ### routes > `readonly` **routes**: readonly [`ServiceRoute`](../type-aliases/ServiceRoute.md)\[] Defined in: [packages/core/src/types.ts:438](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L438) Registered service routes *** ### shutdownSignal > `readonly` **shutdownSignal**: `AbortSignal` Defined in: [packages/core/src/types.ts:422](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L422) Abort signal that is aborted when server begins shutdown. Use this to signal streaming RPCs and long-running operations that the server is shutting down. *** ### state > `readonly` **state**: [`ServerState`](../type-aliases/ServerState.md) Defined in: [packages/core/src/types.ts:325](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L325) Current server state *** ### transport > `readonly` **transport**: [`TransportServer`](../type-aliases/TransportServer.md) | `null` Defined in: [packages/core/src/types.ts:433](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L433) Underlying transport server Returns null until server is started ## Methods ### \[captureRejectionSymbol]\()? > `optional` **\[captureRejectionSymbol]**(`error`, `event`, ...`args`): `void` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:123 The `Symbol.for('nodejs.rejection')` method is called in case a promise rejection happens when emitting an event and `captureRejections` is enabled on the emitter. It is possible to use `events.captureRejectionSymbol` in place of `Symbol.for('nodejs.rejection')`. ```js import { EventEmitter, captureRejectionSymbol } from 'node:events'; class MyClass extends EventEmitter { constructor() { super({ captureRejections: true }); } [captureRejectionSymbol](err,%20event,%20...args) { console.log('rejection happened for', event, 'with', err, ...args); this.destroy(err); } destroy(err) { // Tear the resource down here. } } ``` #### Parameters ##### error `Error` ##### event `string` | `symbol` ##### args ...`any`\[] #### Returns `void` #### Since v13.4.0, v12.16.0 #### Inherited from `EventEmitter.[captureRejectionSymbol]` *** ### addInterceptor() > **addInterceptor**(`interceptor`): `void` Defined in: [packages/core/src/types.ts:374](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L374) Add an interceptor at runtime #### Parameters ##### interceptor `Interceptor` #### Returns `void` #### Throws Error if server is already running *** ### addListener() > **addListener**<`E`>(`eventName`, `listener`): `this` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:128 Alias for `emitter.on(eventName, listener)`. #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName `string` | `symbol` ##### listener (...`args`) => `void` #### Returns `this` #### Since v0.1.26 #### Inherited from `EventEmitter.addListener` *** ### addProtocol() > **addProtocol**(`protocol`): `void` Defined in: [packages/core/src/types.ts:381](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L381) Add a protocol at runtime #### Parameters ##### protocol [`ProtocolRegistration`](ProtocolRegistration.md) #### Returns `void` #### Throws Error if server is already running *** ### addService() > **addService**(`service`): `void` Defined in: [packages/core/src/types.ts:367](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L367) Add a service route at runtime #### Parameters ##### service [`ServiceRoute`](../type-aliases/ServiceRoute.md) #### Returns `void` #### Throws Error if server is already running *** ### emit() > **emit**<`E`>(`eventName`, ...`args`): `boolean` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:170 Synchronously calls each of the listeners registered for the event named `eventName`, in the order they were registered, passing the supplied arguments to each. Returns `true` if the event had listeners, `false` otherwise. ```js import { EventEmitter } from 'node:events'; const myEmitter = new EventEmitter(); // First listener myEmitter.on('event', function firstListener() { console.log('Helloooo! first listener'); }); // Second listener myEmitter.on('event', function secondListener(arg1, arg2) { console.log(`event with parameters ${arg1}, ${arg2} in second listener`); }); // Third listener myEmitter.on('event', function thirdListener(...args) { const parameters = args.join(', '); console.log(`event with parameters ${parameters} in third listener`); }); console.log(myEmitter.listeners('event')); myEmitter.emit('event', 1, 2, 3, 4, 5); // Prints: // [ // [Function: firstListener], // [Function: secondListener], // [Function: thirdListener] // ] // Helloooo! first listener // event with parameters 1, 2 in second listener // event with parameters 1, 2, 3, 4, 5 in third listener ``` #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName `string` | `symbol` ##### args ...`any`\[] #### Returns `boolean` #### Since v0.1.26 #### Inherited from `EventEmitter.emit` *** ### eventNames() > **eventNames**(): (`string` | `symbol`)\[] Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:190 Returns an array listing the events for which the emitter has registered listeners. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.on('foo', () => {}); myEE.on('bar', () => {}); const sym = Symbol('symbol'); myEE.on(sym, () => {}); console.log(myEE.eventNames()); // Prints: [ 'foo', 'bar', Symbol(symbol) ] ``` #### Returns (`string` | `symbol`)\[] #### Since v6.0.0 #### Inherited from `EventEmitter.eventNames` *** ### getMaxListeners() > **getMaxListeners**(): `number` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:197 Returns the current max listener value for the `EventEmitter` which is either set by `emitter.setMaxListeners(n)` or defaults to `events.defaultMaxListeners`. #### Returns `number` #### Since v1.0.0 #### Inherited from `EventEmitter.getMaxListeners` *** ### listenerCount() > **listenerCount**<`E`>(`eventName`, `listener?`): `number` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:206 Returns the number of listeners listening for the event named `eventName`. If `listener` is provided, it will return how many times the listener is found in the list of the listeners of the event. #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName The name of the event being listened for `string` | `symbol` ##### listener? (...`args`) => `void` The event handler function #### Returns `number` #### Since v3.2.0 #### Inherited from `EventEmitter.listenerCount` *** ### listeners() > **listeners**<`E`>(`eventName`): (...`args`) => `void`\[] Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:222 Returns a copy of the array of listeners for the event named `eventName`. ```js server.on('connection', (stream) => { console.log('someone connected!'); }); console.log(util.inspect(server.listeners('connection'))); // Prints: [ [Function] ] ``` #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName `string` | `symbol` #### Returns (...`args`) => `void`\[] #### Since v0.1.26 #### Inherited from `EventEmitter.listeners` *** ### off() #### Call Signature > **off**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:352](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L352) Remove listener for lifecycle events ##### Parameters ###### event `"start"` ###### listener () => `void` ##### Returns `this` ##### Overrides `EventEmitter.off` #### Call Signature > **off**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:353](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L353) Alias for `emitter.removeListener()`. ##### Parameters ###### event `"ready"` ###### listener () => `void` ##### Returns `this` ##### Since v10.0.0 ##### Overrides `EventEmitter.off` #### Call Signature > **off**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:354](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L354) Alias for `emitter.removeListener()`. ##### Parameters ###### event `"stopping"` ###### listener () => `void` ##### Returns `this` ##### Since v10.0.0 ##### Overrides `EventEmitter.off` #### Call Signature > **off**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:355](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L355) Alias for `emitter.removeListener()`. ##### Parameters ###### event `"stop"` ###### listener () => `void` ##### Returns `this` ##### Since v10.0.0 ##### Overrides `EventEmitter.off` #### Call Signature > **off**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:356](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L356) Alias for `emitter.removeListener()`. ##### Parameters ###### event `"error"` ###### listener (`error`) => `void` ##### Returns `this` ##### Since v10.0.0 ##### Overrides `EventEmitter.off` *** ### on() #### Call Signature > **on**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:334](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L334) Register listener for lifecycle events ##### Parameters ###### event `"start"` ###### listener () => `void` ##### Returns `this` ##### Overrides `EventEmitter.on` #### Call Signature > **on**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:335](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L335) Adds the `listener` function to the end of the listeners array for the event named `eventName`. No checks are made to see if the `listener` has already been added. Multiple calls passing the same combination of `eventName` and `listener` will result in the `listener` being added, and called, multiple times. ```js server.on('connection', (stream) => { console.log('someone connected!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. By default, event listeners are invoked in the order they are added. The `emitter.prependListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.on('foo', () => console.log('a')); myEE.prependListener('foo', () => console.log('b')); myEE.emit('foo'); // Prints: // b // a ``` ##### Parameters ###### event `"ready"` ###### listener () => `void` The callback function ##### Returns `this` ##### Since v0.1.101 ##### Overrides `EventEmitter.on` #### Call Signature > **on**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:336](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L336) Adds the `listener` function to the end of the listeners array for the event named `eventName`. No checks are made to see if the `listener` has already been added. Multiple calls passing the same combination of `eventName` and `listener` will result in the `listener` being added, and called, multiple times. ```js server.on('connection', (stream) => { console.log('someone connected!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. By default, event listeners are invoked in the order they are added. The `emitter.prependListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.on('foo', () => console.log('a')); myEE.prependListener('foo', () => console.log('b')); myEE.emit('foo'); // Prints: // b // a ``` ##### Parameters ###### event `"stopping"` ###### listener () => `void` The callback function ##### Returns `this` ##### Since v0.1.101 ##### Overrides `EventEmitter.on` #### Call Signature > **on**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:337](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L337) Adds the `listener` function to the end of the listeners array for the event named `eventName`. No checks are made to see if the `listener` has already been added. Multiple calls passing the same combination of `eventName` and `listener` will result in the `listener` being added, and called, multiple times. ```js server.on('connection', (stream) => { console.log('someone connected!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. By default, event listeners are invoked in the order they are added. The `emitter.prependListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.on('foo', () => console.log('a')); myEE.prependListener('foo', () => console.log('b')); myEE.emit('foo'); // Prints: // b // a ``` ##### Parameters ###### event `"stop"` ###### listener () => `void` The callback function ##### Returns `this` ##### Since v0.1.101 ##### Overrides `EventEmitter.on` #### Call Signature > **on**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:338](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L338) Adds the `listener` function to the end of the listeners array for the event named `eventName`. No checks are made to see if the `listener` has already been added. Multiple calls passing the same combination of `eventName` and `listener` will result in the `listener` being added, and called, multiple times. ```js server.on('connection', (stream) => { console.log('someone connected!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. By default, event listeners are invoked in the order they are added. The `emitter.prependListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.on('foo', () => console.log('a')); myEE.prependListener('foo', () => console.log('b')); myEE.emit('foo'); // Prints: // b // a ``` ##### Parameters ###### event `"error"` ###### listener (`error`) => `void` The callback function ##### Returns `this` ##### Since v0.1.101 ##### Overrides `EventEmitter.on` *** ### once() #### Call Signature > **once**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:343](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L343) Register one-time listener for lifecycle events ##### Parameters ###### event `"start"` ###### listener () => `void` ##### Returns `this` ##### Overrides `EventEmitter.once` #### Call Signature > **once**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:344](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L344) Adds a **one-time** `listener` function for the event named `eventName`. The next time `eventName` is triggered, this listener is removed and then invoked. ```js server.once('connection', (stream) => { console.log('Ah, we have our first user!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. By default, event listeners are invoked in the order they are added. The `emitter.prependOnceListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.once('foo', () => console.log('a')); myEE.prependOnceListener('foo', () => console.log('b')); myEE.emit('foo'); // Prints: // b // a ``` ##### Parameters ###### event `"ready"` ###### listener () => `void` The callback function ##### Returns `this` ##### Since v0.3.0 ##### Overrides `EventEmitter.once` #### Call Signature > **once**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:345](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L345) Adds a **one-time** `listener` function for the event named `eventName`. The next time `eventName` is triggered, this listener is removed and then invoked. ```js server.once('connection', (stream) => { console.log('Ah, we have our first user!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. By default, event listeners are invoked in the order they are added. The `emitter.prependOnceListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.once('foo', () => console.log('a')); myEE.prependOnceListener('foo', () => console.log('b')); myEE.emit('foo'); // Prints: // b // a ``` ##### Parameters ###### event `"stopping"` ###### listener () => `void` The callback function ##### Returns `this` ##### Since v0.3.0 ##### Overrides `EventEmitter.once` #### Call Signature > **once**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:346](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L346) Adds a **one-time** `listener` function for the event named `eventName`. The next time `eventName` is triggered, this listener is removed and then invoked. ```js server.once('connection', (stream) => { console.log('Ah, we have our first user!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. By default, event listeners are invoked in the order they are added. The `emitter.prependOnceListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.once('foo', () => console.log('a')); myEE.prependOnceListener('foo', () => console.log('b')); myEE.emit('foo'); // Prints: // b // a ``` ##### Parameters ###### event `"stop"` ###### listener () => `void` The callback function ##### Returns `this` ##### Since v0.3.0 ##### Overrides `EventEmitter.once` #### Call Signature > **once**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:347](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L347) Adds a **one-time** `listener` function for the event named `eventName`. The next time `eventName` is triggered, this listener is removed and then invoked. ```js server.once('connection', (stream) => { console.log('Ah, we have our first user!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. By default, event listeners are invoked in the order they are added. The `emitter.prependOnceListener()` method can be used as an alternative to add the event listener to the beginning of the listeners array. ```js import { EventEmitter } from 'node:events'; const myEE = new EventEmitter(); myEE.once('foo', () => console.log('a')); myEE.prependOnceListener('foo', () => console.log('b')); myEE.emit('foo'); // Prints: // b // a ``` ##### Parameters ###### event `"error"` ###### listener (`error`) => `void` The callback function ##### Returns `this` ##### Since v0.3.0 ##### Overrides `EventEmitter.once` *** ### onShutdown() #### Call Signature > **onShutdown**(`handler`): `void` Defined in: [packages/core/src/types.ts:393](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L393) Register an anonymous shutdown hook ##### Parameters ###### handler [`ShutdownHook`](../type-aliases/ShutdownHook.md) Shutdown hook function ##### Returns `void` ##### Throws Error if server is already stopped #### Call Signature > **onShutdown**(`name`, `handler`): `void` Defined in: [packages/core/src/types.ts:402](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L402) Register a named shutdown hook ##### Parameters ###### name `string` Module name for dependency resolution ###### handler [`ShutdownHook`](../type-aliases/ShutdownHook.md) Shutdown hook function ##### Returns `void` ##### Throws Error if server is already stopped #### Call Signature > **onShutdown**(`name`, `dependencies`, `handler`): `void` Defined in: [packages/core/src/types.ts:414](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L414) Register a named shutdown hook with dependencies Dependencies are executed before this hook during shutdown. ##### Parameters ###### name `string` Module name for dependency resolution ###### dependencies `string`\[] Module names that must shut down first ###### handler [`ShutdownHook`](../type-aliases/ShutdownHook.md) Shutdown hook function ##### Returns `void` ##### Throws Error if server is already stopped *** ### prependListener() > **prependListener**<`E`>(`eventName`, `listener`): `this` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:311 Adds the `listener` function to the *beginning* of the listeners array for the event named `eventName`. No checks are made to see if the `listener` has already been added. Multiple calls passing the same combination of `eventName` and `listener` will result in the `listener` being added, and called, multiple times. ```js server.prependListener('connection', (stream) => { console.log('someone connected!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName The name of the event. `string` | `symbol` ##### listener (...`args`) => `void` The callback function #### Returns `this` #### Since v6.0.0 #### Inherited from `EventEmitter.prependListener` *** ### prependOnceListener() > **prependOnceListener**<`E`>(`eventName`, `listener`): `this` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:328 Adds a **one-time** `listener` function for the event named `eventName` to the *beginning* of the listeners array. The next time `eventName` is triggered, this listener is removed, and then invoked. ```js server.prependOnceListener('connection', (stream) => { console.log('Ah, we have our first user!'); }); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName The name of the event. `string` | `symbol` ##### listener (...`args`) => `void` The callback function #### Returns `this` #### Since v6.0.0 #### Inherited from `EventEmitter.prependOnceListener` *** ### rawListeners() > **rawListeners**<`E`>(`eventName`): (...`args`) => `void`\[] Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:362 Returns a copy of the array of listeners for the event named `eventName`, including any wrappers (such as those created by `.once()`). ```js import { EventEmitter } from 'node:events'; const emitter = new EventEmitter(); emitter.once('log', () => console.log('log once')); // Returns a new Array with a function `onceWrapper` which has a property // `listener` which contains the original listener bound above const listeners = emitter.rawListeners('log'); const logFnWrapper = listeners[0]; // Logs "log once" to the console and does not unbind the `once` event logFnWrapper.listener(); // Logs "log once" to the console and removes the listener logFnWrapper(); emitter.on('log', () => console.log('log persistently')); // Will return a new Array with a single function bound by `.on()` above const newListeners = emitter.rawListeners('log'); // Logs "log persistently" twice newListeners[0](); emitter.emit('log'); ``` #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName `string` | `symbol` #### Returns (...`args`) => `void`\[] #### Since v9.4.0 #### Inherited from `EventEmitter.rawListeners` *** ### removeAllListeners() > **removeAllListeners**<`E`>(`eventName?`): `this` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:374 Removes all listeners, or those of the specified `eventName`. It is bad practice to remove listeners added elsewhere in the code, particularly when the `EventEmitter` instance was created by some other component or module (e.g. sockets or file streams). Returns a reference to the `EventEmitter`, so that calls can be chained. #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName? `string` | `symbol` #### Returns `this` #### Since v0.1.26 #### Inherited from `EventEmitter.removeAllListeners` *** ### removeListener() > **removeListener**<`E`>(`eventName`, `listener`): `this` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:461 Removes the specified `listener` from the listener array for the event named `eventName`. ```js const callback = (stream) => { console.log('someone connected!'); }; server.on('connection', callback); // ... server.removeListener('connection', callback); ``` `removeListener()` will remove, at most, one instance of a listener from the listener array. If any single listener has been added multiple times to the listener array for the specified `eventName`, then `removeListener()` must be called multiple times to remove each instance. Once an event is emitted, all listeners attached to it at the time of emitting are called in order. This implies that any `removeListener()` or `removeAllListeners()` calls *after* emitting and *before* the last listener finishes execution will not remove them from `emit()` in progress. Subsequent events behave as expected. ```js import { EventEmitter } from 'node:events'; class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); const callbackA = () => { console.log('A'); myEmitter.removeListener('event', callbackB); }; const callbackB = () => { console.log('B'); }; myEmitter.on('event', callbackA); myEmitter.on('event', callbackB); // callbackA removes listener callbackB but it will still be called. // Internal listener array at time of emit [callbackA, callbackB] myEmitter.emit('event'); // Prints: // A // B // callbackB is now removed. // Internal listener array [callbackA] myEmitter.emit('event'); // Prints: // A ``` Because listeners are managed using an internal array, calling this will change the position indexes of any listener registered *after* the listener being removed. This will not impact the order in which listeners are called, but it means that any copies of the listener array as returned by the `emitter.listeners()` method will need to be recreated. When a single function has been added as a handler multiple times for a single event (as in the example below), `removeListener()` will remove the most recently added instance. In the example the `once('ping')` listener is removed: ```js import { EventEmitter } from 'node:events'; const ee = new EventEmitter(); function pong() { console.log('pong'); } ee.on('ping', pong); ee.once('ping', pong); ee.removeListener('ping', pong); ee.emit('ping'); ee.emit('ping'); ``` Returns a reference to the `EventEmitter`, so that calls can be chained. #### Type Parameters ##### E `E` *extends* `string` | `symbol` #### Parameters ##### eventName `string` | `symbol` ##### listener (...`args`) => `void` #### Returns `this` #### Since v0.1.26 #### Inherited from `EventEmitter.removeListener` *** ### setMaxListeners() > **setMaxListeners**(`n`): `this` Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/events.d.ts:472 By default `EventEmitter`s will print a warning if more than `10` listeners are added for a particular event. This is a useful default that helps finding memory leaks. The `emitter.setMaxListeners()` method allows the limit to be modified for this specific `EventEmitter` instance. The value can be set to `Infinity` (or `0`) to indicate an unlimited number of listeners. Returns a reference to the `EventEmitter`, so that calls can be chained. #### Parameters ##### n `number` #### Returns `this` #### Since v0.3.5 #### Inherited from `EventEmitter.setMaxListeners` *** ### start() > **start**(): `Promise`<`void`> Defined in: [packages/core/src/types.ts:297](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L297) Start the server #### Returns `Promise`<`void`> #### Throws Error if server is not in CREATED state *** ### stop() > **stop**(): `Promise`<`void`> Defined in: [packages/core/src/types.ts:304](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L304) Stop the server gracefully #### Returns `Promise`<`void`> #### Throws Error if server is not in RUNNING state --- --- url: /en/api/@connectum/healthcheck/interfaces/ServiceStatus.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / ServiceStatus # Interface: ServiceStatus Defined in: [types.ts:20](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L20) Service health status ## Properties ### status > **status**: `HealthCheckResponse_ServingStatus` Defined in: [types.ts:21](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L21) --- --- url: /en/api/@connectum/auth/interfaces/SessionAuthInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / SessionAuthInterceptorOptions # Interface: SessionAuthInterceptorOptions Defined in: [packages/auth/src/types.ts:326](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L326) Session-based auth interceptor options. Two-step authentication: verify session token, then map session data to AuthContext. ## Properties ### cache? > `readonly` `optional` **cache**: [`CacheOptions`](CacheOptions.md) Defined in: [packages/auth/src/types.ts:349](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L349) LRU cache for session verification results *** ### extractToken()? > `readonly` `optional` **extractToken**: (`req`) => `string` | `Promise`<`string` | `null`> | `null` Defined in: [packages/auth/src/types.ts:347](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L347) Custom token extraction. Default: extracts Bearer token from Authorization header. #### Parameters ##### req ###### header `Headers` #### Returns `string` | `Promise`<`string` | `null`> | `null` *** ### mapSession() > `readonly` **mapSession**: (`session`) => [`AuthContext`](AuthContext.md) | `Promise`<[`AuthContext`](AuthContext.md)> Defined in: [packages/auth/src/types.ts:342](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L342) Map raw session data to AuthContext. #### Parameters ##### session `unknown` Raw session data from verifySession #### Returns [`AuthContext`](AuthContext.md) | `Promise`<[`AuthContext`](AuthContext.md)> Normalized auth context *** ### propagatedClaims? > `readonly` `optional` **propagatedClaims**: `string`\[] Defined in: [packages/auth/src/types.ts:359](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L359) Filter which claims are propagated in headers. When set, only listed claim keys are included in x-auth-claims header. When not set, all claims are propagated. *** ### propagateHeaders? > `readonly` `optional` **propagateHeaders**: `boolean` Defined in: [packages/auth/src/types.ts:353](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L353) Propagate auth context as headers for downstream services *** ### skipMethods? > `readonly` `optional` **skipMethods**: `string`\[] Defined in: [packages/auth/src/types.ts:351](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L351) Methods to skip authentication for *** ### verifySession() > `readonly` **verifySession**: (`token`, `headers`) => `unknown` Defined in: [packages/auth/src/types.ts:335](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L335) Verify session token and return raw session data. Must throw on invalid/expired sessions. #### Parameters ##### token `string` Session token string ##### headers `Headers` Request headers (for additional context) #### Returns `unknown` Raw session data --- --- url: /en/api/@connectum/core/types/interfaces/ShutdownOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / ShutdownOptions # Interface: ShutdownOptions Defined in: [packages/core/src/types.ts:161](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L161) Graceful shutdown options ## Properties ### autoShutdown? > `optional` **autoShutdown**: `boolean` Defined in: [packages/core/src/types.ts:178](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L178) Enable automatic graceful shutdown on signals #### Default ```ts false ``` *** ### forceCloseOnTimeout? > `optional` **forceCloseOnTimeout**: `boolean` Defined in: [packages/core/src/types.ts:186](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L186) Force close all HTTP/2 sessions when shutdown timeout is exceeded. When true, sessions are destroyed after timeout. When false, server waits indefinitely for in-flight requests to complete. #### Default ```ts true ``` *** ### signals? > `optional` **signals**: `Signals`\[] Defined in: [packages/core/src/types.ts:172](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L172) Signals to listen for graceful shutdown #### Default ```ts ["SIGTERM", "SIGINT"] ``` *** ### timeout? > `optional` **timeout**: `number` Defined in: [packages/core/src/types.ts:166](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L166) Timeout in milliseconds for graceful shutdown #### Default ```ts 30000 ``` --- --- url: /en/api/@connectum/interceptors/interfaces/TimeoutOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / TimeoutOptions # Interface: TimeoutOptions Defined in: [types.ts:149](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L149) Timeout interceptor options ## Properties ### duration? > `optional` **duration**: `number` Defined in: [types.ts:154](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L154) Request timeout in milliseconds #### Default ```ts 30000 (30 seconds) ``` *** ### skipStreaming? > `optional` **skipStreaming**: `boolean` Defined in: [types.ts:160](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L160) Skip timeout for streaming calls #### Default ```ts true ``` --- --- url: /en/api/@connectum/core/types/interfaces/TLSOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / TLSOptions # Interface: TLSOptions Defined in: [packages/core/src/types.ts:98](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L98) TLS configuration options ## Properties ### certPath? > `optional` **certPath**: `string` Defined in: [packages/core/src/types.ts:107](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L107) Path to TLS certificate file *** ### dirPath? > `optional` **dirPath**: `string` Defined in: [packages/core/src/types.ts:113](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L113) TLS directory path (alternative to keyPath/certPath) Will look for server.key and server.crt in this directory *** ### keyPath? > `optional` **keyPath**: `string` Defined in: [packages/core/src/types.ts:102](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L102) Path to TLS key file --- --- url: /en/api/@connectum/otel/interfaces/TraceAllOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / TraceAllOptions # Interface: TraceAllOptions Defined in: [packages/otel/src/types.ts:133](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L133) Options for traceAll() Proxy-based object wrapper ## Properties ### argsFilter? > `optional` **argsFilter**: [`MethodArgsFilter`](../type-aliases/MethodArgsFilter.md) Defined in: [packages/otel/src/types.ts:157](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L157) Transform/masking for recorded args -- has access to method name. *** ### exclude? > `optional` **exclude**: `string`\[] Defined in: [packages/otel/src/types.ts:144](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L144) Blacklist of method names to exclude from wrapping *** ### include? > `optional` **include**: `string`\[] Defined in: [packages/otel/src/types.ts:141](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L141) Whitelist of method names to wrap (if provided, only these are wrapped) *** ### prefix? > `optional` **prefix**: `string` Defined in: [packages/otel/src/types.ts:138](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L138) Prefix for span names: "${prefix}.${methodName}" Defaults to constructor.name or "Object" *** ### recordArgs? > `optional` **recordArgs**: `boolean` | `string`\[] Defined in: [packages/otel/src/types.ts:152](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L152) Record method arguments as span attributes. * `false` (default): no args recorded * `true`: all args recorded * `string[]`: whitelist of argument names/indices --- --- url: /en/api/@connectum/otel/interfaces/TracedOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / TracedOptions # Interface: TracedOptions Defined in: [packages/otel/src/types.ts:104](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L104) Options for traced() function wrapper ## Properties ### argsFilter? > `optional` **argsFilter**: [`ArgsFilter`](../type-aliases/ArgsFilter.md) Defined in: [packages/otel/src/types.ts:122](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L122) Additional transform/masking for recorded args. Called after whitelist filtering. *** ### attributes? > `optional` **attributes**: `Record`<`string`, `string` | `number` | `boolean`> Defined in: [packages/otel/src/types.ts:127](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L127) Custom attributes to add to every span *** ### name? > `optional` **name**: `string` Defined in: [packages/otel/src/types.ts:108](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L108) Span name. Defaults to fn.name or "anonymous" *** ### recordArgs? > `optional` **recordArgs**: `boolean` | `string`\[] Defined in: [packages/otel/src/types.ts:116](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L116) Record function arguments as span attributes. * `false` (default): no args recorded * `true`: all args recorded * `string[]`: whitelist of argument names/indices --- --- url: /en/api/@connectum/otel/interfaces/Tracer.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / Tracer # Interface: Tracer Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:7 Tracer provides an interface for creating [Span](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.Span.html)s. ## Methods ### startActiveSpan() #### Call Signature > **startActiveSpan**<`F`>(`name`, `fn`): `ReturnType`<`F`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:67 Starts a new [Span](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.Span.html) and calls the given function passing it the created span as first argument. Additionally the new span gets set in context and this context is activated for the duration of the function call. ##### Type Parameters ###### F `F` *extends* (`span`) => `unknown` ##### Parameters ###### name `string` The name of the span ###### fn `F` function called in the context of the span and receives the newly created span as an argument ##### Returns `ReturnType`<`F`> return value of fn ##### Examples ```ts const something = tracer.startActiveSpan('op', span => { try { do some work span.setStatus({code: SpanStatusCode.OK}); return something; } catch (err) { span.setStatus({ code: SpanStatusCode.ERROR, message: err.message, }); throw err; } finally { span.end(); } }); ``` ```ts const span = tracer.startActiveSpan('op', span => { try { do some work return span; } catch (err) { span.setStatus({ code: SpanStatusCode.ERROR, message: err.message, }); throw err; } }); do some more work span.end(); ``` #### Call Signature > **startActiveSpan**<`F`>(`name`, `options`, `fn`): `ReturnType`<`F`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:68 ##### Type Parameters ###### F `F` *extends* (`span`) => `unknown` ##### Parameters ###### name `string` ###### options `SpanOptions` ###### fn `F` ##### Returns `ReturnType`<`F`> #### Call Signature > **startActiveSpan**<`F`>(`name`, `options`, `context`, `fn`): `ReturnType`<`F`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:69 ##### Type Parameters ###### F `F` *extends* (`span`) => `unknown` ##### Parameters ###### name `string` ###### options `SpanOptions` ###### context `Context` ###### fn `F` ##### Returns `ReturnType`<`F`> *** ### startSpan() > **startSpan**(`name`, `options?`, `context?`): [`Span`](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.Span.html) Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.0/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:22 Starts a new [Span](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.Span.html). Start the span without setting it on context. This method do NOT modify the current Context. #### Parameters ##### name `string` The name of the span ##### options? `SpanOptions` SpanOptions used for span creation ##### context? `Context` Context to use to extract parent #### Returns [`Span`](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.Span.html) Span The newly created span #### Example ```ts const span = tracer.startSpan('op'); span.setAttribute('key', 'value'); span.end(); ``` --- --- url: /en/guide/production/service-mesh.md description: >- Deploying Connectum gRPC services with Istio for automatic mTLS, traffic management, observability, and resilience. --- # Istio Service Mesh A service mesh adds infrastructure-level capabilities -- mTLS, traffic management, observability, and policy enforcement -- without modifying application code. This guide covers deploying Connectum services with [Istio](https://istio.io/) and how its features complement Connectum's built-in functionality. ::: tip Full Example All Istio manifests described below are available in the [production-ready/istio](https://github.com/Connectum-Framework/examples/tree/main/production-ready/istio) directory. ::: ## When to Use a Service Mesh A service mesh adds value when your deployment has: | Requirement | Without Mesh | With Istio | |---|---|---| | Mutual TLS between services | Implement per-service with `@connectum/core` TLS options | Automatic, zero-config mTLS | | Traffic splitting (canary) | Manual DNS/LB configuration | Declarative VirtualService rules | | Distributed tracing | Requires `@connectum/otel` in every service | Automatic span injection via sidecar | | Access policies | Implement in application code | Declarative AuthorizationPolicy | | Rate limiting | Application-level only | Mesh-wide + application-level | ::: tip **Rule of thumb:** If you run 3+ Connectum services that communicate with each other, a service mesh pays for itself in reduced operational complexity. For 1-2 services, Connectum's built-in features are sufficient. ::: ## Enabling Istio Sidecar Injection Label your namespace to enable automatic Envoy sidecar injection. The manifest creates the `connectum` namespace with the `istio-injection: enabled` label so that every new pod receives an Istio sidecar proxy automatically. See [namespace.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/namespace.yaml) for the full manifest. After applying, every new pod in the `connectum` namespace will automatically receive an Istio sidecar proxy. ### Verify Injection ```bash kubectl -n connectum get pods # Expected output: # NAME READY STATUS RESTARTS # order-service-7b9f8c6d4-abc12 2/2 Running 0 # ^^^ # 2 containers: app + istio-proxy ``` ## Automatic mTLS ### PeerAuthentication Enforce strict mTLS for all traffic within the namespace. This policy sets `STRICT` mode, meaning all inter-service communication must use mutual TLS. See [peer-authentication.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/peer-authentication.yaml) for the full manifest. This means: * All inter-service traffic is encrypted with mutual TLS * Both client and server identities are verified via SPIFFE certificates * No application code changes needed -- Connectum services communicate in plaintext to their local sidecar, which handles encryption ::: tip When using Istio mTLS, you do **not** need to configure TLS in `createServer()`. The sidecar handles encryption transparently. Remove any `tls` configuration from your Connectum server options to avoid double encryption: ```typescript const server = createServer({ services: [routes], port: 5000, // No tls configuration needed -- Istio handles it protocols: [Healthcheck({ httpEnabled: true }), Reflection()], }); ``` ::: ### Per-Service Override If a specific service needs to accept non-mTLS traffic (e.g., from external clients), you can override the namespace-wide policy with a `PERMISSIVE` mode selector targeting that specific service. See [peer-authentication-permissive.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/peer-authentication-permissive.yaml) for the full manifest. ## Traffic Management ### VirtualService Control how traffic flows to your Connectum services. This manifest configures timeouts, retry policies, and routing for the order-service. See [virtual-service.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/virtual-service.yaml) for the full manifest. ### DestinationRule Configure connection pooling, outlier detection, load balancing, and subset definitions (stable/canary) for your service. See [destination-rule.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/destination-rule.yaml) for the full manifest. ### Canary Deployments Route a percentage of traffic to a new version. The canary VirtualService splits traffic between stable (90%) and canary (10%) subsets. See [canary-virtual-service.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/canary-virtual-service.yaml) for the full manifest. Deploy the canary with a different version label. This Deployment creates a single-replica canary pod labeled `version: canary` running the release candidate image. See [deployment-canary.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/deployment-canary.yaml) for the full manifest. ### Header-Based Routing Route specific users or test traffic to the canary. This VirtualService matches requests with the `x-canary: true` header and sends them to the canary subset, while all other traffic goes to stable. See [header-routing.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/header-routing.yaml) for the full manifest. ## Observability Integration ### Istio Telemetry + @connectum/otel Istio sidecars automatically generate metrics, traces, and access logs. Connectum's `@connectum/otel` package provides application-level traces and metrics. Together, they offer complete observability. ```mermaid graph TB subgraph Pod["Pod: order-service"] APP["Connectum Service
@connectum/otel traces"] SIDECAR["Istio Sidecar
Network-level metrics + traces"] end subgraph Collector["OTel Collector"] RECV["OTLP Receiver"] end subgraph Backend["Observability Backend"] JAEGER["Jaeger / Tempo
(Traces)"] PROM["Prometheus
(Metrics)"] GRAFANA["Grafana
(Dashboards)"] end APP -->|"OTLP/gRPC
Application traces"| RECV SIDECAR -->|"Envoy metrics
+ access logs"| PROM SIDECAR -->|"Network traces"| RECV RECV --> JAEGER RECV --> PROM PROM --> GRAFANA JAEGER --> GRAFANA ``` ### Telemetry Resource Configure Istio to export telemetry to the same OTel Collector used by Connectum. This manifest enables tracing (100% sampling), Prometheus metrics, and OTel access logging for the namespace. See [telemetry.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/telemetry.yaml) for the full manifest. ### Trace Propagation For end-to-end traces that span both Istio sidecars and application code, Connectum's `@connectum/otel` interceptor automatically propagates W3C Trace Context headers (`traceparent`, `tracestate`). Istio's sidecar reads these same headers, creating a unified trace. The `createOtelInterceptor()` from `@connectum/otel` handles this automatically: ```typescript import { initProvider } from '@connectum/otel'; // Initialize OTel before creating the server initProvider({ serviceName: 'order-service', serviceVersion: '1.0.0', }); ``` No additional configuration is needed -- both `@connectum/otel` and Istio use OpenTelemetry-compatible trace context propagation. ## Circuit Breaking: Mesh vs Application Connectum and Istio both provide circuit breaking. Understanding when to use each is critical. ### Comparison | Feature | `@connectum/interceptors` | Istio DestinationRule | |---|---|---| | **Scope** | Per-method, per-service | Per-host (all methods) | | **Granularity** | Fine: different thresholds per RPC method | Coarse: same threshold for all methods | | **State visibility** | Application logs, custom metrics | Envoy stats, Kiali dashboard | | **Fallback** | Custom fallback handlers | 503 Unavailable | | **Retry** | Exponential backoff with jitter | Fixed retry count | | **Bulkhead** | Concurrency limiting per method | Connection pool limits | ### Recommended Strategy Use **both layers** with complementary roles: 1. **Istio (outer layer):** Outlier detection to eject unhealthy pods from the load balancer pool. This handles infrastructure-level failures (crashed pods, network issues). 2. **Connectum interceptors (inner layer):** Application-level circuit breaking with per-method configuration, custom fallbacks, and retry with backoff. This handles application-level failures (timeouts, business logic errors). For the Istio infrastructure-level resilience DestinationRule with outlier detection, see [destination-rule.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/destination-rule.yaml) (the same manifest also covers connection pooling and subsets). ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; // Connectum: application-level resilience const server = createServer({ services: [routes], port: 5000, interceptors: createDefaultInterceptors({ circuitBreaker: { threshold: 3 }, timeout: { duration: 10_000 }, retry: { maxRetries: 2 }, bulkhead: { capacity: 20, queueSize: 10 }, }), }); ``` ::: warning When using both Istio retries and Connectum retries, be careful about **retry amplification**. If Istio retries 3 times and Connectum retries 3 times, a single failing request can generate up to 9 downstream calls. Set Istio retries to 0 or 1 when using Connectum's retry interceptor. ::: ## Authorization Policies Control which services can communicate with each other. This policy allows traffic only from the API gateway service account and from pods within the `connectum` namespace, denying everything else. See [authorization-policy.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/authorization-policy.yaml) for the full manifest. ## Sidecar Resource Limits Configure resource limits for the Istio sidecar to prevent it from starving the Connectum application container. This Deployment annotates the pod template with CPU and memory requests/limits for the Envoy proxy. See [sidecar-resources.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/istio/sidecar-resources.yaml) for the full manifest. ## Health Check Configuration with Istio Istio rewrites health check probes by default. Ensure your probe configuration works correctly: ```yaml # In your Deployment pod template spec: containers: - name: order-service # Connectum healthcheck HTTP endpoints work through the Istio sidecar # because Istio automatically handles probe rewriting livenessProbe: httpGet: path: /healthz port: 5000 periodSeconds: 15 readinessProbe: httpGet: path: /readyz port: 5000 periodSeconds: 10 ``` ::: tip Istio 1.20+ rewrites HTTP health probes automatically to route through the sidecar. Connectum's `/healthz`, `/readyz`, and `/health` endpoints work without any special configuration. ::: ## Kiali: Service Mesh Dashboard [Kiali](https://kiali.io/) provides a visual dashboard for your Istio service mesh, showing real-time traffic flow between Connectum services: ```bash # Install Kiali (if not already installed with Istio) kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/kiali.yaml # Access the dashboard kubectl port-forward svc/kiali -n istio-system 20001:20001 # Open http://localhost:20001 ``` Kiali shows: * Service graph with real-time traffic * Per-service health indicators * Traffic policies and configuration validation * Distributed traces (integrated with Jaeger) ## Migration Path ### Starting Without a Mesh If you are deploying Connectum services without Istio initially: 1. Use `@connectum/core` TLS options for service-to-service encryption 2. Use `@connectum/interceptors` for all resilience patterns 3. Use `@connectum/otel` for observability ### Adding Istio Later When you add Istio: 1. **Remove** application-level TLS (`tls` option in `createServer()`) -- Istio handles it 2. **Keep** `@connectum/interceptors` for application-level resilience 3. **Keep** `@connectum/otel` -- it complements Istio's network-level telemetry 4. **Add** Istio traffic policies for infrastructure-level resilience 5. **Tune** retry settings to avoid amplification (reduce Connectum retries or Istio retries) ```typescript // Before Istio (application-level TLS) const server = createServer({ services: [routes], port: 5000, tls: { dirPath: '/etc/tls' }, // Remove this // ... }); // After Istio (no application-level TLS) const server = createServer({ services: [routes], port: 5000, // No tls -- Istio sidecar handles mTLS // ... }); ``` ## What's Next * [Kubernetes Deployment](./kubernetes.md) -- Core deployment manifests * [Envoy Gateway](./envoy-gateway.md) -- gRPC-JSON transcoding for REST clients * [Architecture Patterns](./architecture.md) -- Service communication and scaling --- --- url: /en/guide/auth/jwt.md --- # JWT Authentication `createJwtAuthInterceptor` verifies JSON Web Tokens from the `Authorization: Bearer ` header. It supports three key resolution strategies: JWKS, HMAC secret, and public key. ## JWKS (Recommended for Production) JWKS (JSON Web Key Set) is the recommended approach. The interceptor fetches and caches signing keys from the identity provider automatically: ```typescript import { createJwtAuthInterceptor } from '@connectum/auth'; const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', issuer: 'https://auth.example.com/', audience: 'my-api', maxTokenAge: '1h', claimsMapping: { roles: 'realm_access.roles', scopes: 'scope', }, }); ``` ### Options | Option | Type | Required | Description | |--------|------|----------|-------------| | `jwksUri` | `string` | Yes (if no `publicKey`/`secret`) | URL to the JWKS endpoint | | `issuer` | `string` | No | Expected `iss` claim | | `audience` | `string` | No | Expected `aud` claim | | `maxTokenAge` | `string` | No | Maximum acceptable token age (e.g. `'1h'`, `'30m'`) | | `claimsMapping` | `object` | No | Map JWT claims to `AuthContext` fields | ## HMAC Secret For simple setups and testing environments where tokens are signed with a shared secret: ```typescript const jwtAuth = createJwtAuthInterceptor({ secret: process.env.JWT_SECRET, issuer: 'my-service', }); ``` ::: warning HMAC secrets require both the issuer and the verifier to know the secret. Use JWKS in production to avoid sharing signing keys. ::: ## Public Key For asymmetric verification with a pre-loaded public key: ```typescript const publicKey = await crypto.subtle.importKey( 'spki', keyData, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, true, ['verify'], ); const jwtAuth = createJwtAuthInterceptor({ publicKey }); ``` ## Key Resolution Priority When multiple key sources are provided, resolution follows this priority: ``` jwksUri > publicKey > secret ``` At least one must be provided. If `jwksUri` is set, `publicKey` and `secret` are ignored. ## Full Example ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { createJwtAuthInterceptor, createAuthzInterceptor } from '@connectum/auth'; const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', issuer: 'https://auth.example.com/', audience: 'my-api', maxTokenAge: '1h', claimsMapping: { roles: 'realm_access.roles', scopes: 'scope', }, }); const server = createServer({ services: [routes], interceptors: [...createDefaultInterceptors(), jwtAuth], }); await server.start(); ``` ## Related * [Auth Overview](/en/guide/auth) -- all authentication strategies * [Authorization](/en/guide/auth/authorization) -- RBAC rules and programmatic authorization * [Auth Context](/en/guide/auth/context) -- accessing identity in handlers * [@connectum/auth](/en/packages/auth) -- Package Guide * [@connectum/auth API](/en/api/@connectum/auth/) -- Full API Reference --- --- url: /en/guide/production/kubernetes.md description: >- Complete Kubernetes manifests for deploying Connectum gRPC/ConnectRPC microservices with health probes, auto-scaling, and graceful shutdown. --- # Kubernetes Deployment This guide provides production-ready Kubernetes manifests for deploying Connectum services. It covers Deployment, Service, ConfigMap, Secrets, HPA, probes, and graceful shutdown integration. ::: tip Full Example All Kubernetes manifests described below are available in the [production-ready/k8s](https://github.com/Connectum-Framework/examples/tree/main/production-ready/k8s) directory. ::: ## Architecture Overview ```mermaid graph TB subgraph Cluster["Kubernetes Cluster"] subgraph NS["namespace: connectum"] subgraph Deploy["Deployment: order-service"] POD1["Pod 1
:5000"] POD2["Pod 2
:5000"] POD3["Pod 3
:5000"] end SVC["Service: order-service
ClusterIP :5000"] CM["ConfigMap:
order-service-config"] SEC["Secret:
order-service-tls"] HPA["HPA: 2-10 replicas
CPU 70%, Memory 80%"] end INGRESS["Gateway / Ingress
External Access"] end SVC --> POD1 SVC --> POD2 SVC --> POD3 HPA --> Deploy CM -.-> Deploy SEC -.-> Deploy INGRESS --> SVC ``` ## Namespace Create a dedicated namespace for your Connectum services. The manifest creates a `connectum` namespace with standard labels and an optional Istio sidecar injection annotation. See [namespace.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/k8s/namespace.yaml) for the full manifest. ## ConfigMap Store non-sensitive configuration in a ConfigMap. This manifest defines environment variables for the service port, logging, graceful shutdown, OpenTelemetry export, and downstream service addresses. See [configmap.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/k8s/configmap.yaml) for the full manifest. ## Secret Store TLS certificates and sensitive configuration in Secrets. The manifest creates a `kubernetes.io/tls` secret for the service's TLS certificate and private key. See [secret-tls.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/k8s/secret-tls.yaml) for the full manifest. ::: tip For production TLS, consider using [cert-manager](https://cert-manager.io/) to automatically provision and renew certificates. If you are using Istio, the service mesh handles mTLS automatically and you may not need application-level TLS at all. ::: ## Deployment The core manifest. Pay close attention to probes, resource limits, and graceful shutdown configuration. This manifest configures a 3-replica Deployment with rolling update strategy, pod security context, topology spread constraints, startup/liveness/readiness probes against Connectum's HTTP health endpoints, resource limits, and a `preStop` hook for graceful endpoint de-registration. See [deployment.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/k8s/deployment.yaml) for the full manifest. ## Service ### ClusterIP (Internal gRPC Traffic) For service-to-service communication within the cluster. This manifest creates a ClusterIP Service on port 5000 with `appProtocol: grpc` to hint service meshes and ingress controllers about the protocol. See [service.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/k8s/service.yaml) for the full manifest. ### LoadBalancer (External gRPC Access) For direct external gRPC access (without a gateway). This manifest creates a LoadBalancer Service on port 443 with cloud provider annotations (e.g., AWS NLB with HTTP/2 backend protocol). See [service-external.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/k8s/service-external.yaml) for the full manifest. ## Horizontal Pod Autoscaler (HPA) Scale based on CPU and memory utilization. This manifest configures an HPA that scales from 2 to 10 replicas based on 70% CPU and 80% memory thresholds, with stabilization windows and rate-limited scale-up/scale-down policies. See [hpa.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/k8s/hpa.yaml) for the full manifest. ## RBAC Minimal ServiceAccount for the service. The manifest creates a dedicated ServiceAccount; add RoleBindings if the service needs Kubernetes API access. See [rbac.yaml](https://github.com/Connectum-Framework/examples/blob/main/production-ready/k8s/rbac.yaml) for the full manifest. ## Graceful Shutdown Deep Dive Connectum's graceful shutdown integrates with Kubernetes' pod termination lifecycle. Understanding the sequence is critical for zero-downtime deployments. ### Pod Termination Sequence ```mermaid sequenceDiagram participant K8s as Kubernetes participant Pod as Pod participant CT as Connectum Server participant EP as Endpoints Controller K8s->>Pod: 1. Mark pod as Terminating K8s->>EP: 2. Remove pod from Service endpoints K8s->>Pod: 3. Execute preStop hook (sleep 5) Note over Pod: 5s delay for endpoint de-registration to propagate K8s->>CT: 4. Send SIGTERM CT->>CT: 5. autoShutdown catches SIGTERM CT->>CT: 6. Emit "stopping" event CT->>CT: 7. healthcheckManager → NOT_SERVING CT->>CT: 8. Run shutdown hooks (dependency order) CT->>CT: 9. Close HTTP/2 server CT->>CT: 10. Wait for in-flight requests (up to timeout) CT->>CT: 11. Emit "stop" event Note over K8s,CT: If timeout exceeded → SIGKILL after terminationGracePeriodSeconds ``` ### Configuration alignment It is essential to align timeouts: ``` preStop sleep : 5s shutdown.timeout : 30s (Connectum) terminationGracePeriod : 35s (Kubernetes, must be >= preStop + shutdown.timeout) ``` ::: danger If `terminationGracePeriodSeconds` is shorter than the sum of `preStop` delay and `shutdown.timeout`, Kubernetes will SIGKILL the pod before Connectum finishes graceful shutdown, causing dropped requests. ::: ### Connectum Server Configuration ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { shutdownProvider } from '@connectum/otel'; const server = createServer({ services: [routes], port: 5000, protocols: [ Healthcheck({ httpEnabled: true }), Reflection(), ], shutdown: { autoShutdown: true, // Catch SIGTERM and SIGINT timeout: 30000, // 30s to drain connections signals: ['SIGTERM', 'SIGINT'], forceCloseOnTimeout: true, // Force-close after timeout }, }); // Register shutdown hooks with dependency ordering server.onShutdown('otel', async () => { await shutdownProvider(); }); server.onShutdown('database', async () => { await db.close(); }); // OTel depends on database (database shuts down first) server.onShutdown('otel', ['database'], async () => { await shutdownProvider(); }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); server.on('stopping', () => { // Kubernetes readiness probe will start failing // because healthcheckManager transitions to NOT_SERVING console.log('Server is shutting down...'); }); await server.start(); ``` ## Probes: Choosing the Right Path Connectum's `@connectum/healthcheck` package exposes three HTTP paths by default (when `httpEnabled: true`): | Path | Purpose | Kubernetes Probe | Response | |---|---|---|---| | `/healthz` | Is the process alive? | `livenessProbe` | 200 if serving, 503 if not | | `/readyz` | Ready for traffic? | `readinessProbe` | 200 if serving, 503 if not | | `/health` | General health (alias) | Any | Same as above | All three paths check the same `healthcheckManager` status. You can differentiate behavior by registering per-service health statuses: ```typescript // Report individual service health healthcheckManager.update(ServingStatus.SERVING, 'mycompany.orders.v1.OrderService'); // Check via HTTP // GET /healthz?service=mycompany.orders.v1.OrderService ``` ## Complete Deployment Script Apply all manifests: ```bash # Create namespace kubectl apply -f namespace.yaml # Deploy configuration kubectl apply -f configmap.yaml kubectl apply -f secret-tls.yaml # if using application-level TLS kubectl apply -f rbac.yaml # Deploy service kubectl apply -f deployment.yaml kubectl apply -f service.yaml kubectl apply -f hpa.yaml # Verify kubectl -n connectum get pods -w kubectl -n connectum get svc # Check health kubectl -n connectum exec -it deploy/order-service -- curl http://localhost:5000/healthz # View logs kubectl -n connectum logs -f deploy/order-service # Check HPA status kubectl -n connectum get hpa order-service ``` ## Namespace Strategy For multi-environment setups: | Namespace | Purpose | Services | |---|---|---| | `connectum-dev` | Development environment | All services, relaxed limits | | `connectum-staging` | Pre-production testing | All services, prod-like config | | `connectum` | Production | All services, strict policies | | `observability` | Monitoring stack | OTel Collector, Jaeger, Prometheus, Grafana | ## What's Next * [Envoy Gateway](./envoy-gateway.md) -- Expose gRPC services as REST APIs via Envoy * [Service Mesh with Istio](./service-mesh.md) -- Automatic mTLS and advanced traffic management * [Microservice Architecture](./architecture.md) -- Architecture patterns and service communication --- --- url: /en/guide/health-checks/kubernetes.md --- # Kubernetes Integration Configure Kubernetes liveness and readiness probes with Connectum health checks, and integrate with graceful shutdown for zero-downtime deployments. ## HTTP Probes HTTP probes are the simplest approach and work with all Kubernetes versions: ```yaml apiVersion: v1 kind: Pod spec: containers: - name: my-service image: my-service:latest ports: - containerPort: 5000 livenessProbe: httpGet: path: /healthz port: 5000 initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /readyz port: 5000 initialDelaySeconds: 3 periodSeconds: 5 failureThreshold: 2 ``` Requires `httpEnabled: true` in the Healthcheck protocol: ```typescript protocols: [Healthcheck({ httpEnabled: true })] ``` ## gRPC Probes (Kubernetes 1.24+) Kubernetes 1.24+ supports gRPC health probes natively: ```yaml livenessProbe: grpc: port: 5000 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: grpc: port: 5000 service: my.service.v1.MyService initialDelaySeconds: 3 periodSeconds: 5 ``` gRPC probes use the `grpc.health.v1.Health/Check` method directly -- no HTTP endpoint needed. ## Graceful Shutdown Integration Combine health checks with lifecycle events for zero-downtime deployments: ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus, } from '@connectum/healthcheck'; const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true })], shutdown: { autoShutdown: true, timeout: 25000, // Less than Kubernetes terminationGracePeriodSeconds }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); // When shutdown begins, mark as NOT_SERVING // Kubernetes stops routing traffic to this pod server.on('stopping', () => { healthcheckManager.update(ServingStatus.NOT_SERVING); }); await server.start(); ``` ### Pod Specification ```yaml apiVersion: v1 kind: Pod spec: terminationGracePeriodSeconds: 30 # Must be > shutdown.timeout containers: - name: my-service image: my-service:latest ports: - containerPort: 5000 readinessProbe: httpGet: path: /healthz port: 5000 periodSeconds: 5 lifecycle: preStop: exec: # Give load balancers time to remove this pod command: ["sleep", "5"] ``` ## Shutdown Timeline ``` 0s SIGTERM received (Kubernetes sends SIGTERM) 0s 'stopping' event -> healthcheckManager.update(NOT_SERVING) 0-5s Kubernetes removes pod from service endpoints 5s In-flight requests drain 25s Shutdown timeout (forceCloseOnTimeout: true) 25s Shutdown hooks execute 25s 'stop' event 30s Kubernetes terminationGracePeriodSeconds (hard kill) ``` ::: danger Critical Always set `shutdown.timeout` to a value **less than** Kubernetes `terminationGracePeriodSeconds`. Otherwise, Kubernetes may SIGKILL the process before your shutdown hooks complete. ::: ## Related * [Health Checks Overview](/en/guide/health-checks) -- back to overview * [Protocol Details](/en/guide/health-checks/protocol) -- gRPC methods, HTTP endpoints, configuration * [Graceful Shutdown](/en/guide/server/graceful-shutdown) -- shutdown options, hooks, lifecycle events * [Kubernetes Deployment](/en/guide/production/kubernetes) -- full deployment guide * [@connectum/healthcheck](/en/packages/healthcheck) -- Package Guide --- --- url: /en/api/@connectum/interceptors/logger.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / logger # logger Logger interceptor Logs all RPC requests and responses for debugging. ## Functions * [createLoggerInterceptor](functions/createLoggerInterceptor.md) --- --- url: /en/api/@connectum/otel/logger.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / logger # logger ## Interfaces * [Logger](interfaces/Logger.md) * [LoggerOptions](interfaces/LoggerOptions.md) ## Functions * [getLogger](functions/getLogger.md) --- --- url: /en/guide/observability/logging.md --- # Logging Structured logging with OpenTelemetry integration through the `@connectum/otel` package. ## Using getLogger() `getLogger()` provides structured logging with automatic trace correlation: ```typescript import { getLogger } from '@connectum/otel'; const logger = getLogger('OrderService'); // Console-like convenience methods logger.info('Order created', { orderId: '123', userId: 'user-456' }); logger.warn('Low stock', { sku: 'ABC', remaining: 2 }); logger.error('Payment failed', { error: 'timeout', orderId: '123' }); logger.debug('Processing step', { step: 3, total: 5 }); ``` ## Default Attributes Add attributes that appear in every log entry: ```typescript const logger = getLogger('PaymentService', { defaultAttributes: { 'service.layer': 'domain', env: 'production', }, }); logger.info('Charge created'); // Attributes: { "logger.name": "PaymentService", "service.layer": "domain", env: "production" } ``` ## Raw OpenTelemetry LogRecord For advanced use cases, emit raw OTel log records: ```typescript import { SeverityNumber } from '@opentelemetry/api-logs'; logger.emit({ severityNumber: SeverityNumber.INFO, severityText: 'INFO', body: 'Custom log record', attributes: { custom: true }, timestamp: Date.now(), }); ``` ::: info Trace Correlation When an active span exists, the OpenTelemetry SDK automatically includes `trace_id` and `span_id` in log records. No manual correlation needed. ::: ## Related * [Observability Overview](/en/guide/observability) -- back to overview * [Tracing](/en/guide/observability/tracing) -- distributed tracing setup * [Backends & Configuration](/en/guide/observability/backends) -- configure log exporters * [@connectum/otel](/en/packages/otel) -- Package Guide * [@connectum/otel API](/en/api/@connectum/otel/) -- Full API Reference --- --- url: /en/api/@connectum/otel/meter.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / meter # meter Lazy access to the global OpenTelemetry Meter ## Functions * [getMeter](functions/getMeter.md) --- --- url: /en/api/@connectum/interceptors/method-filter.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / method-filter # method-filter Method filter interceptor Routes interceptors to specific methods based on wildcard pattern matching. Provides declarative per-method interceptor configuration without boilerplate. ## Functions * [createMethodFilterInterceptor](functions/createMethodFilterInterceptor.md) --- --- url: /en/api/@connectum/otel/metrics.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / metrics # metrics RPC Metrics for OpenTelemetry Provides pre-configured histograms for measuring RPC server and client performance based on OpenTelemetry semantic conventions for RPC metrics. ## See https://opentelemetry.io/docs/specs/semconv/rpc/rpc-metrics/ ## Interfaces * [RpcClientMetrics](interfaces/RpcClientMetrics.md) * [RpcServerMetrics](interfaces/RpcServerMetrics.md) ## Functions * [createRpcClientMetrics](functions/createRpcClientMetrics.md) * [createRpcServerMetrics](functions/createRpcServerMetrics.md) --- --- url: /en/guide/observability/metrics.md --- # Metrics Collect application and RPC metrics using OpenTelemetry's `Meter` API through the `@connectum/otel` package. ## Using getMeter() `getMeter()` returns a lazy singleton OpenTelemetry meter: ```typescript import { getMeter } from '@connectum/otel'; const meter = getMeter(); ``` ### Counter Track cumulative values that only increase: ```typescript const requestCounter = meter.createCounter('http.requests.total', { description: 'Total number of requests', unit: '1', }); requestCounter.add(1, { method: 'GET', status: 200 }); ``` ### Histogram Record distributions of values (e.g., latencies): ```typescript const latencyHistogram = meter.createHistogram('request.duration', { description: 'Request duration in milliseconds', unit: 'ms', }); latencyHistogram.record(125.5, { method: 'GET' }); ``` ### UpDownCounter Track values that can increase and decrease (e.g., active connections): ```typescript const activeConnections = meter.createUpDownCounter('connections.active'); activeConnections.add(1); // Connection opened activeConnections.add(-1); // Connection closed ``` ### ObservableGauge Report values on demand (pulled during export): ```typescript meter.createObservableGauge('memory.heap_used', { description: 'Heap memory usage', unit: 'bytes', }).addCallback((result) => { result.observe(process.memoryUsage().heapUsed); }); ``` ## RPC Metrics (Automatic) The OTel interceptors automatically record standard RPC metrics. **Server metrics** (via `createOtelInterceptor`): * `rpc.server.duration` -- request duration histogram * `rpc.server.request.size` -- request message size * `rpc.server.response.size` -- response message size **Client metrics** (via `createOtelClientInterceptor`): * `rpc.client.duration` -- request duration histogram * `rpc.client.request.size` -- request message size * `rpc.client.response.size` -- response message size ## Related * [Observability Overview](/en/guide/observability) -- back to overview * [Tracing](/en/guide/observability/tracing) -- server/client interceptors, deep tracing * [Backends & Configuration](/en/guide/observability/backends) -- configure metrics exporters * [@connectum/otel](/en/packages/otel) -- Package Guide * [@connectum/otel API](/en/api/@connectum/otel/) -- Full API Reference --- --- url: /en/guide/production/architecture.md description: >- Scalable architecture patterns for production gRPC/ConnectRPC microservices with Connectum framework. --- # Microservice Architecture Patterns This guide covers proven architecture patterns for building production-grade gRPC/ConnectRPC microservices with Connectum. It addresses service communication, proto-first design workflows, service discovery, and repository strategies. ## High-Level Production Architecture A typical Connectum production deployment consists of multiple gRPC services behind an API gateway, with centralized observability and service discovery. ```mermaid graph TB subgraph External["External Clients"] REST["REST / Web Clients"] GRPC_EXT["gRPC Clients"] end subgraph Gateway["API Gateway Layer"] ENVOY["Envoy Gateway
gRPC-JSON Transcoding"] end subgraph Mesh["Service Mesh (Istio)"] subgraph Services["Connectum Services"] SVC_A["Service A
:5000"] SVC_B["Service B
:5001"] SVC_C["Service C
:5002"] end subgraph Sidecar["Envoy Sidecars"] PROXY_A["Sidecar A"] PROXY_B["Sidecar B"] PROXY_C["Sidecar C"] end end subgraph Observability["Observability Stack"] OTEL_COL["OTel Collector"] JAEGER["Jaeger / Tempo"] PROM["Prometheus"] GRAFANA["Grafana"] end subgraph Infrastructure["Infrastructure"] K8S["Kubernetes"] REGISTRY["Container Registry"] PROTO_REPO["Proto Registry (BSR)"] end REST --> ENVOY GRPC_EXT --> ENVOY ENVOY --> PROXY_A ENVOY --> PROXY_B SVC_A <--> PROXY_A SVC_B <--> PROXY_B SVC_C <--> PROXY_C PROXY_A <--> PROXY_B PROXY_B <--> PROXY_C SVC_A --> OTEL_COL SVC_B --> OTEL_COL SVC_C --> OTEL_COL OTEL_COL --> JAEGER OTEL_COL --> PROM PROM --> GRAFANA JAEGER --> GRAFANA ``` ## Proto-First Design Workflow Connectum follows a **proto-first** approach: you define your API contract in Protocol Buffers before writing any service code. This guarantees type safety, backward compatibility checks, and automatic documentation generation. ### Workflow ```mermaid sequenceDiagram participant Dev as Developer participant Proto as Proto Files participant Buf as buf CLI participant Gen as Code Generation participant Svc as Connectum Service Dev->>Proto: 1. Define .proto schema Dev->>Buf: 2. buf lint Buf-->>Dev: Lint results Dev->>Buf: 3. buf breaking --against main Buf-->>Dev: Breaking change report Dev->>Gen: 4. buf generate (protoc-gen-es) Gen-->>Svc: Generated TypeScript stubs Dev->>Svc: 5. Implement service handlers Svc-->>Dev: Type-safe gRPC service ``` ### Proto file structure Organize proto files in a dedicated directory with clear versioning: ``` proto/ ├── buf.yaml # Buf module configuration ├── buf.gen.yaml # Code generation configuration └── mycompany/ └── myservice/ └── v1/ ├── myservice.proto # Service definition └── types.proto # Shared message types ``` ### Example proto definition ```protobuf syntax = "proto3"; package mycompany.orders.v1; import "google/api/annotations.proto"; import "buf/validate/validate.proto"; service OrderService { // Create a new order rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse) { option (google.api.http) = { post: "/v1/orders" body: "*" }; } // Get order by ID rpc GetOrder(GetOrderRequest) returns (Order) { option (google.api.http) = { get: "/v1/orders/{order_id}" }; } } message CreateOrderRequest { string customer_id = 1 [(buf.validate.field).string.min_len = 1]; repeated OrderItem items = 2 [(buf.validate.field).repeated.min_items = 1]; } ``` ::: tip Common proto definitions such as `google.api.http`, `google.api.annotations`, `google.protobuf.*`, `buf.validate`, and `openapiv3` schemas are available through buf BSR deps. Add them to your `buf.yaml` as dependencies and import them directly without vendoring. ::: ::: tip Guide-Level Documentation For a quick introduction to inter-service calls, see [Service Communication](/en/guide/service-communication). ::: ## Service Communication Patterns ### Synchronous: Unary gRPC The most common pattern. One service calls another and waits for a response. ```typescript import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { OrderService } from '#gen/mycompany/orders/v1/orders_pb.js'; const transport = createGrpcTransport({ baseUrl: 'http://order-service:5000', httpVersion: '2', }); const client = createClient(OrderService, transport); const order = await client.getOrder({ orderId: '123' }); ``` ### Synchronous: Server Streaming For real-time data feeds -- the server sends a stream of messages in response to a single request. ```typescript for await (const update of client.watchOrderStatus({ orderId: '123' })) { console.log(`Order status: ${update.status}`); } ``` ### Service-to-Service Communication When Service A needs data from Service B, create a gRPC client within Service A's handler: ```typescript import { createServer } from '@connectum/core'; import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { InventoryService } from '#gen/inventory/v1/inventory_pb.js'; // Create client to downstream service const inventoryTransport = createGrpcTransport({ baseUrl: `http://${process.env.INVENTORY_SERVICE_HOST}:${process.env.INVENTORY_SERVICE_PORT}`, httpVersion: '2', }); const inventoryClient = createClient(InventoryService, inventoryTransport); // Use in your service handler const routes = (router) => { router.service(OrderService, { async createOrder(req) { // Call downstream service const stock = await inventoryClient.checkStock({ sku: req.sku }); if (!stock.available) { throw new ConnectError('Out of stock', Code.FailedPrecondition); } // ... create order }, }); }; ``` ## Service Discovery ### gRPC Server Reflection Every Connectum service can expose its API contract at runtime via gRPC Server Reflection. This allows tools like `grpcurl`, `grpcui`, and service meshes to discover available methods without proto files. ```typescript import { createServer } from '@connectum/core'; import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [routes], protocols: [Reflection()], port: 5000, }); ``` ### Kubernetes DNS-Based Discovery In Kubernetes, services are discoverable via DNS. A Connectum service deployed as `order-service` in namespace `production` is reachable at: ``` order-service.production.svc.cluster.local:5000 ``` No external service registry is needed -- Kubernetes DNS handles resolution automatically. ### Proto Registry with Buf Schema Registry (BSR) For large organizations, use [Buf Schema Registry](https://buf.build/product/bsr) to centralize proto management: 1. **Push** proto files to BSR on merge to main 2. **Pull** specific service protos when generating client code 3. **Check** backward compatibility automatically in CI ```yaml # buf.yaml version: v2 modules: - path: proto name: buf.build/mycompany/services deps: - buf.build/googleapis/googleapis - buf.build/bufbuild/protovalidate ``` ## Repository Strategies ### Monorepo (Recommended for Small-Medium Teams) All services share a single repository. Proto files, shared libraries, and deployment configs live together. ``` my-platform/ ├── proto/ # All proto definitions │ ├── buf.yaml │ └── mycompany/ │ ├── orders/v1/ │ ├── inventory/v1/ │ └── payments/v1/ ├── packages/ │ ├── shared/ # Shared utilities │ ├── order-service/ # Connectum service │ ├── inventory-service/ # Connectum service │ └── payment-service/ # Connectum service ├── pnpm-workspace.yaml └── turbo.json ``` **Advantages:** * Atomic changes across services and protos * Single CI/CD pipeline * Easy code sharing via workspace dependencies * `pnpm` + Turborepo for efficient builds **Disadvantages:** * CI time grows with repo size * Deployment coupling (mitigated by Turborepo's `--filter`) ### Polyrepo (Recommended for Large Organizations) Each service is an independent repository. Proto files live in a dedicated proto registry repository. ``` github.com/mycompany/ ├── proto-registry/ # Proto definitions (BSR-synced) ├── order-service/ # Independent Connectum service ├── inventory-service/ # Independent Connectum service └── shared-libs/ # Shared npm packages ``` **Advantages:** * Independent deployment cycles * Clear ownership boundaries * Per-repo access control **Disadvantages:** * Cross-service changes require multiple PRs * Proto versioning complexity * Dependency management overhead ## Inter-Service Communication Patterns ### Request-Response Chain ```mermaid sequenceDiagram participant GW as API Gateway participant OS as Order Service participant IS as Inventory Service participant PS as Payment Service GW->>OS: CreateOrder (gRPC) OS->>IS: CheckStock (gRPC) IS-->>OS: StockResponse OS->>PS: ProcessPayment (gRPC) PS-->>OS: PaymentResponse OS-->>GW: OrderResponse ``` ### Fan-Out / Fan-In When a service needs to call multiple downstream services in parallel: ```typescript async createOrder(req) { // Fan-out: parallel calls to independent services const [stock, pricing, customerProfile] = await Promise.all([ inventoryClient.checkStock({ sku: req.sku }), pricingClient.getPrice({ sku: req.sku }), customerClient.getProfile({ customerId: req.customerId }), ]); // Fan-in: combine results return createOrderFromData(stock, pricing, customerProfile); } ``` ### Circuit Breaker at Application Level Connectum's built-in interceptor chain includes a circuit breaker. For inter-service calls, configure it per-client: ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; const transport = createGrpcTransport({ baseUrl: 'http://inventory-service:5000', httpVersion: '2', interceptors: createDefaultInterceptors({ circuitBreaker: { failureThreshold: 5 }, timeout: { timeoutMs: 5000 }, retry: { maxRetries: 2 }, // Disable server-side-only interceptors bulkhead: false, errorHandler: false, serializer: false, validation: false, }), }); ``` ## Scaling Considerations ### Horizontal Scaling Connectum services are stateless by design. Scale horizontally by increasing replica count: | Load Profile | Replicas | CPU Request | Memory Request | |---|---|---|---| | Low (< 100 RPS) | 2 | 100m | 128Mi | | Medium (100-1000 RPS) | 3-5 | 250m | 256Mi | | High (> 1000 RPS) | 5-20 | 500m | 512Mi | ### Connection Management gRPC uses HTTP/2 with persistent connections. Behind a load balancer, use **client-side load balancing** or an L7 proxy (Envoy) to distribute requests across pods -- a single HTTP/2 connection to a Kubernetes Service will pin to one pod. ::: warning Kubernetes `ClusterIP` services perform L4 (connection-level) load balancing. For gRPC, this means all requests on a single HTTP/2 connection go to the same pod. Use a service mesh (Istio/Linkerd) or Envoy for proper L7 (request-level) load balancing. ::: ### Health-Based Routing Connectum's health check system integrates with Kubernetes and service meshes: * **Liveness**: `GET /healthz` -- is the process alive? * **Readiness**: `GET /readyz` -- is the service ready to accept traffic? * **gRPC Health**: `grpc.health.v1.Health/Check` -- standard gRPC health protocol Configure your service to report `SERVING` only after all dependencies are initialized: ```typescript import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true })], port: 5000, shutdown: { autoShutdown: true, timeout: 30000 }, }); server.on('ready', async () => { // Initialize downstream connections, caches, etc. await initializeDependencies(); // Only then report as serving healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ## What's Next * [Docker Containerization](./docker.md) -- Package your Connectum service as a container * [Kubernetes Deployment](./kubernetes.md) -- Deploy to Kubernetes with proper probes and scaling * [Envoy Gateway](./envoy-gateway.md) -- Expose gRPC services as REST APIs * [Service Mesh with Istio](./service-mesh.md) -- Advanced traffic management and mTLS --- --- url: /en/migration.md description: Migration guides and breaking changes for Connectum releases --- # Migration & Changelog This page covers breaking changes and migration steps between Connectum releases. ## RC.3 to RC.4 ### Compile-Before-Publish with tsup All `@connectum/*` packages now ship compiled `.js` + `.d.ts` + source maps via [tsup](https://tsup.egoist.dev/). This is the most significant change in rc.4. | Aspect | Before (rc.3) | After (rc.4) | |--------|---------------|--------------| | Published format | Raw `.ts` source | Compiled `.js` + `.d.ts` + `.js.map` | | Consumer Node.js | >= 25.2.0 (type stripping required) | **>= 18.0.0** (compiled JS) | | Loader/register hook | Required `@connectum/core/register` | **Not needed** | | Runtime compatibility | Node.js 25+ only | Node.js 18+, Bun, tsx | **Migration**: Remove any `--import @connectum/core/register` flags or `register()` calls from your startup scripts. Packages now work out of the box. ### Removed: `@connectum/core/register` The `@connectum/core/register` subpath export has been removed. It was needed in rc.3 to enable type stripping for raw TypeScript source. Since packages now ship compiled JavaScript, no register hook is needed. ```diff - node --import @connectum/core/register server.ts + node server.js ``` ### New Package: `@connectum/auth` A complete authentication and authorization package with 5 interceptor factories: * `createAuthInterceptor()` -- Generic pluggable auth * `createJwtAuthInterceptor()` -- JWT with JWKS support (jose) * `createGatewayAuthInterceptor()` -- Gateway pre-authenticated headers * `createSessionAuthInterceptor()` -- Session-based auth (better-auth, lucia) * `createAuthzInterceptor()` -- Declarative rules-based authorization See [@connectum/auth documentation](/en/packages/auth) for details. ### Error Handler: SanitizableError Protocol The error handler interceptor in `@connectum/interceptors` now recognizes the `SanitizableError` protocol from `@connectum/core`. Errors implementing this interface have their `clientMessage` sent to clients while `serverDetails` are preserved for logging. New `onError` callback option replaces `console.error` for structured error handling: ```typescript createDefaultInterceptors({ errorHandler: { onError: ({ error, code, serverDetails, stack }) => { logger.error('RPC error', { code, serverDetails }); }, }, }); ``` ### OpenTelemetry: Streaming RPC Support `@connectum/otel` now instruments streaming RPCs (client, server, and bidirectional). Span lifecycle is deferred to stream completion for accurate duration measurement. ### Cross-Runtime Testing All packages now include `test:bun` and `test:esbuild` scripts via `@exodus/test`. Known incompatibilities (interceptors/bun, otel/bun, cli/bun) gracefully skip. ### Build Pipeline Changes The turbo build graph has changed: ``` build:proto → build (tsup) → typecheck (tsc --noEmit) → test ``` `typecheck` and `test` now **depend on `build`** (they require compiled `dist/` artifacts). Always run `pnpm build` before `pnpm typecheck` or `pnpm test`. ## Breaking Changes from Alpha If you are migrating from v0.2.0-alpha.x to v1.0.0-beta.x, the following breaking changes apply: ### API Changes | Before (alpha) | After (beta) | ADR | |----------------|--------------|-----| | `Runner(options)` | `createServer(options)` | [ADR-023](/en/contributing/adr/023-uniform-registration-api) | | `withReflection()` | `Reflection()` | [ADR-022](/en/contributing/adr/022-protocol-extraction) | | `withHealthcheck()` | `Healthcheck({ httpEnabled: true })` | [ADR-022](/en/contributing/adr/022-protocol-extraction) | | `server.health.update()` | `healthcheckManager.update()` | [ADR-022](/en/contributing/adr/022-protocol-extraction) | | `builtinInterceptors: { ... }` | `interceptors: createDefaultInterceptors({ ... })` | [ADR-023](/en/contributing/adr/023-uniform-registration-api) | ### Package Changes | Change | Details | |--------|---------| | New package: `@connectum/healthcheck` | Extracted from `@connectum/core` | | New package: `@connectum/reflection` | Extracted from `@connectum/core` | | New package: `@connectum/cli` | Proto sync via gRPC Server Reflection | | Removed: `@connectum/utilities` | Merged into other packages | | Total packages | 6 → 7 | ### Migration Example **Before (alpha):** ```typescript import { Runner } from '@connectum/core'; const server = Runner({ services: [routes], port: 5000, builtinInterceptors: { errorHandler: true, logger: { level: 'debug' }, tracing: true, }, health: { enabled: true }, reflection: true, }); server.on('ready', () => { server.health.update(ServingStatus.SERVING); }); await server.start(); ``` **After (beta):** ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: createDefaultInterceptors(), shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ### Interceptors: No Auto-Defaults Starting from v1.0.0-beta.x, `@connectum/core` has **zero internal dependencies**. Omitting the `interceptors` option (or passing `[]`) means **no interceptors are applied**. To use the default resilience chain, explicitly pass `createDefaultInterceptors()`: ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; const server = createServer({ services: [routes], interceptors: createDefaultInterceptors(), // explicit! }); ``` ## Key Resolution Priority Change (`@connectum/auth`) **Affected versions**: v1.0.0-beta.x onwards The JWT auth interceptor (`createJwtAuthInterceptor`) changed the key resolution priority when multiple key sources are provided: | | Before | After | |---|--------|-------| | **Priority** | `jwksUri` > `secret` > `publicKey` | `jwksUri` > `publicKey` > `secret` | ### Impact This change only affects configurations that provide **both** `publicKey` and `secret` simultaneously. Previously the symmetric `secret` was used; now the asymmetric `publicKey` takes precedence. * **No impact** if you use only one of `jwksUri`, `secret`, or `publicKey` * **No impact** if you use `jwksUri` (highest priority, unchanged) ### Migration If you rely on the previous behavior where `secret` wins over `publicKey`, remove the `publicKey` option: ```typescript // Before: secret was used (publicKey was ignored) const auth = createJwtAuthInterceptor({ secret: process.env.JWT_SECRET, publicKey: myPublicKey, // was silently ignored }); // After: remove publicKey if you want HMAC verification const auth = createJwtAuthInterceptor({ secret: process.env.JWT_SECRET, }); ``` ### Rationale Asymmetric keys (`publicKey`) are cryptographically stronger than symmetric secrets (`secret`). When both are provided, the more secure option should take precedence. ## Changelog For detailed changelog, see the [CHANGELOG.md](https://github.com/Connectum-Framework/connectum/blob/main/CHANGELOG.md) in the main repository. ## Architecture Decision Records All significant architectural decisions are documented as ADRs: * [ADR Index](/en/contributing/adr/) -- Complete list of 23 Architecture Decision Records --- --- url: /en/guide/security/mtls.md --- # Mutual TLS (mTLS) Mutual TLS enables both client and server to authenticate each other, providing strong identity verification for service-to-service communication. ## mTLS Configuration For mutual TLS, use the `http2Options` parameter alongside `tls`: ```typescript import { readFileSync } from 'node:fs'; import { createServer, readTLSCertificates } from '@connectum/core'; const server = createServer({ services: [routes], port: 5000, tls: { keyPath: './keys/server.key', certPath: './keys/server.crt', }, http2Options: { // Require client certificates requestCert: true, rejectUnauthorized: true, // CA certificate(s) used to verify client certificates ca: readFileSync('./keys/ca.crt'), }, }); await server.start(); ``` | Option | Description | |--------|-------------| | `requestCert` | Require the client to present a certificate | | `rejectUnauthorized` | Reject connections with invalid/untrusted certificates | | `ca` | CA certificate(s) used to verify client certificates | ## mTLS Client Configuration When connecting to an mTLS-enabled server: ```bash # grpcurl with client certificate grpcurl \ -cacert keys/ca.crt \ -cert keys/client.crt \ -key keys/client.key \ localhost:5000 list ``` ## Production TLS Best Practices ### Use a Certificate Manager In production, manage certificates through: * **Kubernetes cert-manager** for automatic certificate provisioning and renewal * **Vault** (HashiCorp) for certificate management * **Let's Encrypt** for free, automated certificates ### Environment-Based Configuration Use environment variables to avoid hardcoding paths: ```typescript const server = createServer({ services: [routes], port: 5000, tls: process.env.TLS_ENABLED === 'true' ? { keyPath: process.env.TLS_KEY_PATH, certPath: process.env.TLS_CERT_PATH, } : undefined, }); ``` ### Kubernetes TLS with Secrets Mount TLS certificates as Kubernetes secrets: ```yaml apiVersion: v1 kind: Pod spec: containers: - name: my-service image: my-service:latest env: - name: TLS_DIR_PATH value: /etc/tls volumeMounts: - name: tls-certs mountPath: /etc/tls readOnly: true volumes: - name: tls-certs secret: secretName: my-service-tls ``` ```typescript const server = createServer({ services: [routes], tls: { dirPath: process.env.TLS_DIR_PATH, }, }); ``` ::: danger Security Never commit TLS private keys to version control. Use environment variables, secrets managers, or mounted volumes in production. ::: ### Enforce TLS 1.3 For maximum security, enforce TLS 1.3: ```typescript const server = createServer({ services: [routes], tls: { keyPath: './keys/server.key', certPath: './keys/server.crt', }, http2Options: { minVersion: 'TLSv1.3', }, }); ``` ## Related * [Security Overview](/en/guide/security) -- back to overview * [TLS Configuration](/en/guide/security/tls) -- TLS options, utility functions, self-signed certs * [Kubernetes Deployment](/en/guide/production/kubernetes) -- full deployment guide * [@connectum/core](/en/packages/core) -- Package Guide * [@connectum/core API](/en/api/@connectum/core/) -- Full API Reference --- --- url: /en/guide/observability.md --- # Observability First-class OpenTelemetry support via `@connectum/otel` -- distributed tracing, metrics, and structured logging for your Connectum services. ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { createOtelInterceptor } from '@connectum/otel'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, interceptors: [ createOtelInterceptor({ filter: ({ service }) => !service.includes('grpc.health'), }), ], }); await server.start(); ``` ```bash # .env OTEL_SERVICE_NAME=my-service OTEL_TRACES_EXPORTER=otlp OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 ``` ## Installation ```bash pnpm add @connectum/otel ``` Peer dependencies (installed automatically): `@opentelemetry/api`, `@opentelemetry/sdk-node`. ## Key Concepts | Concept | Description | |---------|-------------| | **Server Interceptor** | `createOtelInterceptor()` -- traces all incoming RPC calls | | **Client Interceptor** | `createOtelClientInterceptor()` -- propagates trace context to outgoing calls | | **Deep Tracing** | `traced()` and `traceAll()` -- instrument business logic functions | | **Metrics** | `getMeter()` -- counters, histograms, gauges via OpenTelemetry | | **Logging** | `getLogger()` -- structured logging with trace correlation | | **Backends** | Configure via environment variables -- Jaeger, Grafana, any OTLP collector | ## Learn More * [Tracing](/en/guide/observability/tracing) -- server/client interceptors, deep tracing, distributed traces * [Metrics](/en/guide/observability/metrics) -- counters, histograms, automatic RPC metrics * [Logging](/en/guide/observability/logging) -- structured logging with trace correlation * [Backends & Configuration](/en/guide/observability/backends) -- environment variables, provider management, Jaeger/Grafana setup * [@connectum/otel](/en/packages/otel) -- Package Guide * [@connectum/otel API](/en/api/@connectum/otel/) -- Full API Reference --- --- url: /en/guide/typescript/patterns.md --- # Patterns & Workflow Common TypeScript patterns used throughout Connectum and the recommended development workflow. ## Named Parameters Prefer objects with named properties over positional parameters: ```typescript // CORRECT: named parameters async function createOrder(options: { userId: string; items: OrderItem[]; priority?: number; }): Promise { // ... } await createOrder({ userId: '123', items: [...] }); // AVOID: positional parameters (hard to read) async function createOrder( userId: string, items: OrderItem[], priority?: number, ): Promise { // ... } ``` ## Const Objects Instead of Enums The standard pattern throughout Connectum: ```typescript // Define the const object export const ServerState = { CREATED: 'created', STARTING: 'starting', RUNNING: 'running', STOPPING: 'stopping', STOPPED: 'stopped', } as const; // Derive the union type export type ServerState = typeof ServerState[keyof typeof ServerState]; // ServerState = 'created' | 'starting' | 'running' | 'stopping' | 'stopped' // Usage function handleState(state: ServerState) { if (state === ServerState.RUNNING) { // ... } } ``` ## Branded Types For type-safe identifiers: ```typescript type UserId = string & { readonly __brand: 'UserId' }; type OrderId = string & { readonly __brand: 'OrderId' }; function getUser(id: UserId): User { ... } function getOrder(id: OrderId): Order { ... } // Compile-time safety: can't pass OrderId where UserId is expected const userId = '123' as UserId; const orderId = '456' as OrderId; getUser(userId); // OK getUser(orderId); // Compile error! ``` ## Strict Null Checks Always handle nullable values explicitly: ```typescript // The server address is null until started const port = server.address?.port; if (port === undefined) { throw new Error('Server not started'); } console.log(`Listening on port ${port}`); ``` ## Type Checking Run type checking as a separate step (not compilation): ```bash # Check types pnpm typecheck # or: tsc --noEmit # Watch mode for development tsc --noEmit --watch ``` ## Development Workflow ```bash # Node.js 25+: start with auto-reload (watches for file changes) node --watch src/index.ts # Bun: start with auto-reload bun --watch src/index.ts # tsx: start with auto-reload (Node.js 18+) tsx --watch src/index.ts # Type check in a separate terminal tsc --noEmit --watch # Or run once pnpm typecheck && pnpm start ``` ## Checklist Before running your Connectum service, verify: * \[ ] Node.js 25+ installed (`node --version`), Bun installed (`bun --version`), or tsx installed (`npx tsx --version`) * \[ ] `"type": "module"` in `package.json` * \[ ] `erasableSyntaxOnly: true` in `tsconfig.json` * \[ ] `verbatimModuleSyntax: true` in `tsconfig.json` * \[ ] No `enum` in application code (use `const` objects) * \[ ] `import type` for all type-only imports * \[ ] `.ts` extensions in relative imports * \[ ] `node:` prefix for built-in modules * \[ ] Proto enums handled via two-step generation (if applicable, Node.js only; not needed for Bun or tsx) ## Related * [TypeScript Overview](/en/guide/typescript) -- back to overview * [Erasable Syntax](/en/guide/typescript/erasable-syntax) -- constraints and tsconfig.json * [Runtime Support](/en/guide/typescript/runtime-support) -- Node.js, Bun, tsx * [Proto Enums](/en/guide/typescript/proto-enums) -- proto enum workaround --- --- url: /en/guide/interceptors/method-filtering.md --- # Per-Method Interceptor Routing By default, interceptors in the `interceptors` array apply to every request. In practice, you often need different interceptors for different services or methods -- authentication for admin endpoints, aggressive timeouts for fast reads, circuit breakers for external-facing APIs. Connectum provides three approaches for per-method interceptor routing. ## Approach 1: ConnectRPC Native Per-Service/Per-Method ConnectRPC natively supports interceptors at the service and method level through `router.service()` and `router.rpc()` options: ```typescript import type { ConnectRouter } from '@connectrpc/connect'; import { GreeterService } from '#gen/greeter_pb.js'; export default (router: ConnectRouter) => { // Per-service -- applies to all methods of GreeterService router.service(GreeterService, greeterImpl, { interceptors: [requireAuth, auditLog], }); // Per-method -- applies only to SayHello router.rpc(GreeterService, GreeterService.methods.sayHello, sayHelloImpl, { interceptors: [rateLimiter], }); }; ``` Use this approach when interceptors are tightly coupled to a specific service in your router definition. ## Approach 2: createMethodFilterInterceptor `createMethodFilterInterceptor` is a declarative helper for routing interceptors to methods based on wildcard patterns. It produces a single `Interceptor` you can add to the global chain: ```typescript import { createMethodFilterInterceptor, createTimeoutInterceptor, createCircuitBreakerInterceptor, } from '@connectum/interceptors'; const perMethodInterceptor = createMethodFilterInterceptor({ // Global wildcard: applies to all methods '*': [logRequest], // Service wildcard: applies to all methods of the service 'admin.v1.AdminService/*': [requireAdmin], // Exact match: applies to a specific method only 'user.v1.UserService/DeleteUser': [requireAdmin, auditLog], }); const server = createServer({ services: [routes], interceptors: [perMethodInterceptor], }); ``` ### Supported Patterns | Pattern | Description | Example | |---------|-------------|---------| | `"*"` | All methods of all services | `"*": [logRequest]` | | `"package.Service/*"` | All methods of a service | `"admin.v1.AdminService/*": [auth]` | | `"package.Service/Method"` | Exact method match | `"user.v1.UserService/GetUser": [cache]` | ::: tip Pattern keys use the protobuf fully-qualified service name (`service.typeName`) plus the method name: `"package.v1.ServiceName/MethodName"`. ::: ### Resolution Order All matching patterns execute sequentially, from most general to most specific: ``` Request: user.v1.UserService/GetUser 1. "*": [logRequest] -- global (always runs) 2. "user.v1.UserService/*": [auth] -- service-level match 3. "user.v1.UserService/GetUser": [cache] -- exact method match Final chain: logRequest -> auth -> cache -> next(req) ``` If no patterns match, the request passes through to the next interceptor in the chain unchanged. ### Invalid Patterns The following patterns throw an error at creation time: ```typescript // Empty service name -- throws Error createMethodFilterInterceptor({ '/*': [auth] }); // No slash separator -- throws Error createMethodFilterInterceptor({ 'SomeService': [auth] }); ``` ### Practical Example: Different Resilience Per Method ```typescript import { createDefaultInterceptors, createMethodFilterInterceptor, createTimeoutInterceptor, createCircuitBreakerInterceptor, } from '@connectum/interceptors'; const resilience = createMethodFilterInterceptor({ // Fast reads: 5 second timeout 'catalog.v1.CatalogService/GetProduct': [ createTimeoutInterceptor({ duration: 5_000 }), ], // Heavy reports: 60 second timeout + circuit breaker 'report.v1.ReportService/*': [ createTimeoutInterceptor({ duration: 60_000 }), createCircuitBreakerInterceptor({ threshold: 3 }), ], // Admin mutations: audit logging 'admin.v1.AdminService/*': [ createAuditLogInterceptor(), ], }); const server = createServer({ services: [routes], interceptors: [ // Default chain with global timeout as fallback ...createDefaultInterceptors({ timeout: { duration: 30_000 } }), resilience, ], }); ``` ## Approach 3: Custom Interceptor with Manual Filtering For dynamic or complex filtering logic that does not fit wildcard patterns, write a custom interceptor: ```typescript import type { Interceptor } from '@connectrpc/connect'; const conditionalAuth: Interceptor = (next) => async (req) => { // Dynamic condition: check service name at runtime if (req.service.typeName === 'admin.v1.AdminService') { await verifyAdminToken(req); } // Check method kind if (req.method.kind === 'server_streaming') { attachStreamMonitoring(req); } return next(req); }; ``` Use this approach for filtering based on request content, runtime state, or conditions that cannot be expressed as static patterns. ## skip\* Options on Built-in Interceptors The built-in resilience interceptors have `skip*` options that serve a different purpose from method filtering. These are **technical constraints**, not routing concerns: | Option | Used by | Why | |--------|---------|-----| | `skipStreaming` | timeout, bulkhead, circuitBreaker, retry, fallback | Resilience patterns wrap the full call. You cannot retry a stream, timeout a long-lived connection, or replace a stream with a fallback value. | | `skipGrpcServices` | serializer | JSON serialization is incompatible with gRPC binary protocol. | | `skipHealthCheck` | logger | Convenience shortcut to reduce log noise from frequent health checks. | These options complement `createMethodFilterInterceptor`. Method filtering handles business routing ("which interceptors for which methods"), while `skip*` handles technical limitations ("this interceptor cannot operate on this call type"). ## Choosing the Right Approach | Scenario | Approach | |----------|----------| | Interceptor tied to a specific service in router code | ConnectRPC native (`router.service()` / `router.rpc()`) | | Declarative routing by pattern across multiple services | `createMethodFilterInterceptor` | | Dynamic logic based on request content or runtime state | Custom interceptor with manual filtering | | Technical limitation (streaming, binary protocol) | `skip*` options on built-in interceptors | ::: warning Do not confuse `skip*` options with method filtering. Setting `skipStreaming: true` on the retry interceptor means "retry cannot handle streams" -- it is not a routing decision. Use `createMethodFilterInterceptor` or router-level interceptors for business routing. ::: ## Related * [Interceptors Overview](/en/guide/interceptors) -- quick start and key concepts * [Built-in Interceptors](/en/guide/interceptors/built-in) -- default chain reference * [Custom Interceptors](/en/guide/interceptors/custom) -- creating custom interceptors * [Custom Protocols](/en/guide/protocols/custom) -- creating protocol plugins * [@connectum/interceptors](/en/packages/interceptors) -- Package Guide * [ADR-014: Method Filter Interceptor](/en/contributing/adr/014-method-filter-interceptor) -- design rationale --- --- url: /en/api/@connectum/auth/proto.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / proto # proto Proto-based authorization configuration. Provides access to protobuf custom options for declarative authorization defined in .proto files, plus utilities for reading and resolving service/method-level authorization settings. ## Type Aliases * [AuthRequirements](type-aliases/AuthRequirements.md) * [MethodAuth](type-aliases/MethodAuth.md) * [ServiceAuth](type-aliases/ServiceAuth.md) ## Variables * [AuthRequirementsSchema](variables/AuthRequirementsSchema.md) * [method\_auth](variables/method_auth.md) * [MethodAuthSchema](variables/MethodAuthSchema.md) * [service\_auth](variables/service_auth.md) * [ServiceAuthSchema](variables/ServiceAuthSchema.md) ## References ### createProtoAuthzInterceptor Re-exports [createProtoAuthzInterceptor](../functions/createProtoAuthzInterceptor.md) *** ### getPublicMethods Re-exports [getPublicMethods](../functions/getPublicMethods.md) *** ### ResolvedMethodAuth Re-exports [ResolvedMethodAuth](../interfaces/ResolvedMethodAuth.md) *** ### resolveMethodAuth Re-exports [resolveMethodAuth](../functions/resolveMethodAuth.md) --- --- url: /en/guide/typescript/proto-enums.md --- # Proto Enums Proto files commonly use `enum`, which generates non-erasable TypeScript. This page describes the workaround and alternatives. ## The Problem `protoc-gen-es` generates TypeScript `enum` declarations for proto enums: ```protobuf // In your .proto file enum OrderStatus { ORDER_STATUS_UNSPECIFIED = 0; ORDER_STATUS_PENDING = 1; ORDER_STATUS_SHIPPED = 2; } ``` This generates: ```typescript // Generated code -- NOT erasable export enum OrderStatus { UNSPECIFIED = 0, PENDING = 1, SHIPPED = 2, } ``` Node.js cannot execute this directly because `enum` generates runtime code. ## The Two-Step Workaround 1. Generate TypeScript to a temporary directory (`gen-ts/`) 2. Compile with `tsc` to produce JavaScript in `gen/` ```json { "scripts": { "build:proto": "protoc -I proto --plugin=protoc-gen-es=./node_modules/.bin/protoc-gen-es --es_out=gen-ts --es_opt=target=ts proto/*.proto", "build:proto:compile": "tsc -p tsconfig.gen.json", "build:proto:all": "pnpm build:proto && pnpm build:proto:compile" } } ``` Create `tsconfig.gen.json` for the compilation step: ```json { "compilerOptions": { "target": "esnext", "module": "nodenext", "moduleResolution": "nodenext", "declaration": true, "outDir": "gen", "rootDir": "gen-ts" }, "include": ["gen-ts/**/*.ts"] } ``` ## Avoiding the Workaround If you control your proto definitions, you can avoid `enum` entirely: ```protobuf // Instead of enum, use int32 constants message Order { int32 status = 1; // Constants defined in documentation: // 0 = UNSPECIFIED // 1 = PENDING // 2 = SHIPPED } ``` Then define constants in TypeScript: ```typescript const OrderStatus = { UNSPECIFIED: 0, PENDING: 1, SHIPPED: 2, } as const; type OrderStatus = typeof OrderStatus[keyof typeof OrderStatus]; ``` ::: info Future improvement This workaround is temporary. When Node.js adds native `enum` support (or `protoc-gen-es` offers an option to generate `as const` objects), the two-step process will no longer be needed. ::: ## Related * [TypeScript Overview](/en/guide/typescript) -- back to overview * [Erasable Syntax](/en/guide/typescript/erasable-syntax) -- why enums are not allowed * [Patterns & Workflow](/en/guide/typescript/patterns) -- const objects pattern --- --- url: /en/guide/auth/proto-authz.md --- # Proto-Based Authorization Define authorization rules directly in `.proto` files using custom options. The `createProtoAuthzInterceptor()` reads these options at runtime via protobuf reflection -- no code changes when access rules evolve. ## Proto Options Import `connectum/auth/v1/options.proto` and annotate services and methods: ```protobuf syntax = "proto3"; package user.v1; import "connectum/auth/v1/options.proto"; service UserService { // Service-level default: deny unless explicitly allowed option (connectum.auth.v1.service_auth) = { default_policy: "deny" }; // Public endpoint -- skip authentication and authorization rpc GetProfile(GetProfileRequest) returns (GetProfileResponse) { option (connectum.auth.v1.method_auth) = { public: true }; } // Requires "admin" role rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse) { option (connectum.auth.v1.method_auth) = { requires: { roles: ["admin"] } }; } // Requires both "users:write" scope rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse) { option (connectum.auth.v1.method_auth) = { requires: { scopes: ["users:write"] } }; } // Inherits service-level default_policy (deny) rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) {} } ``` ### Available Options #### `service_auth` (service-level) | Field | Type | Description | |-------|------|-------------| | `default_policy` | `string` | `"allow"` or `"deny"` when no rule matches | | `default_requires` | `AuthRequirements` | Default roles/scopes for all methods | | `public` | `bool` | Mark all methods as public (skip authn + authz) | #### `method_auth` (method-level) | Field | Type | Description | |-------|------|-------------| | `public` | `bool` | Skip authentication and authorization | | `requires` | `AuthRequirements` | Required roles and/or scopes | | `policy` | `string` | Override service-level default policy | #### `AuthRequirements` | Field | Type | Semantics | |-------|------|-----------| | `roles` | `repeated string` | **any-of** -- user needs at least one | | `scopes` | `repeated string` | **all-of** -- user needs every scope | Method-level options always override service-level defaults. ## Interceptor Setup ```typescript import { createProtoAuthzInterceptor } from '@connectum/auth'; const authz = createProtoAuthzInterceptor(); ``` That's it -- the interceptor reads proto options at runtime. No explicit rules needed. ### With Fallback Rules Combine proto options with programmatic rules for methods without proto annotations: ```typescript import { createProtoAuthzInterceptor } from '@connectum/auth'; const authz = createProtoAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'admin-all', methods: ['admin.v1.AdminService/*'], requires: { roles: ['admin'] }, effect: 'allow' }, ], authorize: (ctx, req) => ctx.roles.includes('superadmin'), }); ``` ## Decision Flow The interceptor resolves authorization in this priority: ``` 1. Proto `public` option → allow (skip authn + authz) 2. Check auth context exists → reject if unauthenticated 3. Proto `requires` option → check roles/scopes 4. Proto `policy` → apply "allow" or "deny" 5. Programmatic rules → evaluate in order 6. `authorize` callback → custom logic 7. `defaultPolicy` → final fallback (default: "deny") ``` Proto options take priority over programmatic rules. This means you can define fine-grained access in `.proto` files and use programmatic rules as a safety net. ## Syncing Public Methods with Authentication Use `getPublicMethods()` to extract public method patterns from proto options and pass them to your authentication interceptor's `skipMethods`: ```typescript import { createJwtAuthInterceptor, createProtoAuthzInterceptor, getPublicMethods } from '@connectum/auth'; import { UserService } from '#gen/user_pb.js'; import { HealthService } from '#gen/health_pb.js'; const publicMethods = getPublicMethods([UserService, HealthService]); // ["user.v1.UserService/GetProfile", "grpc.health.v1.Health/Check"] const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', skipMethods: [ ...publicMethods, 'grpc.reflection.v1.ServerReflection/*', ], }); const authz = createProtoAuthzInterceptor({ defaultPolicy: 'deny' }); ``` This keeps the single source of truth in `.proto` files -- mark a method as `public` once and both authn and authz respect it. ## Resolution Details ### Hierarchical Merge Service-level defaults are merged with method-level overrides: | Setting | Method-level | Service-level | Default | |---------|-------------|--------------|---------| | `public` | `method_auth.public` | `service_auth.public` | `false` | | `requires` | `method_auth.requires` | `service_auth.default_requires` | none | | `policy` | `method_auth.policy` | `service_auth.default_policy` | none | ### Caching Resolved options are cached in a `WeakMap` keyed by method descriptor. After the first call per method, resolution is a single map lookup. ### Error Handling | Scenario | Error | |----------|-------| | Unauthenticated + requires roles/scopes | `Code.Unauthenticated` | | Authenticated but roles/scopes not met | `Code.PermissionDenied` via `AuthzDeniedError` | | Default policy = deny, no match | `Code.PermissionDenied` | `AuthzDeniedError` carries server-side details (rule name, required roles/scopes) while exposing only "Access denied" to clients via the `SanitizableError` protocol. ## Full Example ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { createJwtAuthInterceptor, createProtoAuthzInterceptor, getPublicMethods, } from '@connectum/auth'; import { UserService } from '#gen/user_pb.js'; const publicMethods = getPublicMethods([UserService]); const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', issuer: 'https://auth.example.com/', audience: 'my-api', skipMethods: publicMethods, }); const authz = createProtoAuthzInterceptor({ defaultPolicy: 'deny', authorize: (ctx, req) => ctx.roles.includes('superadmin'), }); const server = createServer({ services: [userServiceRoutes], interceptors: [...createDefaultInterceptors(), jwtAuth, authz], }); await server.start(); ``` ## Related * [Auth Overview](/en/guide/auth) -- all authentication strategies * [Authorization (RBAC)](/en/guide/auth/authorization) -- declarative rules-based authorization * [Auth Context](/en/guide/auth/context) -- accessing identity in handlers * [@connectum/auth](/en/packages/auth) -- Package Guide * [@connectum/auth API](/en/api/@connectum/auth/) -- Full API Reference * [ADR-024: Auth/Authz Strategy](/en/contributing/adr/024-auth-authz-strategy) -- design rationale --- --- url: /en/guide/protocols.md --- # Protocols Protocol plugin system for extending Connectum servers with custom gRPC services and HTTP endpoints. ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { Healthcheck } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, protocols: [ Healthcheck({ httpEnabled: true }), Reflection(), ], }); await server.start(); ``` ## Key Concepts | Concept | Description | |---------|-------------| | **ProtocolRegistration** | Interface with `name`, `register()`, and optional `httpHandler` | | **Healthcheck** | Built-in protocol -- gRPC Health Check + HTTP endpoints (`/healthz`, `/health`, `/readyz`) | | **Reflection** | Built-in protocol -- gRPC Server Reflection for runtime service discovery | | **Custom Protocols** | Implement `ProtocolRegistration` to add gRPC services or HTTP endpoints | | **addProtocol()** | Add protocols dynamically before `server.start()` | Every protocol's `register()` receives a `ConnectRouter` and a `ProtocolContext` with all registered service file descriptors. ## Learn More * [Server Reflection](/en/guide/protocols/reflection) -- runtime service discovery, grpcurl, buf curl, Postman * [Custom Protocols](/en/guide/protocols/custom) -- ProtocolRegistration interface, HTTP handlers, examples * [@connectum/healthcheck](/en/packages/healthcheck) -- Health Check Package Guide * [@connectum/reflection](/en/packages/reflection) -- Reflection Package Guide --- --- url: /en/api/@connectum/otel/provider.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / provider # provider OpenTelemetry Provider Manages OpenTelemetry providers for traces, metrics, and logs. Replaces the previous OTLPProvider singleton with explicit lifecycle control. ## Interfaces * [ProviderOptions](interfaces/ProviderOptions.md) ## Functions * [getProvider](functions/getProvider.md) * [initProvider](functions/initProvider.md) * [shutdownProvider](functions/shutdownProvider.md) --- --- url: /en/guide/quickstart.md description: Build and run a gRPC/ConnectRPC microservice with Connectum in 5 minutes. --- # Quickstart Build a fully functional gRPC/ConnectRPC microservice with health checks, server reflection, and production-ready interceptors. ## Prerequisites * **Node.js >= 25.2.0** -- native TypeScript via [type stripping](https://nodejs.org/api/typescript.html) * **pnpm >= 10** -- `corepack enable && corepack prepare pnpm@latest --activate` * **buf** -- installed automatically via `@bufbuild/buf` npm package ::: tip Node.js version for consumers This guide uses Node.js 25+ for native `.ts` execution of your own source files. However, `@connectum/*` packages ship **compiled JavaScript**, so if you compile your own code (e.g., with tsx or a build tool), you can run on **Node.js >= 18.0.0**. See [Runtime Support](/en/guide/typescript#runtime-support-node-js-vs-bun). ::: ## 1. Project Setup ```bash mkdir greeter-service && cd greeter-service pnpm init ``` Install dependencies: ```bash # Core framework pnpm add @connectum/core @connectum/healthcheck @connectum/reflection @connectum/interceptors # ConnectRPC runtime pnpm add @connectrpc/connect @connectrpc/connect-node @bufbuild/protobuf # Validation (recommended: @connectrpc/validate) pnpm add @bufbuild/protovalidate @connectrpc/validate # Dev dependencies (buf + code generation) pnpm add -D typescript @types/node @bufbuild/buf @bufbuild/protoc-gen-es ``` Configure `package.json`: ```json { "name": "greeter-service", "version": "1.0.0", "type": "module", "imports": { "#gen/*": "./gen/*", "#*": "./src/*" }, "scripts": { "start": "node src/index.ts", "dev": "node --watch src/index.ts", "typecheck": "tsc --noEmit", "build:proto": "buf generate proto" }, "engines": { "node": ">=25.2.0" } } ``` Create `tsconfig.json` (type checking only -- no compilation): ```json { "compilerOptions": { "noEmit": true, "target": "esnext", "module": "nodenext", "moduleResolution": "nodenext", "rewriteRelativeImportExtensions": true, "erasableSyntaxOnly": true, "verbatimModuleSyntax": true, "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*.ts", "gen/**/*.ts"], "exclude": ["node_modules"] } ``` Create project structure: ```bash mkdir -p src/services gen proto ``` ## 2. Proto Definition Create `proto/greeter.proto`: ```protobuf syntax = "proto3"; package greeter.v1; import "buf/validate/validate.proto"; service GreeterService { rpc SayHello(SayHelloRequest) returns (SayHelloResponse) {} } message SayHelloRequest { string name = 1 [(buf.validate.field).string.min_len = 1]; } message SayHelloResponse { string message = 1; } ``` Create `buf.yaml` to declare the validate dependency: ```yaml version: v2 deps: - buf.build/bufbuild/protovalidate ``` Then fetch dependencies: ```bash npx buf dep update ``` ## 3. Code Generation Create `buf.gen.yaml`: ```yaml version: v2 plugins: - local: protoc-gen-es out: gen opt: - target=ts - import_extension=.ts inputs: - directory: proto ``` Run code generation: ```bash pnpm run build:proto ``` This produces `gen/greeter_pb.ts` containing message schemas, types, and the service definition. ::: warning Proto enums and native TypeScript If your proto files use `enum`, the generated code contains non-erasable TypeScript. Use a [two-step generation process](/en/guide/typescript#proto-generation-and-enums). ::: ## 4. Service Handler Create `src/services/greeterService.ts`: ```typescript import { create } from '@bufbuild/protobuf'; import type { ConnectRouter } from '@connectrpc/connect'; import { GreeterService, SayHelloResponseSchema } from '#gen/greeter_pb.js'; import type { SayHelloRequest } from '#gen/greeter_pb.js'; export function greeterServiceRoutes(router: ConnectRouter): void { router.service(GreeterService, { async sayHello(request: SayHelloRequest) { const name = request.name || 'World'; return create(SayHelloResponseSchema, { message: `Hello, ${name}!`, }); }, }); } ``` ## 5. Server Entry Point Create `src/index.ts`: ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { greeterServiceRoutes } from './services/greeterService.ts'; const server = createServer({ services: [greeterServiceRoutes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: createDefaultInterceptors(), shutdown: { autoShutdown: true }, }); server.on('ready', () => { console.log(`Server ready on port ${server.address?.port}`); healthcheckManager.update(ServingStatus.SERVING); }); server.on('stopping', () => { healthcheckManager.update(ServingStatus.NOT_SERVING); }); server.on('error', (err) => console.error(err)); await server.start(); ``` ## 6. Run & Test ```bash # Node.js 25+ (native TypeScript) pnpm dev # Bun bun src/index.ts # tsx (Node.js 18+) npx tsx src/index.ts ``` ### gRPC (grpcurl) ```bash # List services (reflection) grpcurl -plaintext localhost:5000 list # Call SayHello grpcurl -plaintext -d '{"name": "Alice"}' localhost:5000 greeter.v1.GreeterService/SayHello # Health check grpcurl -plaintext localhost:5000 grpc.health.v1.Health/Check ``` ### HTTP/1.1 (curl) ```bash # Call SayHello via ConnectRPC HTTP curl -X POST http://localhost:5000/greeter.v1.GreeterService/SayHello \ -H "Content-Type: application/json" \ -d '{"name": "Bob"}' # Health check curl http://localhost:5000/healthz ``` ## What You Get Out of the Box | Feature | Details | |---------|---------| | **Error handling** | Automatic error normalization to gRPC status codes | | **Timeout** | 30s default per request | | **Bulkhead** | Max 10 concurrent requests + 10-item queue | | **Circuit breaker** | Opens after 5 consecutive failures | | **Retry** | 3 retries with exponential backoff | | **Validation** | Proto constraint validation via [@connectrpc/validate](https://github.com/connectrpc/validate-es) | | **Health checks** | gRPC + HTTP endpoints | | **Reflection** | Runtime service discovery | | **Graceful shutdown** | SIGTERM/SIGINT with connection draining | *** The following steps show how to extend your base service with additional framework features. ## 7. Test Validation The `min_len = 1` rule from Step 2 is enforced automatically by the validation interceptor: ```bash grpcurl -plaintext -d '{"name": ""}' localhost:5000 greeter.v1.GreeterService/SayHello # ERROR: Code: InvalidArgument # Message: validation error: name: value length must be at least 1 characters [string.min_len] ``` No application code required -- proto constraints are validated before your handler runs. See [Validation](/en/guide/validation) for the full constraint catalog. ## 8. Add TLS Generate a self-signed certificate and add `tls` to `createServer`: ```bash mkdir -p keys openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout keys/server.key -out keys/server.crt \ -days 365 -nodes -subj '/CN=localhost' ``` ```typescript const server = createServer({ // ...same options as Step 5, plus: tls: { dirPath: './keys' }, // looks for server.key and server.crt }); ``` ```bash grpcurl -insecure localhost:5000 list # gRPC over TLS curl -k https://localhost:5000/healthz # HTTP over TLS ``` See [Security (TLS)](/en/guide/security) for `keyPath`/`certPath`, mTLS, and production recommendations. ## 9. Add Authentication & Authorization ```bash pnpm add @connectum/auth ``` ```typescript import { createJwtAuthInterceptor, createAuthzInterceptor } from '@connectum/auth'; const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://auth.example.com/.well-known/jwks.json', issuer: 'https://auth.example.com/', audience: 'my-api', skipMethods: ['grpc.health.v1.Health/*', 'grpc.reflection.v1.ServerReflection/*'], }); const authz = createAuthzInterceptor({ defaultPolicy: 'deny', rules: [ { name: 'public', methods: ['greeter.v1.GreeterService/*'], effect: 'allow' }, ], }); // Add before default interceptors interceptors: [jwtAuth, authz, ...createDefaultInterceptors()], ``` The auth interceptor extracts `Authorization: Bearer `, verifies the JWT against JWKS, and populates `AuthContext` -- accessible in handlers via `getAuthContext()`. The authz interceptor evaluates declarative rules against that context. See [Auth & Authorization](/en/guide/auth) for HMAC secrets, gateway auth, session-based auth, and RBAC with roles/scopes. ## 10. Add Observability ```bash pnpm add @connectum/otel ``` ```typescript import { createOtelInterceptor } from '@connectum/otel'; // Add as first interceptor (before auth and defaults) interceptors: [ createOtelInterceptor({ serverPort: 5000 }), ...createDefaultInterceptors(), ], ``` ```bash OTEL_SERVICE_NAME=greeter-service OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 ``` Every RPC now produces traces and metrics following [OTel semantic conventions](https://opentelemetry.io/docs/specs/semconv/rpc/connect-rpc/). See [Observability](/en/guide/observability) for correlated logging, deep tracing, and Grafana dashboards. ## 11. Graceful Shutdown Hooks The `shutdown: { autoShutdown: true }` option from Step 5 handles SIGTERM/SIGINT. Register hooks for resource cleanup: ```typescript server.onShutdown('database', async () => { await db.close(); }); server.onShutdown('cache', ['database'], async () => { await redis.quit(); }); ``` Hooks execute in dependency order -- `cache` waits for `database` to complete. Use `server.shutdownSignal` to cancel long-running operations. Override defaults: ```typescript shutdown: { autoShutdown: true, timeout: 15_000, signals: ['SIGTERM', 'SIGINT'] }, ``` See [Graceful Shutdown](/en/guide/server/graceful-shutdown) for dependency graphs and Kubernetes integration. ## 12. Built-in Interceptors `createDefaultInterceptors()` assembles 8 interceptors in a fixed order: | # | Interceptor | Default | Purpose | |---|-------------|---------|---------| | 1 | **errorHandler** | on | Normalize errors to gRPC status codes | | 2 | **timeout** | 30s | Enforce per-request deadline | | 3 | **bulkhead** | 10/10 | Limit concurrent requests + queue | | 4 | **circuitBreaker** | 5 failures | Prevent cascading failures | | 5 | **retry** | 3 retries | Exponential backoff for transients | | 6 | **fallback** | off | Graceful degradation (requires handler) | | 7 | **validation** | on | Proto constraint validation | | 8 | **serializer** | on | JSON serialization | ```typescript const interceptors = createDefaultInterceptors({ retry: false, timeout: { duration: 10_000 }, bulkhead: { maxConcurrent: 20, maxQueue: 50 }, }); ``` See [Interceptors](/en/guide/interceptors) for the full options reference and custom interceptors. ## 13. Sync Contracts with CLI When Reflection is enabled, clients can sync proto types without `.proto` files: ```bash pnpm add -D @connectum/cli npx connectum proto sync --from localhost:5000 --out ./gen --dry-run # discover npx connectum proto sync --from localhost:5000 --out ./gen # generate ``` ## 14. Call Another Service Microservices communicate via gRPC clients. Create a transport with `createClient` and add observability with `createOtelClientInterceptor`: ```typescript import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { createOtelClientInterceptor } from '@connectum/otel'; import { InventoryService } from '#gen/inventory_pb.js'; const inventoryTransport = createGrpcTransport({ baseUrl: `http://${process.env.INVENTORY_HOST}:${process.env.INVENTORY_PORT}`, httpVersion: '2', interceptors: [ createOtelClientInterceptor({ serverAddress: process.env.INVENTORY_HOST!, serverPort: Number(process.env.INVENTORY_PORT), }), ], }); const inventoryClient = createClient(InventoryService, inventoryTransport); // Use in any service handler const stock = await inventoryClient.checkStock({ sku: 'ABC-123' }); ``` Trace context propagates automatically -- the client span links to the server span in the downstream service. See [Service Communication](/en/guide/service-communication) for patterns, resilience, and service discovery. ## Next Steps You've built a microservice with validation, TLS, auth, observability, and resilience. Dive deeper: * [About Connectum](/en/guide/about) -- framework design decisions and philosophy * [Server](/en/guide/server) -- lifecycle, configuration, graceful shutdown * [Interceptors](/en/guide/interceptors) -- built-in chain, custom interceptors, method filtering * [Service Communication](/en/guide/service-communication) -- inter-service calls, patterns, client interceptors * [Auth & Authorization](/en/guide/auth) -- JWT, gateway, session, RBAC, proto-based authz * [Observability](/en/guide/observability) -- tracing, metrics, correlated logging * [Health Checks](/en/guide/health-checks) -- gRPC/HTTP protocol, Kubernetes probes * [Security (TLS)](/en/guide/security) -- TLS, mTLS, certificate management * [Protocols](/en/guide/protocols) -- server reflection, custom protocol plugins * [Validation](/en/guide/validation) -- proto constraint catalog * [TypeScript](/en/guide/typescript) -- native runtime support, erasable syntax patterns * [Testing](/en/guide/testing) -- integration testing strategies * [Production](/en/guide/production/architecture) -- Docker, Kubernetes, Envoy Gateway --- --- url: /en/api/@connectum/interceptors/retry.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / retry # retry Retry interceptor Automatically retries failed unary RPC calls with exponential backoff. Uses cockatiel for consistent resilience pattern implementation. ## Functions * [createRetryInterceptor](functions/createRetryInterceptor.md) --- --- url: /en/guide/testing/runn.md --- # runn [runn](https://github.com/k1LoW/runn) is a scenario-based testing tool that supports gRPC, HTTP/REST, databases, and more. It uses YAML runbooks to define multi-step test scenarios with assertions. **Why runn for Connectum:** * gRPC testing with **server reflection** (no proto files needed) * HTTP testing for ConnectRPC endpoints * Built-in assertion engine ([expr-lang](https://expr-lang.org/docs/language-definition)) * Single binary, CI-friendly * Docker image available ::: info Working Example See [examples/runn](https://github.com/Connectum-Framework/examples/tree/main/runn) for a complete Docker-based E2E test suite with 9 runbooks covering healthcheck, reflection, auth, interceptors, timeout, and multi-service scenarios. ::: ## Installation ::: code-group ```bash [Homebrew] brew install k1LoW/tap/runn ``` ```bash [Go] go install github.com/k1LoW/runn/cmd/runn@latest ``` ```bash [Aqua] aqua g -i k1LoW/runn ``` ```bash [Docker] docker pull ghcr.io/k1low/runn:latest ``` ::: ## Testing gRPC Services Connectum services expose gRPC endpoints. Server reflection can be enabled via `Reflection()` protocol. runn can discover services automatically without proto files. Create `tests/grpc-greeter.yml`: ```yaml desc: Greeter service — gRPC runners: greq: grpc://localhost:5000 steps: say_hello: desc: SayHello returns greeting greq: greeter.v1.GreeterService/SayHello: message: name: Alice test: | current.res.status == 0 && current.res.message.message == 'Hello, Alice!' ``` Run it: ```bash runn run tests/grpc-greeter.yml ``` ::: tip Server Reflection When Server Reflection is enabled via `protocols: [Reflection()]`, runn discovers services automatically. No `protos:` or `importPaths:` configuration needed. ::: ### With Proto Files If reflection is disabled, point runn to your proto definitions: ```yaml runners: greq: addr: localhost:5000 importPaths: - proto protos: - greeter.proto ``` ### gRPC Metadata Pass metadata (headers) with requests: ```yaml steps: with_metadata: greq: greeter.v1.GreeterService/SayHello: headers: authorization: "Bearer my-token" x-request-id: "test-123" message: name: Bob test: current.res.status == 0 ``` ## Testing ConnectRPC HTTP Endpoints Connectum services also accept HTTP/1.1 requests via the ConnectRPC protocol. Create `tests/http-greeter.yml`: ```yaml desc: Greeter service — ConnectRPC HTTP runners: req: http://localhost:5000 steps: say_hello: desc: SayHello via HTTP POST req: /greeter.v1.GreeterService/SayHello: post: header: Content-Type: application/json body: application/json: name: Bob test: | current.res.status == 200 && current.res.body.message == 'Hello, Bob!' ``` ## Testing Health Checks Connectum exposes both gRPC and HTTP health check endpoints. Create `tests/health.yml`: ```yaml desc: Health check endpoints runners: greq: grpc://localhost:5000 req: http://localhost:5000 steps: grpc_health: desc: gRPC Health Check greq: grpc.health.v1.Health/Check: message: {} test: | current.res.status == 0 && current.res.message.status == 1 http_health: desc: HTTP /healthz req: /healthz: get: header: Accept: application/json test: current.res.status == 200 ``` ## Validation Testing Test that [protovalidate](/en/guide/interceptors) constraints reject invalid input: ```yaml desc: Validation rejects empty name runners: greq: grpc://localhost:5000 steps: empty_name: desc: SayHello with empty name returns INVALID_ARGUMENT greq: greeter.v1.GreeterService/SayHello: message: name: "" test: current.res.status == 3 ``` gRPC status code `3` = `INVALID_ARGUMENT`. ## Multi-Step Scenarios Chain steps together using variable references: ```yaml desc: Multi-step gRPC scenario runners: greq: grpc://localhost:5000 vars: username: Charlie steps: greet: desc: Greet user greq: greeter.v1.GreeterService/SayHello: message: name: "{{ vars.username }}" test: current.res.status == 0 verify_message: desc: Verify greeting format test: | steps.greet.res.message.message == 'Hello, Charlie!' ``` ## Variables and Environment ```yaml desc: Environment-driven tests vars: host: ${GRPC_HOST:-localhost:5000} token: ${AUTH_TOKEN} runners: greq: "grpc://{{ vars.host }}" steps: authenticated_call: greq: myapp.v1.MyService/GetData: headers: authorization: "Bearer {{ vars.token }}" message: {} test: current.res.status == 0 ``` ## Streaming RPCs Test server-streaming responses: ```yaml steps: server_stream: greq: myapp.v1.MyService/ListItems: message: limit: 10 test: | current.res.status == 0 && len(current.res.messages) > 0 ``` For client-streaming, send multiple messages: ```yaml steps: client_stream: greq: myapp.v1.MyService/SendBatch: messages: - data: "item-1" - data: "item-2" - data: "item-3" test: current.res.status == 0 ``` ## TLS / mTLS ```yaml runners: greq: addr: grpc.example.com:443 tls: true cacert: certs/ca.pem cert: certs/client.pem key: certs/client-key.pem ``` ## CI/CD Integration ### GitHub Actions ```yaml jobs: api-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install runn run: | brew install k1LoW/tap/runn - name: Start service run: node src/index.ts & - name: Wait for service run: | for i in $(seq 1 30); do curl -sf http://localhost:5000/healthz && break sleep 1 done - name: Run API tests run: runn run tests/**/*.yml ``` ### Docker Compose ```yaml services: app: build: . ports: - "5000:5000" api-tests: image: ghcr.io/k1low/runn:latest depends_on: app: condition: service_healthy volumes: - ./tests:/books command: run /books/**/*.yml ``` ## CLI Reference ```bash # Run all test scenarios runn run tests/**/*.yml # Run with verbose output runn run --verbose tests/grpc-greeter.yml # List available scenarios runn list tests/**/*.yml # Generate a runbook from a command runn new -- grpcurl -plaintext localhost:5000 greeter.v1.GreeterService/SayHello ``` ## Recommended Test Structure ``` tests/ ├── api/ │ ├── grpc-greeter.yml # gRPC endpoint tests │ ├── http-greeter.yml # ConnectRPC HTTP tests │ ├── health.yml # Health check tests │ └── validation.yml # Validation constraint tests └── scenarios/ ├── user-flow.yml # Multi-step user scenarios └── error-handling.yml # Error response tests ``` ## Related * [Testing Overview](/en/guide/testing) -- back to overview * [scenarigo](/en/guide/testing/scenarigo) -- alternative testing tool * [Server Reflection](/en/guide/protocols/reflection) -- enable reflection for runn * [Health Checks](/en/guide/health-checks) -- health check endpoints to test --- --- url: /en/guide/typescript/runtime-support.md --- # Runtime Support Connectum packages ship **compiled JavaScript** with TypeScript declarations (`.d.ts`) and source maps. This means **no special loader or register hook is needed** for any runtime -- all runtimes can import `@connectum/*` packages directly. ## Node.js 25+ Node.js 25+ supports [type stripping](https://nodejs.org/api/typescript.html) for your own `.ts` source files. Since `@connectum/*` packages ship compiled `.js`, no loader is required: ```bash node src/index.ts ``` In `package.json`: ```json { "scripts": { "start": "node src/index.ts", "dev": "node --watch src/index.ts" } } ``` ### What Packages Ship Each `@connectum/*` package is built with [tsup](https://tsup.egoist.dev/) and publishes: * **Compiled `.js` files** (ESM) -- ready to run on any ES module-capable runtime * **TypeScript declarations** (`.d.ts`) -- full type information for IDE support and type checking * **Source maps** (`.js.map`) -- accurate stack traces pointing to the original TypeScript source ## Bun Bun natively supports TypeScript for your own source files. Since `@connectum/*` packages ship compiled `.js`, everything works out of the box: ```bash bun src/index.ts ``` In `package.json`: ```json { "scripts": { "start": "bun src/index.ts", "dev": "bun --watch src/index.ts" } } ``` ## tsx (Node.js 18+) [tsx](https://tsx.is) is a TypeScript execution engine powered by [esbuild](https://esbuild.github.io/). It works as a drop-in replacement for `node` and runs on **Node.js 18+**, making it a good option when you cannot use Node.js 25+. Since `@connectum/*` packages ship compiled `.js`, no special configuration is needed. ```bash npx tsx src/index.ts ``` In `package.json`: ```json { "scripts": { "start": "tsx src/index.ts", "dev": "tsx --watch src/index.ts" } } ``` ::: tip Install tsx as a devDependency (`pnpm add -D tsx`) for faster invocation without `npx`. ::: ## Comparison | Feature | Node.js 25+ | Bun | tsx (Node.js 18+) | |---------|------------|-----|-------------------| | Your `.ts` files | Native type stripping | Native | esbuild | | `@connectum/*` packages | Compiled `.js` (no loader needed) | Compiled `.js` (no loader needed) | Compiled `.js` (no loader needed) | | `--watch` mode | `node --watch` | `bun --watch` | `tsx --watch` | | Proto enum support | Requires [two-step generation](/en/guide/typescript/proto-enums) | Native | Native (esbuild) | | Min Node.js version | 25.2.0 (for native `.ts` execution) | N/A (Bun runtime) | 18.0.0 | ## Docker In Dockerfiles, use the appropriate `CMD` for your runtime: ```dockerfile # Node.js 25+ (native TypeScript for your own .ts files) CMD ["node", "src/index.ts"] # Bun CMD ["bun", "src/index.ts"] # tsx (Node.js 18+) CMD ["npx", "tsx", "src/index.ts"] ``` ## Related * [TypeScript Overview](/en/guide/typescript) -- back to overview * [Erasable Syntax](/en/guide/typescript/erasable-syntax) -- constraints for native TypeScript execution * [Proto Enums](/en/guide/typescript/proto-enums) -- workaround for proto enum generation * [@connectum/core](/en/packages/core) -- Package Guide --- --- url: /en/guide/testing/scenarigo.md --- # scenarigo [scenarigo](https://github.com/scenarigo/scenarigo) is a scenario-based API testing tool with gRPC and HTTP support. It offers a Go plugin system and JUnit XML report generation. ## Installation ```bash go install github.com/scenarigo/scenarigo/cmd/scenarigo@latest ``` ## Example: gRPC Test ```yaml title: Greeter gRPC test steps: - title: SayHello protocol: grpc request: method: greeter.v1.GreeterService/SayHello body: name: Alice expect: code: OK body: message: "Hello, Alice!" ``` ## Example: HTTP Test ```yaml title: Greeter HTTP test steps: - title: SayHello via ConnectRPC protocol: http request: method: POST url: "http://localhost:5000/greeter.v1.GreeterService/SayHello" header: Content-Type: application/json body: name: Bob expect: code: OK body: message: "Hello, Bob!" ``` ## Key Differences: runn vs scenarigo | Feature | runn | scenarigo | |---------|------|-----------| | **Installation** | Single binary (brew, aqua, Docker) | Go install or binary | | **gRPC reflection** | Built-in | Requires proto files | | **Assertions** | expr-lang expressions | Template-based + assert functions | | **Streaming** | Client + server streaming | Limited | | **Plugin system** | No | Go plugins | | **Reports** | Text output | JUnit XML, JSON | | **Docker** | Official image | No | | **Database testing** | Built-in | Via plugins | ## Related * [Testing Overview](/en/guide/testing) -- back to overview * [runn](/en/guide/testing/runn) -- recommended testing tool with gRPC reflection support --- --- url: /en/guide/security.md --- # Security Built-in TLS and mTLS for secure gRPC/ConnectRPC communication over HTTP/2. ## Quick Start ```typescript import { createServer } from '@connectum/core'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, tls: { dirPath: './keys', // Looks for server.key + server.crt }, }); await server.start(); ``` When TLS is configured, Connectum creates an HTTP/2 secure server (`http2.createSecureServer`). Without TLS, it creates a plaintext HTTP/2 server. ## Key Concepts | Concept | Description | |---------|-------------| | **TLS Options** | `keyPath` + `certPath` for explicit paths, or `dirPath` for directory-based config | | **Environment Variables** | `TLS_DIR_PATH`, `TLS_KEY_PATH`, `TLS_CERT_PATH` for deployment flexibility | | **mTLS** | Mutual TLS via `http2Options`: `requestCert`, `rejectUnauthorized`, `ca` | | **HTTP/2** | Default transport; `allowHTTP1: true` enables HTTP/1.1 fallback for ConnectRPC | | **Utility Functions** | `readTLSCertificates()`, `getTLSPath()` from `@connectum/core` | ## Learn More * [TLS Configuration](/en/guide/security/tls) -- TLS options, utility functions, self-signed certs, testing * [Mutual TLS (mTLS)](/en/guide/security/mtls) -- mTLS setup, production best practices, Kubernetes secrets * [@connectum/core](/en/packages/core) -- Package Guide * [@connectum/core API](/en/api/@connectum/core/) -- Full API Reference --- --- url: /en/api/@connectum/interceptors/serializer.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / serializer # serializer Serializer interceptor Auto-converts messages to/from JSON for non-gRPC services. ## Functions * [createSerializerInterceptor](functions/createSerializerInterceptor.md) --- --- url: /en/guide/server.md --- # Server `createServer()` is the single entry point for every Connectum service -- it wires up transports, interceptors, protocols, and graceful shutdown in one call. ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: createDefaultInterceptors(), shutdown: { autoShutdown: true, timeout: 30_000 }, }); server.on('ready', () => healthcheckManager.update(ServingStatus.SERVING)); server.on('stop', () => console.log('Server stopped')); await server.start(); ``` ## Key Concepts ### Server States Every server follows a deterministic state machine: | State | Description | |-------|-------------| | `created` | Server object constructed, not yet listening | | `starting` | Binding port, initializing protocols | | `running` | Accepting requests | | `stopping` | Draining connections, executing shutdown hooks | | `stopped` | Fully shut down, all resources released | ### Lifecycle Events | Event | Fires when | |-------|------------| | `start` | Server begins the start sequence | | `ready` | Server is bound and accepting requests | | `stopping` | Shutdown initiated (signal or manual `stop()`) | | `stop` | Server is fully stopped | | `error` | An error occurs during start or shutdown | ### shutdownSignal The server exposes an `AbortSignal` via `server.shutdownSignal`. It is aborted when shutdown begins -- pass it to streaming handlers and background workers so they can cancel gracefully. ```typescript server.on('ready', () => { startBackgroundWorker(server.shutdownSignal); }); ``` ## Learn More * [Lifecycle](/en/guide/server/lifecycle) -- states, events, and the shutdownSignal in detail * [Configuration](/en/guide/server/configuration) -- environment variables, TLS, and schema extension * [Graceful Shutdown](/en/guide/server/graceful-shutdown) -- hooks, timeouts, and Kubernetes integration * [@connectum/core](/en/packages/core) -- Package Guide * [@connectum/core API](/en/api/@connectum/core/) -- Full API Reference --- --- url: /en/guide/server/lifecycle.md --- # Server Lifecycle Connectum servers follow a deterministic state machine. Understanding the lifecycle helps you hook into the right moment for health checks, telemetry, background workers, and resource cleanup. ## Server States ``` created ──> starting ──> running ──> stopping ──> stopped ``` | State | What happens | |-------|--------------| | **created** | `createServer()` returns. No port is bound yet. | | **starting** | `server.start()` called -- the server binds the port and initializes protocols. | | **running** | The server is accepting requests. | | **stopping** | `server.stop()` called (or a signal received with `autoShutdown`). Connections are being drained. | | **stopped** | All connections closed, shutdown hooks executed, resources released. | The transitions are one-directional. A stopped server cannot be restarted -- create a new one instead. ## Lifecycle Events Register listeners with `server.on(event, handler)`: ```typescript server.on('start', () => { console.log('Server is starting...'); }); server.on('ready', () => { console.log(`Listening on port ${server.address?.port}`); healthcheckManager.update(ServingStatus.SERVING); }); server.on('stopping', () => { console.log('Shutdown initiated'); healthcheckManager.update(ServingStatus.NOT_SERVING); }); server.on('stop', () => { console.log('Server stopped cleanly'); }); server.on('error', (err) => { console.error('Server error:', err); }); ``` ### Event Reference | Event | Payload | Typical use | |-------|---------|-------------| | `start` | -- | Log startup, initialize external connections | | `ready` | -- | Update health status to `SERVING`, start background workers | | `stopping` | -- | Update health status to `NOT_SERVING`, stop accepting new work | | `stop` | -- | Final log, exit the process if needed | | `error` | `Error` | Log the error, alert monitoring | ### Event Ordering Events always fire in a fixed order: ``` start → ready → ... (server is running) ... → stopping → stop ``` `error` may fire at any point. If an error occurs during startup, the sequence is `start → error`. If it occurs during shutdown, it is `stopping → error → stop`. ## The shutdownSignal Every server exposes an `AbortSignal` via `server.shutdownSignal`. The signal is **aborted** when the server enters the `stopping` state. Use it to propagate cancellation to streaming RPCs, background workers, and long-running operations. ```typescript server.on('ready', () => { startBackgroundWorker(server.shutdownSignal); }); function startBackgroundWorker(signal: AbortSignal) { const interval = setInterval(() => { if (signal.aborted) { clearInterval(interval); return; } // Periodic work }, 5_000); } ``` ### With Fetch or Timers Node.js built-in APIs accept `AbortSignal` natively: ```typescript import { setTimeout } from 'node:timers/promises'; // Cancels if the server shuts down before the delay completes await setTimeout(10_000, undefined, { signal: server.shutdownSignal }); ``` ```typescript // Abort an outgoing HTTP request when the server shuts down const res = await fetch('https://api.example.com/data', { signal: server.shutdownSignal, }); ``` ## Integrating with Health Checks The `@connectum/healthcheck` package exposes a `healthcheckManager` singleton. Update it in lifecycle events so that load balancers and Kubernetes know when the service is ready: ```typescript import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true })], shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); server.on('stopping', () => { healthcheckManager.update(ServingStatus.NOT_SERVING); }); await server.start(); ``` The `stopping` handler ensures that readiness probes fail immediately, giving the load balancer time to remove the pod before connections are drained. ## Complete Example ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: createDefaultInterceptors(), shutdown: { autoShutdown: true, timeout: 25_000 }, }); server.on('start', () => console.log('Starting...')); server.on('ready', () => { console.log(`Ready on port ${server.address?.port}`); healthcheckManager.update(ServingStatus.SERVING); startBackgroundWorker(server.shutdownSignal); }); server.on('stopping', () => { healthcheckManager.update(ServingStatus.NOT_SERVING); }); server.on('stop', () => console.log('Stopped')); server.on('error', (err) => console.error('Error:', err)); await server.start(); ``` ## Related * [Server Overview](/en/guide/server) -- quick start and key concepts * [Configuration](/en/guide/server/configuration) -- environment variables and TLS * [Graceful Shutdown](/en/guide/server/graceful-shutdown) -- hooks, timeouts, and Kubernetes * [@connectum/core](/en/packages/core) -- Package Guide * [@connectum/core API](/en/api/@connectum/core/) -- Full API Reference --- --- url: /en/guide/protocols/reflection.md --- # Server Reflection Server Reflection allows clients to discover services, methods, and message types at runtime without access to `.proto` files. Connectum implements the [gRPC Server Reflection Protocol](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) (v1 and v1alpha) through the `@connectum/reflection` package. ## Why Use Reflection? * **grpcurl**: List and call services without providing `.proto` files * **Postman / Insomnia / [Warthog](https://github.com/Forest33/warthog)**: Auto-discover gRPC services for manual testing * **buf curl**: Test ConnectRPC services with automatic schema resolution * **Service registries**: Dynamic service discovery in microservice architectures * **Development**: Explore APIs interactively during development ::: warning Production consideration Server Reflection exposes your service schema to any client that can connect. In production environments, consider disabling it or restricting access. ::: ## Installation ```bash pnpm add @connectum/reflection ``` Peer dependency: `@connectum/core`. ## Quick Setup ```typescript import { createServer } from '@connectum/core'; import { Reflection } from '@connectum/reflection'; import routes from '#gen/routes.js'; const server = createServer({ services: [routes], port: 5000, protocols: [Reflection()], }); await server.start(); ``` That is all you need. The `Reflection()` factory creates a `ProtocolRegistration` that registers the `grpc.reflection.v1.ServerReflection` service on your server. ## How It Works When you pass `Reflection()` to the `protocols` array, Connectum: 1. Collects all registered service file descriptors from your services 2. Builds a `FileDescriptorSet` from those descriptors and their dependencies 3. Registers the `grpc.reflection.v1.ServerReflection` service on the ConnectRouter 4. Clients can then query the reflection service to discover available services The reflection service is registered **after** your application services, so it has access to all registered service descriptors. ## Using grpcurl with Reflection [grpcurl](https://github.com/fullstorydev/grpcurl) is the most common tool for interacting with reflection-enabled gRPC services. ### List All Services ```bash grpcurl -plaintext localhost:5000 list ``` Output: ``` greeter.v1.GreeterService grpc.health.v1.Health grpc.reflection.v1.ServerReflection ``` ### Describe a Service ```bash grpcurl -plaintext localhost:5000 describe greeter.v1.GreeterService ``` Output: ``` greeter.v1.GreeterService is a service: service GreeterService { rpc SayHello ( .greeter.v1.SayHelloRequest ) returns ( .greeter.v1.SayHelloResponse ); } ``` ### Describe a Message Type ```bash grpcurl -plaintext localhost:5000 describe greeter.v1.SayHelloRequest ``` Output: ``` greeter.v1.SayHelloRequest is a message: message SayHelloRequest { string name = 1; } ``` ### Call a Method With reflection enabled, grpcurl does not need `-proto` or `-protoset` flags: ```bash grpcurl -plaintext \ -d '{"name": "Alice"}' \ localhost:5000 \ greeter.v1.GreeterService/SayHello ``` ### With TLS ```bash # Self-signed (development) grpcurl -insecure localhost:5000 list # With CA certificate grpcurl -cacert keys/server.crt localhost:5000 list ``` ## Using buf curl with Reflection [buf curl](https://buf.build/docs/reference/cli/buf/curl/) supports ConnectRPC protocol and can use reflection: ```bash # List services buf curl --protocol connect --http2-prior-knowledge \ http://localhost:5000 --list-services # Call a method buf curl --protocol connect --http2-prior-knowledge \ -d '{"name": "Alice"}' \ http://localhost:5000/greeter.v1.GreeterService/SayHello ``` ## Using Postman / Insomnia / Warthog Postman, Insomnia, and [Warthog](https://github.com/Forest33/warthog) support gRPC Server Reflection: 1. Create a new gRPC request 2. Enter the server URL: `localhost:5000` 3. Click "Use Server Reflection" (or similar) 4. The tool discovers and lists all available services and methods 5. Select a method, fill in the request payload, and send ## Conditional Reflection Enable reflection only in non-production environments: ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; const protocols = [Healthcheck({ httpEnabled: true })]; // Only enable reflection in development if (process.env.NODE_ENV !== 'production') { protocols.push(Reflection()); } const server = createServer({ services: [routes], port: 5000, protocols, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` ## Adding Reflection at Runtime You can add reflection before starting the server using `addProtocol()`: ```typescript const server = createServer({ services: [routes], port: 5000, }); // Conditionally add reflection if (config.enableReflection) { server.addProtocol(Reflection()); } await server.start(); ``` ::: warning `addProtocol()` can only be called before `server.start()`. Attempting to add protocols after the server has started throws an error. ::: ## Complete Example ```typescript import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { Reflection } from '@connectum/reflection'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { greeterServiceRoutes } from './services/greeterService.ts'; import { orderServiceRoutes } from './services/orderService.ts'; const server = createServer({ services: [greeterServiceRoutes, orderServiceRoutes], port: 5000, protocols: [ Healthcheck({ httpEnabled: true }), Reflection(), ], interceptors: createDefaultInterceptors(), shutdown: { autoShutdown: true }, }); server.on('ready', () => { const port = server.address?.port; console.log(`Server ready on port ${port}`); console.log(` grpcurl -plaintext localhost:${port} list`); healthcheckManager.update(ServingStatus.SERVING); }); await server.start(); ``` After starting, verify everything is discoverable: ```bash # List all services grpcurl -plaintext localhost:5000 list # greeter.v1.GreeterService # order.v1.OrderService # grpc.health.v1.Health # grpc.reflection.v1.ServerReflection # Describe the order service grpcurl -plaintext localhost:5000 describe order.v1.OrderService ``` ## collectFileProtos Utility The `collectFileProtos` utility function is also exported for advanced use cases. It collects file descriptor protos with their dependency tree: ```typescript import { collectFileProtos } from '@connectum/reflection'; ``` This is used internally by the `Reflection()` factory to build the `FileDescriptorSet`. ## Protocol Registration Details Under the hood, `Reflection()` returns a `ProtocolRegistration` object: ```typescript { name: 'reflection', register(router, context) { // context.registry contains all registered service DescFile[] // Builds FileDescriptorSet and registers reflection service }, } ``` The `context.registry` is populated by `@connectum/core` during server startup, containing file descriptors from all registered services. ## Related * [Protocols Overview](/en/guide/protocols) -- back to overview * [Custom Protocols](/en/guide/protocols/custom) -- create your own protocol plugins * [Health Checks](/en/guide/health-checks) -- health monitoring * [@connectum/reflection](/en/packages/reflection) -- Package Guide * [@connectum/reflection API](/en/api/@connectum/reflection/) -- Full API Reference --- --- url: /en/guide/service-communication.md --- # Service Communication Use `createClient()` + `createGrpcTransport()` from ConnectRPC to call other services. Add `createOtelClientInterceptor()` for distributed tracing and `createDefaultInterceptors()` with client-safe options for resilience. ## Quick Start ```typescript import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { createOtelClientInterceptor } from '@connectum/otel'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { InventoryService } from '#gen/inventory/v1/inventory_pb.js'; // 1. Create transport with OTel + resilience interceptors const inventoryTransport = createGrpcTransport({ baseUrl: `http://${process.env.INVENTORY_HOST}:${process.env.INVENTORY_PORT}`, httpVersion: '2', interceptors: [ createOtelClientInterceptor({ serverAddress: process.env.INVENTORY_HOST!, serverPort: Number(process.env.INVENTORY_PORT), }), ...createDefaultInterceptors({ circuitBreaker: { failureThreshold: 5 }, timeout: { duration: 5_000 }, retry: { maxRetries: 2 }, // Disable server-only interceptors bulkhead: false, errorHandler: false, serializer: false, validation: false, }), ], }); // 2. Create typed client const inventoryClient = createClient(InventoryService, inventoryTransport); // 3. Call from any handler const stock = await inventoryClient.checkStock({ sku: 'ABC-123' }); ``` Trace context propagates automatically -- the client span links to the server span in the downstream service. ## Key Concepts ### Transport Configuration `createGrpcTransport()` creates an HTTP/2 transport for gRPC communication: | Option | Type | Description | |--------|------|-------------| | `baseUrl` | `string` | Target service URL (e.g. `http://order-service:5000`) | | `httpVersion` | `'2'` | HTTP version (always `'2'` for gRPC) | | `interceptors` | `Interceptor[]` | Client-side interceptor chain | For ConnectRPC protocol (HTTP/1.1 JSON), use `createConnectTransport()` instead. ### Client vs Server Interceptors Not all server interceptors are appropriate for client transports. When using `createDefaultInterceptors()` on the client side, disable server-only interceptors: | Interceptor | Server | Client | Notes | |-------------|:------:|:------:|-------| | **errorHandler** | Yes | No | Normalizes errors for responses -- not needed on client | | **timeout** | Yes | Yes | Enforce per-request deadline | | **bulkhead** | Yes | No | Limits server concurrency -- not applicable to clients | | **circuitBreaker** | Yes | Yes | Prevents cascading failures to downstream services | | **retry** | Yes | Yes | Retries transient errors | | **fallback** | Yes | No | Requires server-side handler | | **validation** | Yes | No | Validates incoming requests -- not outgoing | | **serializer** | Yes | No | Server-side JSON serialization | ### Service Discovery | Pattern | When to Use | Example | |---------|-------------|---------| | **Kubernetes DNS** | Most K8s deployments | `http://order-service.production.svc.cluster.local:5000` | | **Environment variables** | Simple setups, Docker Compose | `http://${process.env.ORDER_HOST}:${process.env.ORDER_PORT}` | | **Buf Schema Registry** | Polyrepo proto management | Centralized proto definitions via [BSR](https://buf.build/product/bsr) | | **gRPC Server Reflection** | Development, tooling | Runtime API discovery via `grpcurl`, `grpcui` | In Kubernetes, services are discoverable via DNS. A Connectum service deployed as `order-service` in namespace `production` is reachable at `order-service.production.svc.cluster.local:5000`. No external service registry is needed. ## Learn More * [Communication Patterns](./service-communication/patterns) -- request-response, fan-out, streaming * [Client Interceptors](./service-communication/client-interceptors) -- OTel, resilience, custom * [Distributed Tracing](/en/guide/observability/tracing) -- trace context propagation * [Architecture Patterns](/en/guide/production/architecture) -- full production architecture reference * [@connectum/otel](/en/packages/otel) -- Package Guide * [@connectum/otel API](/en/api/@connectum/otel/) -- Full API Reference --- --- url: /en/guide/auth/session.md --- # Session Authentication `createSessionAuthInterceptor` verifies session tokens using a pluggable `verifySession` callback. It is designed for frameworks like [better-auth](https://www.better-auth.com/), [lucia](https://lucia-auth.com/), or custom session stores. ## Configuration ```typescript import { createSessionAuthInterceptor } from '@connectum/auth'; const sessionAuth = createSessionAuthInterceptor({ verifySession: (token, headers) => auth.api.getSession({ headers }), mapSession: (session) => ({ subject: session.user.id, name: session.user.name, roles: [], scopes: [], claims: session.user, type: 'session', }), cache: { ttl: 60_000 }, }); ``` ### Options | Option | Type | Required | Description | |--------|------|----------|-------------| | `verifySession` | `(token, headers) => Promise` | Yes | Validates the session token and returns session data | | `mapSession` | `(session) => AuthContext` | Yes | Maps the session object to a standard `AuthContext` | | `cache` | `{ ttl: number }` | No | Cache verified sessions to reduce backend calls | ## How It Works Unlike `createJwtAuthInterceptor`, the session interceptor receives the **full request `Headers`** in its `verifySession` callback. This enables cookie-based auth flows where the session token is sent as a cookie rather than an `Authorization` header. ``` Request → extract token/cookies → verifySession(token, headers) → mapSession(session) → AuthContext ``` ## Cookie-Based Auth When your session framework reads cookies directly from headers: ```typescript const sessionAuth = createSessionAuthInterceptor({ verifySession: async (_token, headers) => { // The session framework reads the cookie from headers const session = await auth.api.getSession({ headers }); if (!session) throw new Error('Invalid session'); return session; }, mapSession: (session) => ({ subject: session.user.id, name: session.user.name, roles: session.user.roles ?? [], scopes: [], claims: session.user, type: 'session', }), }); ``` ## Session Caching Enable caching to avoid calling the session backend on every request: ```typescript const sessionAuth = createSessionAuthInterceptor({ verifySession: (token, headers) => auth.api.getSession({ headers }), mapSession: (session) => ({ /* ... */ }), cache: { ttl: 60_000 }, // Cache for 60 seconds }); ``` Cached sessions are keyed by the session token. When the TTL expires, the next request triggers a fresh `verifySession` call. ## Full Example ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { createSessionAuthInterceptor, createAuthzInterceptor } from '@connectum/auth'; const sessionAuth = createSessionAuthInterceptor({ verifySession: (token, headers) => auth.api.getSession({ headers }), mapSession: (session) => ({ subject: session.user.id, name: session.user.name, roles: session.user.roles ?? [], scopes: [], claims: session.user, type: 'session', }), cache: { ttl: 60_000 }, }); const server = createServer({ services: [routes], interceptors: [...createDefaultInterceptors(), sessionAuth], }); await server.start(); ``` ## Related * [Auth Overview](/en/guide/auth) -- all authentication strategies * [JWT Authentication](/en/guide/auth/jwt) -- token-based authentication * [Auth Context](/en/guide/auth/context) -- accessing identity in handlers * [@connectum/auth](/en/packages/auth) -- Package Guide * [@connectum/auth API](/en/api/@connectum/auth/) -- Full API Reference --- --- url: /en/api/@connectum/otel/shared.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / shared # shared Shared utilities for server and client OTel interceptors Contains common helper functions used by both createOtelInterceptor() and createOtelClientInterceptor(). ## Interfaces * [BaseAttributeParams](interfaces/BaseAttributeParams.md) ## Functions * [applyAttributeFilter](functions/applyAttributeFilter.md) * [buildBaseAttributes](functions/buildBaseAttributes.md) * [buildErrorAttributes](functions/buildErrorAttributes.md) * [estimateMessageSize](functions/estimateMessageSize.md) * [wrapAsyncIterable](functions/wrapAsyncIterable.md) --- --- url: /en/api/@connectum/auth/testing.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / testing # testing @connectum/auth/testing Test utilities for authentication and authorization. ## Variables * [TEST\_JWT\_SECRET](variables/TEST_JWT_SECRET.md) ## Functions * [createMockAuthContext](functions/createMockAuthContext.md) * [createTestJwt](functions/createTestJwt.md) * [withAuthContext](functions/withAuthContext.md) --- --- url: /en/guide/testing.md --- # Testing Scenario-based API testing with YAML runbooks -- validate gRPC and ConnectRPC services end-to-end. ## Quick Start Install [runn](https://github.com/k1LoW/runn) and create `tests/grpc-greeter.yml`: ```yaml desc: Greeter service -- gRPC runners: greq: grpc://localhost:5000 steps: say_hello: greq: greeter.v1.GreeterService/SayHello: message: name: Alice test: | current.res.status == 0 && current.res.message.message == 'Hello, Alice!' ``` ```bash runn run tests/grpc-greeter.yml ``` ::: info Working Example See [examples/runn](https://github.com/Connectum-Framework/examples/tree/main/runn) for a complete Docker-based E2E test suite with 9 runbooks covering healthcheck, reflection, auth, interceptors, timeout, and multi-service scenarios. ::: ## Key Concepts | Tool | Strengths | |------|-----------| | **runn** (recommended) | gRPC reflection support, HTTP testing, expr-lang assertions, single binary, Docker image | | **scenarigo** | Go plugin system, JUnit XML reports, template-based assertions | Both tools use YAML to define multi-step test scenarios. Scenario-based testing catches integration issues that unit tests miss: serialization, validation, interceptor chains, and health check endpoints. ## Learn More * [runn](/en/guide/testing/runn) -- gRPC/HTTP testing, streaming, TLS, CI/CD integration * [scenarigo](/en/guide/testing/scenarigo) -- alternative tool with Go plugins and JUnit reports --- --- url: /en/api/@connectum/interceptors/timeout.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / timeout # timeout Timeout interceptor Prevents requests from hanging indefinitely. ## Functions * [createTimeoutInterceptor](functions/createTimeoutInterceptor.md) --- --- url: /en/guide/security/tls.md --- # TLS Configuration Configure TLS for secure gRPC/ConnectRPC communication in Connectum services. ## TLS Options The `tls` option in `createServer()` accepts a `TLSOptions` object: ```typescript interface TLSOptions { /** Path to TLS private key file */ keyPath?: string; /** Path to TLS certificate file */ certPath?: string; /** TLS directory path (alternative to keyPath/certPath) */ dirPath?: string; } ``` ### Explicit File Paths Provide paths directly to the key and certificate files: ```typescript const server = createServer({ services: [routes], tls: { keyPath: './keys/server.key', certPath: './keys/server.crt', }, }); ``` Paths are resolved relative to the current working directory. ### Directory-Based Configuration Point to a directory containing `server.key` and `server.crt`: ```typescript const server = createServer({ services: [routes], tls: { dirPath: './keys', }, }); ``` The server looks for: * `/server.key` * `/server.crt` ### Environment Variable Configuration TLS paths can also be set via environment variables: | Variable | Description | |----------|-------------| | `TLS_DIR_PATH` | Directory containing `server.key` and `server.crt` | | `TLS_KEY_PATH` | Path to TLS private key file | | `TLS_CERT_PATH` | Path to TLS certificate file | When no explicit paths are provided, the `readTLSCertificates()` utility falls back to the `TLS_DIR_PATH` environment variable. ## TLS Utility Functions ### readTLSCertificates() Reads TLS key and certificate files: ```typescript import { readTLSCertificates } from '@connectum/core'; // With explicit paths const { key, cert } = readTLSCertificates({ keyPath: './keys/server.key', certPath: './keys/server.crt', }); // With directory path const { key, cert } = readTLSCertificates({ dirPath: './keys', }); // With default TLS path (from TLS_DIR_PATH env or convention) const { key, cert } = readTLSCertificates(); ``` ### getTLSPath() Returns the TLS directory path based on environment and convention: ```typescript import { getTLSPath } from '@connectum/core'; const path = getTLSPath(); // In production (NODE_ENV=production): current working directory // In development: ../../keys relative to cwd // Overridden by: TLS_DIR_PATH environment variable ``` ## Self-Signed Certificates for Development Generate self-signed certificates for local development: ```bash # Create keys directory mkdir -p keys # Generate self-signed certificate (valid for 365 days) openssl req -x509 -newkey rsa:4096 \ -keyout keys/server.key \ -out keys/server.crt \ -days 365 \ -nodes \ -subj "/C=US/ST=Local/L=Local/O=Dev/CN=localhost" ``` Use in your service: ```typescript const server = createServer({ services: [routes], port: 5000, tls: { dirPath: './keys', }, }); ``` ::: warning Development only Self-signed certificates should only be used in development. For production, use certificates from a trusted Certificate Authority (CA) or a service like Let's Encrypt. ::: ## Testing with TLS When testing with grpcurl against a TLS-enabled server using self-signed certificates: ```bash # Skip certificate verification (development only) grpcurl -insecure localhost:5000 list # Or provide the CA certificate grpcurl -cacert keys/server.crt localhost:5000 list ``` With curl: ```bash # Skip certificate verification curl -k https://localhost:5000/healthz # Or provide the CA certificate curl --cacert keys/server.crt https://localhost:5000/healthz ``` ## Additional HTTP/2 Options Use `http2Options` for any additional Node.js HTTP/2 secure server options: ```typescript const server = createServer({ services: [routes], port: 5000, tls: { keyPath: './keys/server.key', certPath: './keys/server.crt', }, http2Options: { // Minimum TLS version minVersion: 'TLSv1.3', // Cipher suites ciphers: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256', // Session timeout sessionTimeout: 300, }, // Handshake timeout handshakeTimeout: 30000, }); ``` ## HTTP/1.1 Support By default, Connectum allows HTTP/1.1 connections (via ALPN negotiation). This is important for ConnectRPC clients that use HTTP/1.1 with JSON: ```typescript const server = createServer({ services: [routes], tls: { dirPath: './keys' }, allowHTTP1: true, // default: true }); ``` To restrict to HTTP/2 only: ```typescript const server = createServer({ services: [routes], tls: { dirPath: './keys' }, allowHTTP1: false, }); ``` ## Related * [Security Overview](/en/guide/security) -- back to overview * [Mutual TLS (mTLS)](/en/guide/security/mtls) -- client certificate authentication, production best practices * [@connectum/core](/en/packages/core) -- Package Guide * [@connectum/core API](/en/api/@connectum/core/) -- Full API Reference --- --- url: /en/api/@connectum/otel/traceAll.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / traceAll # traceAll Proxy-based object wrapper for OpenTelemetry tracing Wraps all methods of an object in OTel spans using ES6 Proxy. Does NOT mutate the original object or its prototype. ## Functions * [traceAll](functions/traceAll.md) --- --- url: /en/api/@connectum/otel/traced.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / traced # traced Type-safe function wrapper for OpenTelemetry tracing Wraps a single function in an OTel span without mutating prototypes. ## Functions * [traced](functions/traced.md) --- --- url: /en/api/@connectum/otel/tracer.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / tracer # tracer Lazy access to the global OpenTelemetry Tracer ## Functions * [getTracer](functions/getTracer.md) --- --- url: /en/guide/observability/tracing.md --- # Tracing Connectum provides distributed tracing through OpenTelemetry interceptors for both server-side and client-side RPC calls, plus deep tracing utilities for your business logic. ## Server Interceptor `createOtelInterceptor()` automatically creates spans for all incoming RPC calls with OpenTelemetry semantic conventions: ```typescript import { createOtelInterceptor } from '@connectum/otel'; const interceptor = createOtelInterceptor({ filter: ({ service }) => !service.includes('grpc.health'), serverPort: 5000, recordMessages: false, trustRemote: false, }); ``` ### Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `filter` | `OtelFilter` | -- | Skip specific requests from tracing | | `serverAddress` | `string` | `os.hostname()` | Override server address attribute | | `serverPort` | `number` | -- | Add server port attribute | | `recordMessages` | `boolean` | `false` | Include request/response bodies in spans | | `trustRemote` | `boolean` | `false` | Use remote context as parent span | | `withoutTracing` | `boolean` | `false` | Disable tracing (metrics only) | | `withoutMetrics` | `boolean` | `false` | Disable metrics (tracing only) | | `attributeFilter` | `OtelAttributeFilter` | -- | Exclude specific span attributes | ## Client Interceptor For outgoing RPC calls, use `createOtelClientInterceptor()` to propagate trace context: ```typescript import { createConnectTransport } from '@connectrpc/connect-node'; import { createClient } from '@connectrpc/connect'; import { createOtelClientInterceptor } from '@connectum/otel'; import { UserService } from '#gen/user_pb.js'; const transport = createConnectTransport({ baseUrl: 'http://user-service:5001', interceptors: [ createOtelClientInterceptor({ serverAddress: 'user-service', // Required serverPort: 5001, }), ], }); const client = createClient(UserService, transport); ``` The client interceptor: * Injects trace context into outgoing requests via `propagation.inject()` * Creates `SpanKind.CLIENT` spans * Records `rpc.client.*` metrics ## Deep Tracing with `traced()` Wrap individual functions to create spans for your business logic: ```typescript import { traced } from '@connectum/otel'; const findUser = traced(async (id: string) => { return await db.users.findById(id); }, { name: 'UserRepository.findUser', recordArgs: true, // Record function arguments as span attributes }); // Each call creates a span: "UserRepository.findUser" const user = await findUser('123'); ``` ## Deep Tracing with `traceAll()` Wrap all methods of an object via Proxy: ```typescript import { traceAll } from '@connectum/otel'; class OrderRepository { async findById(id: string) { return await db.orders.findById(id); } async create(data: OrderData) { return await db.orders.create(data); } async updateStatus(id: string, status: string) { return await db.orders.update(id, { status }); } } // Auto-instrument all methods (does NOT mutate the original) const repository = traceAll(new OrderRepository(), { prefix: 'OrderRepository', exclude: ['internalHelper'], recordArgs: true, }); // Calls automatically create spans: // "OrderRepository.findById", "OrderRepository.create", etc. await repository.findById('order-123'); ``` ::: tip Performance `traceAll()` uses ES6 Proxy and creates method wrappers lazily on first access. It prevents double-wrapping automatically. ::: ## Distributed Tracing Across Services A complete example showing trace context propagation between two services: ```typescript // ---- Service A (Gateway) ---- import { createServer } from '@connectum/core'; import { createConnectTransport } from '@connectrpc/connect-node'; import { createClient } from '@connectrpc/connect'; import { createOtelInterceptor, createOtelClientInterceptor } from '@connectum/otel'; import { UserService } from '#gen/user_pb.js'; import gatewayRoutes from '#gen/routes.js'; // Server: trace incoming requests const server = createServer({ services: [gatewayRoutes], port: 5000, interceptors: [ createOtelInterceptor({ serverPort: 5000, filter: ({ service }) => !service.includes('grpc.health'), }), ], }); // Client: propagate trace context to Service B const transport = createConnectTransport({ baseUrl: 'http://user-service:5001', interceptors: [ createOtelClientInterceptor({ serverAddress: 'user-service', serverPort: 5001, }), ], }); const userClient = createClient(UserService, transport); // Trace context flows automatically: // Service A (server span) -> Service A (client span) -> Service B (server span) ``` ## Related * [Observability Overview](/en/guide/observability) -- back to overview * [Service Communication](/en/guide/service-communication) -- inter-service calls, transport configuration * [Client Interceptors](/en/guide/service-communication/client-interceptors) -- OTel client interceptor, resilience * [Metrics](/en/guide/observability/metrics) -- counters, histograms, automatic RPC metrics * [Logging](/en/guide/observability/logging) -- structured logging with trace correlation * [@connectum/otel](/en/packages/otel) -- Package Guide * [@connectum/otel API](/en/api/@connectum/otel/) -- Full API Reference --- --- url: /en/api/@connectum/otel/type-aliases/ArgsFilter.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / ArgsFilter # Type Alias: ArgsFilter() > **ArgsFilter** = (`args`) => `unknown`\[] Defined in: [packages/otel/src/types.ts:94](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L94) Args filter for traced() -- sanitize/transform function arguments before recording ## Parameters ### args `unknown`\[] ## Returns `unknown`\[] --- --- url: /en/api/@connectum/auth/proto/type-aliases/AuthRequirements.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [proto](../index.md) / AuthRequirements # Type Alias: AuthRequirements > **AuthRequirements** = `Message`<`"connectum.auth.v1.AuthRequirements"`> & `object` Defined in: packages/auth/gen/connectum/auth/v1/options\_pb.d.ts:13 Authorization requirements for a method or service. ## Type Declaration ### roles > **roles**: `string`\[] Roles required to access the method (any-of semantics: the user must have at least one of the listed roles). #### Generated from field: repeated string roles = 1; ### scopes > **scopes**: `string`\[] Scopes required to access the method (all-of semantics: the user must have every listed scope). #### Generated from field: repeated string scopes = 2; ## Generated from message connectum.auth.v1.AuthRequirements --- --- url: /en/api/@connectum/auth/type-aliases/AuthzEffect.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AuthzEffect # Type Alias: AuthzEffect > **AuthzEffect** = *typeof* [`AuthzEffect`](../variables/AuthzEffect.md)\[keyof *typeof* [`AuthzEffect`](../variables/AuthzEffect.md)] Defined in: [packages/auth/src/types.ts:67](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L67) Authorization rule effect --- --- url: /en/api/@connectum/otel/attributes/type-aliases/ConnectErrorCode.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ConnectErrorCode # Type Alias: ConnectErrorCode > **ConnectErrorCode** = *typeof* [`ConnectErrorCode`](../variables/ConnectErrorCode.md)\[keyof *typeof* [`ConnectErrorCode`](../variables/ConnectErrorCode.md)] Defined in: [packages/otel/src/attributes.ts:38](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L38) ConnectRPC error code map (numeric code -> string name) Based on Connect protocol error codes --- --- url: /en/api/@connectum/core/type-aliases/ConnectumEnv.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ConnectumEnv # Type Alias: ConnectumEnv > **ConnectumEnv** = `z.infer`<*typeof* [`ConnectumEnvSchema`](../variables/ConnectumEnvSchema.md)> Defined in: [packages/core/src/config/envSchema.ts:133](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L133) Connectum environment configuration type --- --- url: /en/api/@connectum/otel/type-aliases/ExporterType.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / ExporterType # Type Alias: ExporterType > **ExporterType** = *typeof* [`ExporterType`](../variables/ExporterType.md)\[keyof *typeof* [`ExporterType`](../variables/ExporterType.md)] Defined in: [packages/otel/src/config.ts:19](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L19) Available exporter types * CONSOLE: Outputs telemetry to stdout * OTLP\_HTTP: Sends telemetry via OTLP/HTTP protocol * OTLP\_GRPC: Sends telemetry via OTLP/gRPC protocol * NONE: Disables telemetry export --- --- url: /en/api/@connectum/core/types/type-aliases/HttpHandler.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / HttpHandler # Type Alias: HttpHandler() > **HttpHandler** = (`req`, `res`) => `boolean` Defined in: [packages/core/src/types.ts:61](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L61) HTTP handler for protocol-specific endpoints ## Parameters ### req [`NodeRequest`](NodeRequest.md) ### res [`NodeResponse`](NodeResponse.md) ## Returns `boolean` true if the request was handled, false otherwise --- --- url: /en/api/@connectum/auth/type-aliases/InterceptorFactory.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / InterceptorFactory # Type Alias: InterceptorFactory\ > **InterceptorFactory**<`TOptions`> = `TOptions` *extends* `void` ? () => `Interceptor` : (`options`) => `Interceptor` Defined in: [packages/auth/src/types.ts:14](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L14) Interceptor factory function type ## Type Parameters ### TOptions `TOptions` = `void` Options type for the interceptor --- --- url: /en/api/@connectum/interceptors/type-aliases/InterceptorFactory.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / InterceptorFactory # Type Alias: InterceptorFactory\ > **InterceptorFactory**<`TOptions`> = `TOptions` *extends* `void` ? () => `Interceptor` : (`options`) => `Interceptor` Defined in: [types.ts:16](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L16) Interceptor factory function type ## Type Parameters ### TOptions `TOptions` = `void` Options type for the interceptor --- --- url: /en/api/@connectum/core/types/type-aliases/LifecycleEvent.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / LifecycleEvent # Type Alias: LifecycleEvent > **LifecycleEvent** = *typeof* [`LifecycleEvent`](../variables/LifecycleEvent.md)\[keyof *typeof* [`LifecycleEvent`](../variables/LifecycleEvent.md)] Defined in: [packages/core/src/types.ts:143](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L143) Lifecycle event names --- --- url: /en/api/@connectum/otel/type-aliases/MethodArgsFilter.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / MethodArgsFilter # Type Alias: MethodArgsFilter() > **MethodArgsFilter** = (`methodName`, `args`) => `unknown`\[] Defined in: [packages/otel/src/types.ts:99](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L99) Args filter for traceAll() -- has access to method name ## Parameters ### methodName `string` ### args `unknown`\[] ## Returns `unknown`\[] --- --- url: /en/api/@connectum/auth/proto/type-aliases/MethodAuth.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [proto](../index.md) / MethodAuth # Type Alias: MethodAuth > **MethodAuth** = `Message`<`"connectum.auth.v1.MethodAuth"`> & `object` Defined in: packages/auth/gen/connectum/auth/v1/options\_pb.d.ts:39 Authorization configuration for an RPC method. ## Type Declaration ### policy > **policy**: `string` Override the service-level default\_policy for this method. Valid values: "allow", "deny". #### Generated from field: optional string policy = 3; ### public > **public**: `boolean` Skip both authentication and authorization for this method. #### Generated from field: optional bool public = 1; ### requires? > `optional` **requires**: [`AuthRequirements`](AuthRequirements.md) Access requirements (roles/scopes) for this method. #### Generated from field: optional connectum.auth.v1.AuthRequirements requires = 2; ## Generated from message connectum.auth.v1.MethodAuth --- --- url: /en/api/@connectum/interceptors/type-aliases/MethodFilterMap.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / MethodFilterMap # Type Alias: MethodFilterMap > **MethodFilterMap** = `Record`<`string`, `Interceptor`\[]> Defined in: [types.ts:224](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/interceptors/src/types.ts#L224) Method pattern to interceptors mapping. Patterns: * `"*"` -- matches all methods (global) * `"package.Service/*"` -- matches all methods of a service (service wildcard) * `"package.Service/Method"` -- matches exact method Key format uses protobuf fully-qualified service name: `service.typeName + "/" + method.name` All matching patterns are executed in order: global -> service wildcard -> exact match. Within each pattern, interceptors execute in array order. ## Example ```typescript const methods: MethodFilterMap = { "*": [logRequest], "admin.v1.AdminService/*": [requireAdmin], "user.v1.UserService/DeleteUser": [requireAdmin, auditLog], }; ``` --- --- url: /en/api/@connectum/core/types/type-aliases/NodeRequest.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / NodeRequest # Type Alias: NodeRequest > **NodeRequest** = `IncomingMessage` | `Http2ServerRequest` Defined in: [packages/core/src/types.ts:19](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L19) Incoming request — HTTP/1.1 or HTTP/2 --- --- url: /en/api/@connectum/core/types/type-aliases/NodeResponse.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / NodeResponse # Type Alias: NodeResponse > **NodeResponse** = `ServerResponse` | `Http2ServerResponse` Defined in: [packages/core/src/types.ts:22](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L22) Server response — HTTP/1.1 or HTTP/2 --- --- url: /en/api/@connectum/otel/type-aliases/OtelAttributeFilter.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / OtelAttributeFilter # Type Alias: OtelAttributeFilter() > **OtelAttributeFilter** = (`key`, `value`) => `boolean` Defined in: [packages/otel/src/types.ts:24](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L24) Filter callback to exclude specific attributes from spans/metrics ## Parameters ### key `string` Attribute key ### value Attribute value `string` | `number` | `boolean` ## Returns `boolean` `true` to include, `false` to exclude --- --- url: /en/api/@connectum/otel/type-aliases/OtelFilter.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / OtelFilter # Type Alias: OtelFilter() > **OtelFilter** = (`context`) => `boolean` Defined in: [packages/otel/src/types.ts:15](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/types.ts#L15) Filter callback to skip specific RPC requests from instrumentation ## Parameters ### context RPC call context #### method `string` #### service `string` #### stream `boolean` ## Returns `boolean` `true` to instrument, `false` to skip --- --- url: /en/api/@connectum/core/types/type-aliases/ServerState.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / ServerState # Type Alias: ServerState > **ServerState** = *typeof* [`ServerState`](../variables/ServerState.md)\[keyof *typeof* [`ServerState`](../variables/ServerState.md)] Defined in: [packages/core/src/types.ts:125](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L125) Server state constants Note: Using const object instead of enum for native TypeScript compatibility --- --- url: /en/api/@connectum/auth/proto/type-aliases/ServiceAuth.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [proto](../index.md) / ServiceAuth # Type Alias: ServiceAuth > **ServiceAuth** = `Message`<`"connectum.auth.v1.ServiceAuth"`> & `object` Defined in: packages/auth/gen/connectum/auth/v1/options\_pb.d.ts:70 Default authorization configuration for all methods in a service. ## Type Declaration ### defaultPolicy > **defaultPolicy**: `string` Default policy when no rule matches. Valid values: "allow", "deny". #### Generated from field: optional string default\_policy = 1; ### defaultRequires? > `optional` **defaultRequires**: [`AuthRequirements`](AuthRequirements.md) Default access requirements applied to all methods unless overridden at the method level. #### Generated from field: optional connectum.auth.v1.AuthRequirements default\_requires = 2; ### public > **public**: `boolean` Mark all methods in the service as public (skip authentication and authorization). #### Generated from field: optional bool public = 3; ## Generated from message connectum.auth.v1.ServiceAuth --- --- url: /en/api/@connectum/core/types/type-aliases/ServiceRoute.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / ServiceRoute # Type Alias: ServiceRoute() > **ServiceRoute** = (`router`) => `void` Defined in: [packages/core/src/types.ts:32](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L32) Service route function Function that registers services on the ConnectRouter. ## Parameters ### router `ConnectRouter` ## Returns `void` --- --- url: /en/api/@connectum/healthcheck/type-aliases/ServingStatus.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / ServingStatus # Type Alias: ServingStatus > **ServingStatus** = `HealthCheckResponse_ServingStatus` Defined in: [types.ts:14](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L14) Service serving status Re-export generated const from proto. --- --- url: /en/api/@connectum/core/types/type-aliases/ShutdownHook.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / ShutdownHook # Type Alias: ShutdownHook() > **ShutdownHook** = () => `void` | `Promise`<`void`> Defined in: [packages/core/src/types.ts:39](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L39) Shutdown hook function type A function called during graceful shutdown. May be synchronous or async. ## Returns `void` | `Promise`<`void`> --- --- url: /en/api/@connectum/core/types/type-aliases/TransportServer.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / TransportServer # Type Alias: TransportServer > **TransportServer** = `HttpServer` | `Http2Server` | `Http2SecureServer` Defined in: [packages/core/src/types.ts:25](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L25) Underlying transport server — HTTP/1.1, HTTP/2 plaintext, or HTTP/2 TLS --- --- url: /en/api/@connectum/core/types.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / types # types Public API types for Server ## Interfaces * [CreateServerOptions](interfaces/CreateServerOptions.md) * [ProtocolContext](interfaces/ProtocolContext.md) * [ProtocolRegistration](interfaces/ProtocolRegistration.md) * [Server](interfaces/Server.md) * [ShutdownOptions](interfaces/ShutdownOptions.md) * [TLSOptions](interfaces/TLSOptions.md) ## Type Aliases * [HttpHandler](type-aliases/HttpHandler.md) * [LifecycleEvent](type-aliases/LifecycleEvent.md) * [NodeRequest](type-aliases/NodeRequest.md) * [NodeResponse](type-aliases/NodeResponse.md) * [ServerState](type-aliases/ServerState.md) * [ServiceRoute](type-aliases/ServiceRoute.md) * [ShutdownHook](type-aliases/ShutdownHook.md) * [TransportServer](type-aliases/TransportServer.md) ## Variables * [LifecycleEvent](variables/LifecycleEvent.md) * [ServerState](variables/ServerState.md) --- --- url: /en/guide/typescript.md --- # TypeScript Native TypeScript execution via type stripping on Node.js 25+. Packages compile to JS + DTS via tsup for any runtime (Node.js 18+, Bun, tsx). ## Quick Start ```bash # Node.js 25+ -- run TypeScript directly node src/index.ts # Development with auto-reload node --watch src/index.ts ``` No loaders, no compilation step, no `tsc` required to run your code. TypeScript is used for type checking only (`tsc --noEmit`). ## Key Concepts | Constraint | Rule | |------------|------| | **No `enum`** | Use `const` objects with `as const` | | **No `namespace`** | Only type-only namespaces allowed | | **No parameter properties** | Use explicit property declarations | | **Explicit `import type`** | `verbatimModuleSyntax: true` | | **`.ts` extensions** | Relative imports use `.ts`; generated code uses `.js` | | **`node:` prefix** | Required for Node.js built-in modules | These constraints come from `erasableSyntaxOnly: true` -- TypeScript syntax must be removable by stripping types, leaving valid JavaScript. ## Learn More * [Runtime Support](/en/guide/typescript/runtime-support) -- Node.js 25+, Bun, tsx, Docker, comparison table * [Erasable Syntax](/en/guide/typescript/erasable-syntax) -- constraints, import rules, tsconfig.json * [Proto Enums](/en/guide/typescript/proto-enums) -- two-step generation workaround for proto enums * [Patterns & Workflow](/en/guide/typescript/patterns) -- named parameters, branded types, development workflow * [@connectum/core](/en/packages/core) -- Package Guide --- --- url: /en/api/@connectum/cli/utils/reflection.md --- [Connectum API Reference](../../../../index.md) / [@connectum/cli](../../index.md) / utils/reflection # utils/reflection Reflection client utilities Wraps @lambdalisue/connectrpc-grpcreflect ServerReflectionClient for use in CLI commands. ## Interfaces * [ReflectionResult](interfaces/ReflectionResult.md) ## Functions * [fetchFileDescriptorSetBinary](functions/fetchFileDescriptorSetBinary.md) * [fetchReflectionData](functions/fetchReflectionData.md) --- --- url: /en/guide/validation.md description: Proto-first input validation with protovalidate in Connectum services. --- # Validation Connectum uses `@connectrpc/validate` (backed by `@bufbuild/protovalidate`) for schema-based input validation. Constraints are defined directly in `.proto` files and enforced automatically by the validation interceptor. ## Overview The validation approach is **proto-first**: validation rules live alongside message definitions in `.proto` files. This ensures the proto schema is the single source of truth for both data structure and constraints. ``` Client → errorHandler → ... → validation → serializer → Handler ↓ Invalid: INVALID_ARGUMENT ``` Validation runs as the 7th interceptor in the default chain (before serializer, after resilience interceptors). Invalid requests are rejected with `INVALID_ARGUMENT` before reaching the handler. ## Setup Install the required packages: ```bash # Validation runtime pnpm add @bufbuild/protovalidate @connectrpc/validate # Proto dependency (buf.yaml) # Add buf.build/bufbuild/protovalidate to deps ``` Declare the dependency in `buf.yaml`: ```yaml version: v2 deps: - buf.build/bufbuild/protovalidate ``` Fetch dependencies: ```bash npx buf dep update ``` ## Proto Constraints Import `buf/validate/validate.proto` and annotate fields with constraints: ```protobuf syntax = "proto3"; import "buf/validate/validate.proto"; message CreateOrderRequest { // String constraints string customer_id = 1 [(buf.validate.field).string.min_len = 1]; string currency = 2 [(buf.validate.field).string = {min_len: 3, max_len: 3}]; string email = 3 [(buf.validate.field).string.email = true]; // Numeric constraints int32 quantity = 4 [(buf.validate.field).int32.gt = 0]; int32 page_size = 5 [(buf.validate.field).int32 = {gte: 1, lte: 100}]; // Repeated constraints repeated OrderItem items = 6 [(buf.validate.field).repeated.min_items = 1]; // Required message ShippingAddress address = 7 [(buf.validate.field).required = true]; } message GetOrderRequest { string order_id = 1 [(buf.validate.field).string.uuid = true]; } ``` ### Available Constraints | Type | Constraints | |------|------------| | **String** | `min_len`, `max_len`, `pattern` (regex), `email`, `uri`, `uuid`, `ip`, `hostname` | | **Numeric** | `lt`, `lte`, `gt`, `gte`, `in`, `not_in`, `const` | | **Repeated** | `min_items`, `max_items`, `unique` | | **Message** | `required`, `skip` | | **Enum** | `defined_only` | | **Map** | `min_pairs`, `max_pairs`, key/value constraints | ## Validation Interceptor Validation is enabled by default in `createDefaultInterceptors()`: ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors } from '@connectum/interceptors'; const server = createServer({ services: [routes], interceptors: createDefaultInterceptors(), // validation enabled }); ``` ### Disabling Validation ```typescript const interceptors = createDefaultInterceptors({ validation: false, }); ``` ### Standalone Usage For custom configuration, use `createValidateInterceptor()` directly: ```typescript import { createValidateInterceptor } from '@connectrpc/validate'; const server = createServer({ services: [routes], interceptors: [ createValidateInterceptor(), // ... other interceptors ], }); ``` ## Error Messages When validation fails, the interceptor throws a `ConnectError` with code `INVALID_ARGUMENT`: ``` Code: INVALID_ARGUMENT Message: "customer_id: value length must be at least 1 characters [string.min_len]" ``` Error messages include the field path, the violated constraint, and the constraint identifier. This makes it straightforward for clients to display meaningful validation errors. ## Custom Validation Proto constraints cover structural validation (format, range, presence). For business-level validation (e.g., "email must be unique", "order total must not exceed credit limit"), validate in the service handler: ```typescript import { ConnectError, Code } from '@connectrpc/connect'; async sayHello(request: SayHelloRequest) { // Business validation (beyond proto constraints) const exists = await db.findByEmail(request.email); if (exists) { throw new ConnectError('Email already registered', Code.AlreadyExists); } // ... } ``` ## Related * [Interceptors](/en/guide/interceptors) -- the validation interceptor in the chain * [@connectum/interceptors](/en/packages/interceptors) -- Package Guide * [ADR-005: Input Validation Strategy](/en/contributing/adr/005-input-validation-strategy) -- design rationale and alternatives * [protovalidate documentation](https://github.com/bufbuild/protovalidate) -- full constraint reference * [@connectrpc/validate](https://www.npmjs.com/package/@connectrpc/validate) -- official ConnectRPC validation package --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_ERROR_TYPE.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_ERROR\_TYPE # Variable: ATTR\_ERROR\_TYPE > `const` **ATTR\_ERROR\_TYPE**: `"error.type"` = `"error.type"` Defined in: [packages/otel/src/attributes.ts:20](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L20) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_NETWORK_PEER_ADDRESS.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_NETWORK\_PEER\_ADDRESS # Variable: ATTR\_NETWORK\_PEER\_ADDRESS > `const` **ATTR\_NETWORK\_PEER\_ADDRESS**: `"network.peer.address"` = `"network.peer.address"` Defined in: [packages/otel/src/attributes.ts:25](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L25) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_NETWORK_PEER_PORT.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_NETWORK\_PEER\_PORT # Variable: ATTR\_NETWORK\_PEER\_PORT > `const` **ATTR\_NETWORK\_PEER\_PORT**: `"network.peer.port"` = `"network.peer.port"` Defined in: [packages/otel/src/attributes.ts:26](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L26) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_NETWORK_PROTOCOL_NAME.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_NETWORK\_PROTOCOL\_NAME # Variable: ATTR\_NETWORK\_PROTOCOL\_NAME > `const` **ATTR\_NETWORK\_PROTOCOL\_NAME**: `"network.protocol.name"` = `"network.protocol.name"` Defined in: [packages/otel/src/attributes.ts:23](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L23) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_NETWORK_TRANSPORT.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_NETWORK\_TRANSPORT # Variable: ATTR\_NETWORK\_TRANSPORT > `const` **ATTR\_NETWORK\_TRANSPORT**: `"network.transport"` = `"network.transport"` Defined in: [packages/otel/src/attributes.ts:24](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L24) --- --- url: >- /en/api/@connectum/otel/attributes/variables/ATTR_RPC_CONNECT_RPC_STATUS_CODE.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_RPC\_CONNECT\_RPC\_STATUS\_CODE # Variable: ATTR\_RPC\_CONNECT\_RPC\_STATUS\_CODE > `const` **ATTR\_RPC\_CONNECT\_RPC\_STATUS\_CODE**: `"rpc.connect_rpc.status_code"` = `"rpc.connect_rpc.status_code"` Defined in: [packages/otel/src/attributes.ts:19](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L19) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_RPC_MESSAGE_ID.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_RPC\_MESSAGE\_ID # Variable: ATTR\_RPC\_MESSAGE\_ID > `const` **ATTR\_RPC\_MESSAGE\_ID**: `"rpc.message.id"` = `"rpc.message.id"` Defined in: [packages/otel/src/attributes.ts:31](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L31) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_RPC_MESSAGE_TYPE.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_RPC\_MESSAGE\_TYPE # Variable: ATTR\_RPC\_MESSAGE\_TYPE > `const` **ATTR\_RPC\_MESSAGE\_TYPE**: `"rpc.message.type"` = `"rpc.message.type"` Defined in: [packages/otel/src/attributes.ts:30](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L30) --- --- url: >- /en/api/@connectum/otel/attributes/variables/ATTR_RPC_MESSAGE_UNCOMPRESSED_SIZE.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_RPC\_MESSAGE\_UNCOMPRESSED\_SIZE # Variable: ATTR\_RPC\_MESSAGE\_UNCOMPRESSED\_SIZE > `const` **ATTR\_RPC\_MESSAGE\_UNCOMPRESSED\_SIZE**: `"rpc.message.uncompressed_size"` = `"rpc.message.uncompressed_size"` Defined in: [packages/otel/src/attributes.ts:32](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L32) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_RPC_METHOD.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_RPC\_METHOD # Variable: ATTR\_RPC\_METHOD > `const` **ATTR\_RPC\_METHOD**: `"rpc.method"` = `"rpc.method"` Defined in: [packages/otel/src/attributes.ts:18](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L18) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_RPC_SERVICE.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_RPC\_SERVICE # Variable: ATTR\_RPC\_SERVICE > `const` **ATTR\_RPC\_SERVICE**: `"rpc.service"` = `"rpc.service"` Defined in: [packages/otel/src/attributes.ts:17](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L17) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_RPC_SYSTEM.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_RPC\_SYSTEM # Variable: ATTR\_RPC\_SYSTEM > `const` **ATTR\_RPC\_SYSTEM**: `"rpc.system"` = `"rpc.system"` Defined in: [packages/otel/src/attributes.ts:16](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L16) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_SERVER_ADDRESS.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_SERVER\_ADDRESS # Variable: ATTR\_SERVER\_ADDRESS > `const` **ATTR\_SERVER\_ADDRESS**: `"server.address"` = `"server.address"` Defined in: [packages/otel/src/attributes.ts:21](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L21) --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_SERVER_PORT.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_SERVER\_PORT # Variable: ATTR\_SERVER\_PORT > `const` **ATTR\_SERVER\_PORT**: `"server.port"` = `"server.port"` Defined in: [packages/otel/src/attributes.ts:22](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L22) --- --- url: /en/api/@connectum/auth/variables/AUTH_HEADERS.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AUTH\_HEADERS # Variable: AUTH\_HEADERS > `const` **AUTH\_HEADERS**: `object` Defined in: [packages/auth/src/types.ts:49](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L49) Standard header names for auth context propagation. Used for cross-service context propagation (similar to Envoy credential injection). The auth interceptor sets these headers when propagateHeaders is true. WARNING: These headers are trusted ONLY in service-to-service communication where transport security (mTLS) is established. Never trust these headers from external clients without using createGatewayAuthInterceptor(). ## Type Declaration ### CLAIMS > `readonly` **CLAIMS**: `"x-auth-claims"` = `"x-auth-claims"` JSON-encoded claims object ### NAME > `readonly` **NAME**: `"x-auth-name"` = `"x-auth-name"` Human-readable display name ### ROLES > `readonly` **ROLES**: `"x-auth-roles"` = `"x-auth-roles"` JSON-encoded roles array ### SCOPES > `readonly` **SCOPES**: `"x-auth-scopes"` = `"x-auth-scopes"` Space-separated scopes ### SUBJECT > `readonly` **SUBJECT**: `"x-auth-subject"` = `"x-auth-subject"` Authenticated subject identifier ### TYPE > `readonly` **TYPE**: `"x-auth-type"` = `"x-auth-type"` Credential type (jwt, api-key, mtls, etc.) --- --- url: /en/api/@connectum/auth/variables/authContextStorage.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / authContextStorage # Variable: authContextStorage > `const` **authContextStorage**: `AsyncLocalStorage`<[`AuthContext`](../interfaces/AuthContext.md)> Defined in: [packages/auth/src/context.ts:20](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/context.ts#L20) Module-level AsyncLocalStorage for auth context. Set by auth interceptors, read by handlers via getAuthContext(). Automatically isolated per async context (request). --- --- url: /en/api/@connectum/auth/proto/variables/AuthRequirementsSchema.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [proto](../index.md) / AuthRequirementsSchema # Variable: AuthRequirementsSchema > `const` **AuthRequirementsSchema**: `GenMessage`<[`AuthRequirements`](../type-aliases/AuthRequirements.md)> Defined in: packages/auth/gen/connectum/auth/v1/options\_pb.d.ts:33 Describes the message connectum.auth.v1.AuthRequirements. Use `create(AuthRequirementsSchema)` to create a new message. --- --- url: /en/api/@connectum/auth/variables/AuthzEffect.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / AuthzEffect # Variable: AuthzEffect > `const` **AuthzEffect**: `object` Defined in: [packages/auth/src/types.ts:67](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/types.ts#L67) Authorization rule effect ## Type Declaration ### ALLOW > `readonly` **ALLOW**: `"allow"` = `"allow"` ### DENY > `readonly` **DENY**: `"deny"` = `"deny"` --- --- url: /en/api/@connectum/core/variables/BooleanFromStringSchema.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / BooleanFromStringSchema # Variable: BooleanFromStringSchema > `const` **BooleanFromStringSchema**: `ZodPipe`<`ZodDefault`<`ZodEnum`<{ `0`: `"0"`; `1`: `"1"`; `false`: `"false"`; `no`: `"no"`; `true`: `"true"`; `yes`: `"yes"`; }>>, `ZodTransform`<`boolean`, `"0"` | `"1"` | `"true"` | `"false"` | `"yes"` | `"no"`>> Defined in: [packages/core/src/config/envSchema.ts:35](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L35) Boolean from string schema (for ENV variables) --- --- url: /en/api/@connectum/otel/attributes/variables/ConnectErrorCode.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ConnectErrorCode # Variable: ConnectErrorCode > `const` **ConnectErrorCode**: `object` Defined in: [packages/otel/src/attributes.ts:38](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L38) ConnectRPC error code map (numeric code -> string name) Based on Connect protocol error codes ## Type Declaration ### ABORTED > `readonly` **ABORTED**: `10` = `10` ### ALREADY\_EXISTS > `readonly` **ALREADY\_EXISTS**: `6` = `6` ### CANCELED > `readonly` **CANCELED**: `1` = `1` ### DATA\_LOSS > `readonly` **DATA\_LOSS**: `15` = `15` ### DEADLINE\_EXCEEDED > `readonly` **DEADLINE\_EXCEEDED**: `4` = `4` ### FAILED\_PRECONDITION > `readonly` **FAILED\_PRECONDITION**: `9` = `9` ### INTERNAL > `readonly` **INTERNAL**: `13` = `13` ### INVALID\_ARGUMENT > `readonly` **INVALID\_ARGUMENT**: `3` = `3` ### NOT\_FOUND > `readonly` **NOT\_FOUND**: `5` = `5` ### OUT\_OF\_RANGE > `readonly` **OUT\_OF\_RANGE**: `11` = `11` ### PERMISSION\_DENIED > `readonly` **PERMISSION\_DENIED**: `7` = `7` ### RESOURCE\_EXHAUSTED > `readonly` **RESOURCE\_EXHAUSTED**: `8` = `8` ### UNAUTHENTICATED > `readonly` **UNAUTHENTICATED**: `16` = `16` ### UNAVAILABLE > `readonly` **UNAVAILABLE**: `14` = `14` ### UNIMPLEMENTED > `readonly` **UNIMPLEMENTED**: `12` = `12` ### UNKNOWN > `readonly` **UNKNOWN**: `2` = `2` --- --- url: /en/api/@connectum/otel/attributes/variables/ConnectErrorCodeName.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ConnectErrorCodeName # Variable: ConnectErrorCodeName > `const` **ConnectErrorCodeName**: `Record`<`number`, `string`> Defined in: [packages/otel/src/attributes.ts:62](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L62) Reverse map: numeric code -> string name for span attributes --- --- url: /en/api/@connectum/core/variables/ConnectumEnvSchema.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ConnectumEnvSchema # Variable: ConnectumEnvSchema > `const` **ConnectumEnvSchema**: `ZodObject`<{ `GRACEFUL_SHUTDOWN_ENABLED`: `ZodPipe`<`ZodDefault`<`ZodEnum`<{ `0`: `"0"`; `1`: `"1"`; `false`: `"false"`; `no`: `"no"`; `true`: `"true"`; `yes`: `"yes"`; }>>, `ZodTransform`<`boolean`, `"0"` | `"1"` | `"true"` | `"false"` | `"yes"` | `"no"`>>; `GRACEFUL_SHUTDOWN_TIMEOUT_MS`: `ZodDefault`<`ZodCoercedNumber`<`unknown`>>; `HTTP_HEALTH_ENABLED`: `ZodPipe`<`ZodDefault`<`ZodEnum`<{ `0`: `"0"`; `1`: `"1"`; `false`: `"false"`; `no`: `"no"`; `true`: `"true"`; `yes`: `"yes"`; }>>, `ZodTransform`<`boolean`, `"0"` | `"1"` | `"true"` | `"false"` | `"yes"` | `"no"`>>; `HTTP_HEALTH_PATH`: `ZodDefault`<`ZodString`>; `LISTEN`: `ZodDefault`<`ZodString`>; `LOG_BACKEND`: `ZodDefault`<`ZodEnum`<{ `console`: `"console"`; `otel`: `"otel"`; `pino`: `"pino"`; }>>; `LOG_FORMAT`: `ZodDefault`<`ZodEnum`<{ `json`: `"json"`; `pretty`: `"pretty"`; }>>; `LOG_LEVEL`: `ZodDefault`<`ZodEnum`<{ `debug`: `"debug"`; `error`: `"error"`; `info`: `"info"`; `warn`: `"warn"`; }>>; `NODE_ENV`: `ZodDefault`<`ZodEnum`<{ `development`: `"development"`; `production`: `"production"`; `test`: `"test"`; }>>; `OTEL_EXPORTER_OTLP_ENDPOINT`: `ZodOptional`<`ZodString`>; `OTEL_SERVICE_NAME`: `ZodOptional`<`ZodString`>; `PORT`: `ZodDefault`<`ZodCoercedNumber`<`unknown`>>; }, `$strip`> Defined in: [packages/core/src/config/envSchema.ts:53](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L53) Connectum environment configuration schema All environment variables with their defaults and validation. Based on 12-Factor App configuration principles. ## Example ```typescript const config = ConnectumEnvSchema.parse(process.env); console.log(config.PORT); // 5000 (default) console.log(config.LOG_LEVEL); // 'info' (default) ``` --- --- url: /en/api/@connectum/otel/variables/ExporterType.md --- [Connectum API Reference](../../../index.md) / [@connectum/otel](../index.md) / ExporterType # Variable: ExporterType > `const` **ExporterType**: `object` Defined in: [packages/otel/src/config.ts:19](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/config.ts#L19) Available exporter types * CONSOLE: Outputs telemetry to stdout * OTLP\_HTTP: Sends telemetry via OTLP/HTTP protocol * OTLP\_GRPC: Sends telemetry via OTLP/gRPC protocol * NONE: Disables telemetry export ## Type Declaration ### CONSOLE > `readonly` **CONSOLE**: `"console"` = `"console"` ### NONE > `readonly` **NONE**: `"none"` = `"none"` ### OTLP\_GRPC > `readonly` **OTLP\_GRPC**: `"otlp/grpc"` = `"otlp/grpc"` ### OTLP\_HTTP > `readonly` **OTLP\_HTTP**: `"otlp/http"` = `"otlp/http"` --- --- url: /en/api/@connectum/healthcheck/variables/healthcheckManager.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / healthcheckManager # Variable: healthcheckManager > `const` **healthcheckManager**: [`HealthcheckManager`](../classes/HealthcheckManager.md) Defined in: [Healthcheck.ts:40](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/Healthcheck.ts#L40) Module-level singleton health manager Importable from any file to update service health status. ## Example ```typescript import { healthcheckManager, ServingStatus } from '@connectum/healthcheck'; healthcheckManager.update(ServingStatus.SERVING); healthcheckManager.update(ServingStatus.NOT_SERVING, 'my.service.v1.MyService'); ``` --- --- url: /en/api/@connectum/core/types/variables/LifecycleEvent.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / LifecycleEvent # Variable: LifecycleEvent > `const` **LifecycleEvent**: `object` Defined in: [packages/core/src/types.ts:143](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L143) Lifecycle event names ## Type Declaration ### ERROR > `readonly` **ERROR**: `"error"` = `"error"` Emitted on error ### READY > `readonly` **READY**: `"ready"` = `"ready"` Emitted when server is ready to accept connections ### START > `readonly` **START**: `"start"` = `"start"` Emitted when server starts (before ready) ### STOP > `readonly` **STOP**: `"stop"` = `"stop"` Emitted when server stops ### STOPPING > `readonly` **STOPPING**: `"stopping"` = `"stopping"` Emitted when server begins graceful shutdown --- --- url: /en/api/@connectum/core/variables/LogFormatSchema.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / LogFormatSchema # Variable: LogFormatSchema > `const` **LogFormatSchema**: `ZodDefault`<`ZodEnum`<{ `json`: `"json"`; `pretty`: `"pretty"`; }>> Defined in: [packages/core/src/config/envSchema.ts:20](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L20) Log format schema --- --- url: /en/api/@connectum/core/variables/LoggerBackendSchema.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / LoggerBackendSchema # Variable: LoggerBackendSchema > `const` **LoggerBackendSchema**: `ZodDefault`<`ZodEnum`<{ `console`: `"console"`; `otel`: `"otel"`; `pino`: `"pino"`; }>> Defined in: [packages/core/src/config/envSchema.ts:25](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L25) Logger backend schema --- --- url: /en/api/@connectum/core/variables/LogLevelSchema.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / LogLevelSchema # Variable: LogLevelSchema > `const` **LogLevelSchema**: `ZodDefault`<`ZodEnum`<{ `debug`: `"debug"`; `error`: `"error"`; `info`: `"info"`; `warn`: `"warn"`; }>> Defined in: [packages/core/src/config/envSchema.ts:15](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L15) Log level schema with validation --- --- url: /en/api/@connectum/auth/proto/variables/method_auth.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [proto](../index.md) / method\_auth # Variable: method\_auth > `const` **method\_auth**: `GenExtension`<`MethodOptions`, [`MethodAuth`](../type-aliases/MethodAuth.md)> Defined in: packages/auth/gen/connectum/auth/v1/options\_pb.d.ts:101 ## Generated from extension: optional connectum.auth.v1.MethodAuth method\_auth = 50100; --- --- url: /en/api/@connectum/auth/proto/variables/MethodAuthSchema.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [proto](../index.md) / MethodAuthSchema # Variable: MethodAuthSchema > `const` **MethodAuthSchema**: `GenMessage`<[`MethodAuth`](../type-aliases/MethodAuth.md)> Defined in: packages/auth/gen/connectum/auth/v1/options\_pb.d.ts:64 Describes the message connectum.auth.v1.MethodAuth. Use `create(MethodAuthSchema)` to create a new message. --- --- url: /en/api/@connectum/core/variables/NodeEnvSchema.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / NodeEnvSchema # Variable: NodeEnvSchema > `const` **NodeEnvSchema**: `ZodDefault`<`ZodEnum`<{ `development`: `"development"`; `production`: `"production"`; `test`: `"test"`; }>> Defined in: [packages/core/src/config/envSchema.ts:30](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/config/envSchema.ts#L30) Node environment schema --- --- url: /en/api/@connectum/cli/commands/proto-sync/variables/protoSyncCommand.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/cli](../../../index.md) / [commands/proto-sync](../index.md) / protoSyncCommand # Variable: protoSyncCommand > `const` **protoSyncCommand**: `CommandDef`<{ `dry-run`: { `default`: `false`; `description`: `"Show what would be synced without generating code"`; `type`: `"boolean"`; }; `from`: { `description`: `"Server address (e.g., localhost:5000 or http://localhost:5000)"`; `required`: `true`; `type`: `"string"`; }; `out`: { `description`: `"Output directory for generated types"`; `required`: `true`; `type`: `"string"`; }; `template`: { `description`: `"Path to custom buf.gen.yaml template"`; `type`: `"string"`; }; }> Defined in: [commands/proto-sync.ts:111](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/cli/src/commands/proto-sync.ts#L111) citty command definition for `connectum proto sync`. --- --- url: /en/api/@connectum/otel/attributes/variables/RPC_MESSAGE_EVENT.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / RPC\_MESSAGE\_EVENT # Variable: RPC\_MESSAGE\_EVENT > `const` **RPC\_MESSAGE\_EVENT**: `"rpc.message"` = `"rpc.message"` Defined in: [packages/otel/src/attributes.ts:29](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L29) --- --- url: /en/api/@connectum/otel/attributes/variables/RPC_SYSTEM_CONNECT_RPC.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / RPC\_SYSTEM\_CONNECT\_RPC # Variable: RPC\_SYSTEM\_CONNECT\_RPC > `const` **RPC\_SYSTEM\_CONNECT\_RPC**: `"connect_rpc"` = `"connect_rpc"` Defined in: [packages/otel/src/attributes.ts:13](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/otel/src/attributes.ts#L13) --- --- url: /en/api/@connectum/core/types/variables/ServerState.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / ServerState # Variable: ServerState > `const` **ServerState**: `object` Defined in: [packages/core/src/types.ts:125](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/types.ts#L125) Server state constants Note: Using const object instead of enum for native TypeScript compatibility ## Type Declaration ### CREATED > `readonly` **CREATED**: `"created"` = `"created"` Server created but not started ### RUNNING > `readonly` **RUNNING**: `"running"` = `"running"` Server is running and accepting connections ### STARTING > `readonly` **STARTING**: `"starting"` = `"starting"` Server is starting ### STOPPED > `readonly` **STOPPED**: `"stopped"` = `"stopped"` Server has stopped ### STOPPING > `readonly` **STOPPING**: `"stopping"` = `"stopping"` Server is stopping --- --- url: /en/api/@connectum/auth/proto/variables/service_auth.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [proto](../index.md) / service\_auth # Variable: service\_auth > `const` **service\_auth**: `GenExtension`<`ServiceOptions`, [`ServiceAuth`](../type-aliases/ServiceAuth.md)> Defined in: packages/auth/gen/connectum/auth/v1/options\_pb.d.ts:105 ## Generated from extension: optional connectum.auth.v1.ServiceAuth service\_auth = 50101; --- --- url: /en/api/@connectum/auth/proto/variables/ServiceAuthSchema.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [proto](../index.md) / ServiceAuthSchema # Variable: ServiceAuthSchema > `const` **ServiceAuthSchema**: `GenMessage`<[`ServiceAuth`](../type-aliases/ServiceAuth.md)> Defined in: packages/auth/gen/connectum/auth/v1/options\_pb.d.ts:97 Describes the message connectum.auth.v1.ServiceAuth. Use `create(ServiceAuthSchema)` to create a new message. --- --- url: /en/api/@connectum/healthcheck/variables/ServingStatus.md --- [Connectum API Reference](../../../index.md) / [@connectum/healthcheck](../index.md) / ServingStatus # Variable: ServingStatus > `const` **ServingStatus**: *typeof* `HealthCheckResponse_ServingStatus` = `HealthCheckResponse_ServingStatus` Defined in: [types.ts:14](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/healthcheck/src/types.ts#L14) Service serving status Re-export generated const from proto. --- --- url: /en/api/@connectum/auth/testing/variables/TEST_JWT_SECRET.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / TEST\_JWT\_SECRET # Variable: TEST\_JWT\_SECRET > `const` **TEST\_JWT\_SECRET**: `"connectum-test-secret-do-not-use-in-production"` = `"connectum-test-secret-do-not-use-in-production"` Defined in: [packages/auth/src/testing/test-jwt.ts:18](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/auth/src/testing/test-jwt.ts#L18) Deterministic test secret for HS256 JWTs. WARNING: This is a well-known secret for testing only. NEVER use in production. --- --- url: /en/api/@connectum/core/variables/tlsPath.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / tlsPath # Variable: tlsPath > `const` **tlsPath**: `string` Defined in: [packages/core/src/TLSConfig.ts:63](https://github.com/Connectum-Framework/connectum/blob/47e0b0ef40389913ccd23186e0f4d580f701e822/packages/core/src/TLSConfig.ts#L63) Exported for backward compatibility