--- 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) * [ClientBearerInterceptorOptions](interfaces/ClientBearerInterceptorOptions.md) * [ClientGatewayInterceptorOptions](interfaces/ClientGatewayInterceptorOptions.md) * [GatewayAuthInterceptorOptions](interfaces/GatewayAuthInterceptorOptions.md) * [GatewayHeaderMapping](interfaces/GatewayHeaderMapping.md) * [InternalAuthInterceptorOptions](interfaces/InternalAuthInterceptorOptions.md) * [JwtAuthInterceptorOptions](interfaces/JwtAuthInterceptorOptions.md) * [MeshIdentityEntry](interfaces/MeshIdentityEntry.md) * [MeshIdentityTrustOptions](interfaces/MeshIdentityTrustOptions.md) * [ProtoAuthzInterceptorOptions](interfaces/ProtoAuthzInterceptorOptions.md) * [ResolvedMethodAuth](interfaces/ResolvedMethodAuth.md) * [SessionAuthInterceptorOptions](interfaces/SessionAuthInterceptorOptions.md) * [SharedSecretTrustOptions](interfaces/SharedSecretTrustOptions.md) * [SignedTokenIssuer](interfaces/SignedTokenIssuer.md) * [SignedTokenTrustOptions](interfaces/SignedTokenTrustOptions.md) ## Type Aliases * [AuthzEffect](type-aliases/AuthzEffect.md) * [InterceptorFactory](type-aliases/InterceptorFactory.md) * [InternalTrustSource](type-aliases/InternalTrustSource.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) * [createClientBearerInterceptor](functions/createClientBearerInterceptor.md) * [createClientGatewayInterceptor](functions/createClientGatewayInterceptor.md) * [createGatewayAuthInterceptor](functions/createGatewayAuthInterceptor.md) * [createInternalAuthInterceptor](functions/createInternalAuthInterceptor.md) * [createJwtAuthInterceptor](functions/createJwtAuthInterceptor.md) * [createProtoAuthzInterceptor](functions/createProtoAuthzInterceptor.md) * [createSessionAuthInterceptor](functions/createSessionAuthInterceptor.md) * [getAuthContext](functions/getAuthContext.md) * [getInternalMethods](functions/getInternalMethods.md) * [getPublicMethods](functions/getPublicMethods.md) * [matchesMethodPattern](functions/matchesMethodPattern.md) * [meshIdentityTrust](functions/meshIdentityTrust.md) * [parseAuthHeaders](functions/parseAuthHeaders.md) * [requireAuthContext](functions/requireAuthContext.md) * [resolveMethodAuth](functions/resolveMethodAuth.md) * [setAuthHeaders](functions/setAuthHeaders.md) * [sharedSecretTrust](functions/sharedSecretTrust.md) * [signedTokenTrust](functions/signedTokenTrust.md) --- --- url: /en/packages/auth.md description: >- Authentication, authorization, trusted gateway and internal identity, auth context, and client credentials. --- # @connectum/auth Authentication, authorization, trusted gateway and internal identity, auth context, and client credentials. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/auth ``` \== pnpm ```bash pnpm add @connectum/auth ``` \== bun ```bash bun add @connectum/auth ``` ::: ## Start Here {#quick-start} ```typescript import { createJwtAuthInterceptor } from '@connectum/auth'; const jwtAuth = createJwtAuthInterceptor({ jwksUri: 'https://id.example.com/.well-known/jwks.json', issuer: 'https://id.example.com/', audience: 'orders-api', }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `createJwtAuthInterceptor` | Verify bearer tokens with JWKS, public keys, or a shared secret. | | `createAuthzInterceptor` | Apply explicit allow and deny rules. | | `requireAuthContext` | Read the verified identity in a handler. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/auth) * **Configure:** [Task and configuration guidance](/en/guide/auth/jwt) * **API reference:** [Exact options and symbols](/en/api/@connectum/auth/interfaces/JwtAuthInterceptorOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/auth/) * **Source:** [@connectum/auth on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/auth) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- 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: >- Command-line scaffolding, service generation, package-version reporting, and reflection-based proto synchronization. --- # @connectum/cli Command-line scaffolding, service generation, package-version reporting, and reflection-based proto synchronization. ## Install {#installation} ::: pm \== npm ```bash npm install -D @connectum/cli ``` \== pnpm ```bash pnpm add -D @connectum/cli ``` \== bun ```bash bun add -d @connectum/cli ``` ::: ## Start Here {#quick-start} ```bash npx connectum init payments cd payments npx connectum generate service invoices npx connectum --version ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `connectum init` | Create a service from the version-pinned official base. | | `connectum generate service` | Add a service contract and implementation. | | `connectum proto sync` | Discover and generate types from server reflection. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/scaffolding) * **Configure:** [Task and configuration guidance](/en/guide/protocols/reflection) * **API reference:** [Exact options and symbols](/en/api/@connectum/cli/commands/proto-sync/interfaces/ProtoSyncOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/cli/) * **Source:** [@connectum/cli on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/cli) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/core.md --- [Connectum API Reference](../../index.md) / @connectum/core # @connectum/core ## Modules * [config](config/index.md) * [types](types/index.md) ## Classes * [CatalogConfigError](classes/CatalogConfigError.md) * [TransportValidationError](classes/TransportValidationError.md) ## Interfaces * [BidiStreamHandle](interfaces/BidiStreamHandle.md) * [CatalogClient](interfaces/CatalogClient.md) * [ClientStreamHandle](interfaces/ClientStreamHandle.md) * [ConnectumCallMap](interfaces/ConnectumCallMap.md) * [ConnectumStreamMap](interfaces/ConnectumStreamMap.md) * [Context](interfaces/Context.md) * [CreateCatalogClientOptions](interfaces/CreateCatalogClientOptions.md) * [CreateLocalTransportOptions](interfaces/CreateLocalTransportOptions.md) * [DnsResolverOptions](interfaces/DnsResolverOptions.md) * [PerServiceEnvResolverOptions](interfaces/PerServiceEnvResolverOptions.md) * [ResolverContext](interfaces/ResolverContext.md) * [SanitizableError](interfaces/SanitizableError.md) * [ServiceDefinition](interfaces/ServiceDefinition.md) * [StreamingMethodInfo](interfaces/StreamingMethodInfo.md) ## Type Aliases * [CallOptions](type-aliases/CallOptions.md) * [CatalogCall](type-aliases/CatalogCall.md) * [CatalogStream](type-aliases/CatalogStream.md) * [ConnectumEnv](type-aliases/ConnectumEnv.md) * [ConnectumMethodImpl](type-aliases/ConnectumMethodImpl.md) * [ConnectumServiceImpl](type-aliases/ConnectumServiceImpl.md) * [EffectiveTransport](type-aliases/EffectiveTransport.md) * [RemoteResolver](type-aliases/RemoteResolver.md) * [ServiceCatalog](type-aliases/ServiceCatalog.md) * [ServiceOptions](type-aliases/ServiceOptions.md) * [StreamReturn](type-aliases/StreamReturn.md) * [TransportValidationMode](type-aliases/TransportValidationMode.md) ## Variables * [BooleanFromStringSchema](variables/BooleanFromStringSchema.md) * [ConnectumEnvSchema](variables/ConnectumEnvSchema.md) * [defaultPropagateHeaders](variables/defaultPropagateHeaders.md) * [EffectiveTransport](variables/EffectiveTransport.md) * [LogFormatSchema](variables/LogFormatSchema.md) * [LoggerBackendSchema](variables/LoggerBackendSchema.md) * [LogLevelSchema](variables/LogLevelSchema.md) * [NodeEnvSchema](variables/NodeEnvSchema.md) * [tlsPath](variables/tlsPath.md) * [TRANSPORT\_VALIDATION\_ERROR\_CODE](variables/TRANSPORT_VALIDATION_ERROR_CODE.md) * [TransportValidationMode](variables/TransportValidationMode.md) ## Functions * [collectStreamingMethods](functions/collectStreamingMethods.md) * [createCatalogClient](functions/createCatalogClient.md) * [createLocalTransport](functions/createLocalTransport.md) * [createServer](functions/createServer.md) * [defineCatalog](functions/defineCatalog.md) * [defineLazyService](functions/defineLazyService.md) * [defineService](functions/defineService.md) * [dnsResolver](functions/dnsResolver.md) * [getTLSPath](functions/getTLSPath.md) * [isSanitizableError](functions/isSanitizableError.md) * [mapResolver](functions/mapResolver.md) * [matchServicesPattern](functions/matchServicesPattern.md) * [mergeCatalogs](functions/mergeCatalogs.md) * [mergeEnabledServices](functions/mergeEnabledServices.md) * [parseEnvConfig](functions/parseEnvConfig.md) * [parseServicesEnv](functions/parseServicesEnv.md) * [perServiceEnvResolver](functions/perServiceEnvResolver.md) * [readTLSCertificates](functions/readTLSCertificates.md) * [resolveEffectiveTransport](functions/resolveEffectiveTransport.md) * [safeParseEnvConfig](functions/safeParseEnvConfig.md) * [singleTransportResolver](functions/singleTransportResolver.md) ## References ### CreateServerOptions Re-exports [CreateServerOptions](types/interfaces/CreateServerOptions.md) *** ### EventBusLike Re-exports [EventBusLike](types/interfaces/EventBusLike.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) *** ### ServerClientOptions Re-exports [ServerClientOptions](types/interfaces/ServerClientOptions.md) *** ### ServerState Re-exports [ServerState](types/variables/ServerState.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: >- Server foundation for registration, lifecycle, transport, configuration, TLS, and typed service calls. --- # @connectum/core Server foundation for registration, lifecycle, transport, configuration, TLS, and typed service calls. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/core ``` \== pnpm ```bash pnpm add @connectum/core ``` \== bun ```bash bun add @connectum/core ``` ::: ## Start Here {#quick-start} ```typescript import { createServer } from '@connectum/core'; const server = createServer({ services: [greeterService], port: 5000, shutdown: { autoShutdown: true }, }); await server.start(); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `createServer` | Compose services, protocols, interceptors, and shutdown policy. | | `defineService` | Bind a generated service descriptor to typed handlers. | | `createCatalogClient` | Call catalog services outside a server handler. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/server) * **Configure:** [Task and configuration guidance](/en/guide/server/configuration) * **API reference:** [Exact options and symbols](/en/api/@connectum/core/types/interfaces/CreateServerOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/core/) * **Source:** [@connectum/core on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/core) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/events.md --- [Connectum API Reference](../../index.md) / @connectum/events # @connectum/events ## Modules * [types](types/index.md) ## Classes * [EventRouterImpl](classes/EventRouterImpl.md) * [NonRetryableError](classes/NonRetryableError.md) * [RetryableError](classes/RetryableError.md) ## Interfaces * [BroadcastReactor](interfaces/BroadcastReactor.md) * [BroadcastSubscribersOptions](interfaces/BroadcastSubscribersOptions.md) ## Functions * [composeMiddleware](functions/composeMiddleware.md) * [createBroadcastSubscribers](functions/createBroadcastSubscribers.md) * [createEventBus](functions/createEventBus.md) * [createEventContext](functions/createEventContext.md) * [deriveServiceName](functions/deriveServiceName.md) * [dlqMiddleware](functions/dlqMiddleware.md) * [matchPattern](functions/matchPattern.md) * [MemoryAdapter](functions/MemoryAdapter.md) * [resolveTopicName](functions/resolveTopicName.md) * [retryMiddleware](functions/retryMiddleware.md) ## References ### AdapterContext Re-exports [AdapterContext](types/interfaces/AdapterContext.md) *** ### DlqOptions Re-exports [DlqOptions](types/interfaces/DlqOptions.md) *** ### EventAdapter Re-exports [EventAdapter](types/interfaces/EventAdapter.md) *** ### EventAdapterFactory Re-exports [EventAdapterFactory](types/type-aliases/EventAdapterFactory.md) *** ### EventBus Re-exports [EventBus](types/interfaces/EventBus.md) *** ### EventBusOptions Re-exports [EventBusOptions](types/interfaces/EventBusOptions.md) *** ### EventContext Re-exports [EventContext](types/interfaces/EventContext.md) *** ### EventContextInit Re-exports [EventContextInit](types/interfaces/EventContextInit.md) *** ### EventHandlerConfig Re-exports [EventHandlerConfig](types/interfaces/EventHandlerConfig.md) *** ### EventMiddleware Re-exports [EventMiddleware](types/type-aliases/EventMiddleware.md) *** ### EventMiddlewareNext Re-exports [EventMiddlewareNext](types/type-aliases/EventMiddlewareNext.md) *** ### EventRoute Re-exports [EventRoute](types/type-aliases/EventRoute.md) *** ### EventRouteEntry Re-exports [EventRouteEntry](types/interfaces/EventRouteEntry.md) *** ### EventRouter Re-exports [EventRouter](types/interfaces/EventRouter.md) *** ### EventSubscription Re-exports [EventSubscription](types/interfaces/EventSubscription.md) *** ### MiddlewareConfig Re-exports [MiddlewareConfig](types/interfaces/MiddlewareConfig.md) *** ### PublishOptions Re-exports [PublishOptions](types/interfaces/PublishOptions.md) *** ### RawEvent Re-exports [RawEvent](types/interfaces/RawEvent.md) *** ### RawEventHandler Re-exports [RawEventHandler](types/type-aliases/RawEventHandler.md) *** ### RawSubscribeOptions Re-exports [RawSubscribeOptions](types/interfaces/RawSubscribeOptions.md) *** ### RetryOptions Re-exports [RetryOptions](types/interfaces/RetryOptions.md) *** ### ServiceEventHandlers Re-exports [ServiceEventHandlers](types/type-aliases/ServiceEventHandlers.md) *** ### TypedEventHandler Re-exports [TypedEventHandler](types/type-aliases/TypedEventHandler.md) --- --- url: /en/packages/events.md description: >- Proto-first publish/subscribe with routes, middleware, retries, dead letters, and pluggable adapters. --- # @connectum/events Proto-first publish/subscribe with routes, middleware, retries, dead letters, and pluggable adapters. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/events ``` \== pnpm ```bash pnpm add @connectum/events ``` \== bun ```bash bun add @connectum/events ``` ::: ## Start Here {#quick-start} ```typescript import { createEventBus, MemoryAdapter } from '@connectum/events'; const eventBus = createEventBus({ adapter: MemoryAdapter(), routes: [orderEvents], group: 'orders-service', }); await eventBus.start(); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `createEventBus` | Create the lifecycle-managed event bus. | | `MemoryAdapter` | Run deterministic in-memory event tests. | | `createBroadcastSubscribers` | Create explicit one-to-many reactor subscribers. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/events) * **Configure:** [Task and configuration guidance](/en/guide/events/getting-started) * **API reference:** [Exact options and symbols](/en/api/@connectum/events/types/interfaces/EventBusOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/events/) * **Source:** [@connectum/events on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/events) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/events-amqp.md --- [Connectum API Reference](../../index.md) / @connectum/events-amqp # @connectum/events-amqp ## Modules * [testing](testing/index.md) * [types](types/index.md) ## Classes * [AmqpAdapterError](classes/AmqpAdapterError.md) * [AmqpConnectionError](classes/AmqpConnectionError.md) * [AmqpPublishNackError](classes/AmqpPublishNackError.md) * [AmqpPublishTimeoutError](classes/AmqpPublishTimeoutError.md) * [AmqpSerializationError](classes/AmqpSerializationError.md) * [AmqpTopologyError](classes/AmqpTopologyError.md) * [AmqpUnroutableError](classes/AmqpUnroutableError.md) ## Type Aliases * [AmqpTopologyObject](type-aliases/AmqpTopologyObject.md) ## Functions * [AmqpAdapter](functions/AmqpAdapter.md) * [isAutoRetriablePublishError](functions/isAutoRetriablePublishError.md) * [toAmqpPattern](functions/toAmqpPattern.md) ## References ### AmqpAdapterOptions Re-exports [AmqpAdapterOptions](types/interfaces/AmqpAdapterOptions.md) *** ### AmqpBindingDeclaration Re-exports [AmqpBindingDeclaration](types/interfaces/AmqpBindingDeclaration.md) *** ### AmqpConsumerOptions Re-exports [AmqpConsumerOptions](types/interfaces/AmqpConsumerOptions.md) *** ### AmqpExchangeDeclaration Re-exports [AmqpExchangeDeclaration](types/interfaces/AmqpExchangeDeclaration.md) *** ### AmqpExchangeOptions Re-exports [AmqpExchangeOptions](types/interfaces/AmqpExchangeOptions.md) *** ### AmqpLifecycleCallbacks Re-exports [AmqpLifecycleCallbacks](types/interfaces/AmqpLifecycleCallbacks.md) *** ### AmqpLifecycleEvent Re-exports [AmqpLifecycleEvent](types/type-aliases/AmqpLifecycleEvent.md) *** ### AmqpPublisherOptions Re-exports [AmqpPublisherOptions](types/interfaces/AmqpPublisherOptions.md) *** ### AmqpPublishRetryOptions Re-exports [AmqpPublishRetryOptions](types/interfaces/AmqpPublishRetryOptions.md) *** ### AmqpQueueDeclaration Re-exports [AmqpQueueDeclaration](types/interfaces/AmqpQueueDeclaration.md) *** ### AmqpQueueOptions Re-exports [AmqpQueueOptions](types/interfaces/AmqpQueueOptions.md) *** ### AmqpQueueOverride Re-exports [AmqpQueueOverride](types/interfaces/AmqpQueueOverride.md) *** ### AmqpRecoveryOptions Re-exports [AmqpRecoveryOptions](types/interfaces/AmqpRecoveryOptions.md) *** ### AmqpSerializationOptions Re-exports [AmqpSerializationOptions](types/interfaces/AmqpSerializationOptions.md) *** ### AmqpTopology Re-exports [AmqpTopology](types/interfaces/AmqpTopology.md) *** ### AmqpTopologyMode Re-exports [AmqpTopologyMode](types/variables/AmqpTopologyMode.md) --- --- url: /en/packages/events-amqp.md description: >- AMQP and RabbitMQ adapter with topology, confirms, recovery, and external-contract publishing. --- # @connectum/events-amqp AMQP and RabbitMQ adapter with topology, confirms, recovery, and external-contract publishing. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/events-amqp ``` \== pnpm ```bash pnpm add @connectum/events-amqp ``` \== bun ```bash bun add @connectum/events-amqp ``` ::: ## Start Here {#quick-start} ```typescript import { AmqpAdapter } from '@connectum/events-amqp'; const adapter = AmqpAdapter({ url: 'amqp://localhost:5672', exchange: 'events', exchangeType: 'topic', }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `AmqpAdapter` | Connect EventBus to an AMQP broker. | | `AmqpAdapterOptions` | Configure connection, topology, publishing, and recovery. | | `isAutoRetriablePublishError` | Classify adapter failures for retry policy. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/events/adapters) * **Configure:** [Task and configuration guidance](/en/guide/events/adapters) * **API reference:** [Exact options and symbols](/en/api/@connectum/events-amqp/types/interfaces/AmqpAdapterOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/events-amqp/) * **Source:** [@connectum/events-amqp on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/events-amqp) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/events-kafka.md --- [Connectum API Reference](../../index.md) / @connectum/events-kafka # @connectum/events-kafka ## Modules * [types](types/index.md) ## Functions * [KafkaAdapter](functions/KafkaAdapter.md) ## References ### KafkaAdapterOptions Re-exports [KafkaAdapterOptions](types/interfaces/KafkaAdapterOptions.md) --- --- url: /en/packages/events-kafka.md description: >- Kafka and Redpanda adapter with consumer groups, topic patterns, and message headers. --- # @connectum/events-kafka Kafka and Redpanda adapter with consumer groups, topic patterns, and message headers. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/events-kafka ``` \== pnpm ```bash pnpm add @connectum/events-kafka ``` \== bun ```bash bun add @connectum/events-kafka ``` ::: ## Start Here {#quick-start} ```typescript import { KafkaAdapter } from '@connectum/events-kafka'; const adapter = KafkaAdapter({ brokers: ['localhost:9092'], clientId: 'orders-service', }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `KafkaAdapter` | Connect EventBus to Kafka or Redpanda. | | `KafkaAdapterOptions` | Configure brokers, client, producer, and consumer behavior. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/events/adapters) * **Configure:** [Task and configuration guidance](/en/guide/events/adapters) * **API reference:** [Exact options and symbols](/en/api/@connectum/events-kafka/types/interfaces/KafkaAdapterOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/events-kafka/) * **Source:** [@connectum/events-kafka on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/events-kafka) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/events-nats.md --- [Connectum API Reference](../../index.md) / @connectum/events-nats # @connectum/events-nats ## Modules * [types](types/index.md) ## Functions * [NatsAdapter](functions/NatsAdapter.md) ## References ### NatsAdapterOptions Re-exports [NatsAdapterOptions](types/interfaces/NatsAdapterOptions.md) *** ### NatsConsumerOptions Re-exports [NatsConsumerOptions](types/interfaces/NatsConsumerOptions.md) --- --- url: /en/packages/events-nats.md description: >- NATS JetStream adapter with durable consumers, subjects, metadata, and at-least-once delivery. --- # @connectum/events-nats NATS JetStream adapter with durable consumers, subjects, metadata, and at-least-once delivery. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/events-nats ``` \== pnpm ```bash pnpm add @connectum/events-nats ``` \== bun ```bash bun add @connectum/events-nats ``` ::: ## Start Here {#quick-start} ```typescript import { NatsAdapter } from '@connectum/events-nats'; const adapter = NatsAdapter({ servers: 'nats://localhost:4222', }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `NatsAdapter` | Connect EventBus to NATS JetStream. | | `NatsAdapterOptions` | Configure servers, stream, connection, and consumer behavior. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/events/adapters) * **Configure:** [Task and configuration guidance](/en/guide/events/adapters) * **API reference:** [Exact options and symbols](/en/api/@connectum/events-nats/types/interfaces/NatsAdapterOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/events-nats/) * **Source:** [@connectum/events-nats on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/events-nats) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/events-redis.md --- [Connectum API Reference](../../index.md) / @connectum/events-redis # @connectum/events-redis ## Modules * [types](types/index.md) ## Functions * [RedisAdapter](functions/RedisAdapter.md) ## References ### RedisAdapterOptions Re-exports [RedisAdapterOptions](types/interfaces/RedisAdapterOptions.md) *** ### RedisBrokerOptions Re-exports [RedisBrokerOptions](types/interfaces/RedisBrokerOptions.md) --- --- url: /en/packages/events-redis.md description: >- Redis Streams and Valkey adapter with consumer groups and explicit stream controls. --- # @connectum/events-redis Redis Streams and Valkey adapter with consumer groups and explicit stream controls. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/events-redis ``` \== pnpm ```bash pnpm add @connectum/events-redis ``` \== bun ```bash bun add @connectum/events-redis ``` ::: ## Start Here {#quick-start} ```typescript import { RedisAdapter } from '@connectum/events-redis'; const adapter = RedisAdapter({ url: 'redis://localhost:6379', }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `RedisAdapter` | Connect EventBus to Redis Streams or Valkey. | | `RedisAdapterOptions` | Configure connection, broker, and stream behavior. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/events/adapters) * **Configure:** [Task and configuration guidance](/en/guide/events/adapters) * **API reference:** [Exact options and symbols](/en/api/@connectum/events-redis/types/interfaces/RedisAdapterOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/events-redis/) * **Source:** [@connectum/events-redis on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/events-redis) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/healthcheck/@connectum/healthcheck.md --- [Connectum API Reference](../../../../index.md) / [@connectum/healthcheck](../../index.md) / @connectum/healthcheck # @connectum/healthcheck ## Classes * [HealthcheckManager](classes/HealthcheckManager.md) ## Variables * [healthcheckManager](variables/healthcheckManager.md) ## Functions * [createHealthcheckManager](functions/createHealthcheckManager.md) * [createHttpHealthHandler](functions/createHttpHealthHandler.md) * [Healthcheck](functions/Healthcheck.md) * [parseServiceFromUrl](functions/parseServiceFromUrl.md) ## References ### HealthcheckOptions Re-exports [HealthcheckOptions](types/interfaces/HealthcheckOptions.md) *** ### ServiceStatus Re-exports [ServiceStatus](types/interfaces/ServiceStatus.md) *** ### ServingStatus Re-exports [ServingStatus](types/variables/ServingStatus.md) --- --- url: /en/api/@connectum/healthcheck.md --- [Connectum API Reference](../../index.md) / @connectum/healthcheck # @connectum/healthcheck ## Modules * [@connectum/healthcheck](@connectum/healthcheck/index.md) * [@connectum/healthcheck/types](@connectum/healthcheck/types/index.md) --- --- url: /en/packages/healthcheck.md description: >- gRPC health protocol, HTTP health/readiness endpoints, and mutable serving state. --- # @connectum/healthcheck gRPC health protocol, HTTP health/readiness endpoints, and mutable serving state. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/healthcheck ``` \== pnpm ```bash pnpm add @connectum/healthcheck ``` \== bun ```bash bun add @connectum/healthcheck ``` ::: ## Start Here {#quick-start} ```typescript import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; const protocols = [Healthcheck({ httpEnabled: true })]; healthcheckManager.update(ServingStatus.SERVING); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `Healthcheck` | Register gRPC health and optional HTTP endpoints. | | `healthcheckManager` | Update overall or per-service serving state. | | `ServingStatus` | Use protocol-defined readiness values. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/health-checks) * **Configure:** [Task and configuration guidance](/en/guide/health-checks/kubernetes) * **API reference:** [Exact options and symbols](/en/api/@connectum/healthcheck/@connectum/healthcheck/types/interfaces/HealthcheckOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/healthcheck/) * **Source:** [@connectum/healthcheck on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/healthcheck) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/healthcheck/@connectum/healthcheck/types.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/healthcheck](../../../index.md) / @connectum/healthcheck/types # @connectum/healthcheck/types Healthcheck protocol types ## Interfaces * [HealthcheckOptions](interfaces/HealthcheckOptions.md) * [ServiceStatus](interfaces/ServiceStatus.md) ## Type Aliases * [ServingStatus](type-aliases/ServingStatus.md) ## Variables * [ServingStatus](variables/ServingStatus.md) --- --- 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) *** ### defaultFailurePredicate Re-exports [defaultFailurePredicate](circuit-breaker/functions/defaultFailurePredicate.md) *** ### DefaultInterceptorOptions Re-exports [DefaultInterceptorOptions](defaults/interfaces/DefaultInterceptorOptions.md) --- --- url: /en/packages/interceptors.md description: >- ConnectRPC middleware for errors, validation, timeouts, resilience, logging, serialization, and method routing. --- # @connectum/interceptors ConnectRPC middleware for errors, validation, timeouts, resilience, logging, serialization, and method routing. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/interceptors ``` \== pnpm ```bash pnpm add @connectum/interceptors ``` \== bun ```bash bun add @connectum/interceptors ``` ::: ## Start Here {#quick-start} ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; const interceptors = createDefaultInterceptors({ timeout: { duration: 10_000 }, validation: true, }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `createDefaultInterceptors` | Build the ordered default chain; resilience remains opt-in. | | `createMethodFilterInterceptor` | Apply an interceptor to selected methods. | | `createErrorHandlerInterceptor` | Normalize and sanitize handler failures. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/interceptors) * **Configure:** [Task and configuration guidance](/en/guide/interceptors/built-in) * **API reference:** [Exact options and symbols](/en/api/@connectum/interceptors/defaults/interfaces/DefaultInterceptorOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/interceptors/) * **Source:** [@connectum/interceptors on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/interceptors) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- 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\_CONNECTUM\_TRANSPORT Re-exports [ATTR\_CONNECTUM\_TRANSPORT](attributes/variables/ATTR_CONNECTUM_TRANSPORT.md) *** ### ATTR\_CONNECTUM\_TRANSPORT\_METRIC Re-exports [ATTR\_CONNECTUM\_TRANSPORT\_METRIC](attributes/variables/ATTR_CONNECTUM_TRANSPORT_METRIC.md) *** ### 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\_MESSAGE\_ID Re-exports [ATTR\_RPC\_MESSAGE\_ID](attributes/variables/ATTR_RPC_MESSAGE_ID.md) *** ### ATTR\_RPC\_MESSAGE\_TYPE Re-exports [ATTR\_RPC\_MESSAGE\_TYPE](attributes/variables/ATTR_RPC_MESSAGE_TYPE.md) *** ### ATTR\_RPC\_MESSAGE\_UNCOMPRESSED\_SIZE Re-exports [ATTR\_RPC\_MESSAGE\_UNCOMPRESSED\_SIZE](attributes/variables/ATTR_RPC_MESSAGE_UNCOMPRESSED_SIZE.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) *** ### CONNECTUM\_INTERNAL\_TRANSPORT\_HEADER Re-exports [CONNECTUM\_INTERNAL\_TRANSPORT\_HEADER](attributes/variables/CONNECTUM_INTERNAL_TRANSPORT_HEADER.md) *** ### CONNECTUM\_INTERNAL\_TRANSPORT\_IN\_PROCESS Re-exports [CONNECTUM\_INTERNAL\_TRANSPORT\_IN\_PROCESS](attributes/variables/CONNECTUM_INTERNAL_TRANSPORT_IN_PROCESS.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) *** ### detectConnectumTransport Re-exports [detectConnectumTransport](shared/functions/detectConnectumTransport.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\_MESSAGE\_EVENT Re-exports [RPC\_MESSAGE\_EVENT](attributes/variables/RPC_MESSAGE_EVENT.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 providers, server/client RPC instrumentation, metrics, logging, and deep tracing helpers. --- # @connectum/otel OpenTelemetry providers, server/client RPC instrumentation, metrics, logging, and deep tracing helpers. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/otel ``` \== pnpm ```bash pnpm add @connectum/otel ``` \== bun ```bash bun add @connectum/otel ``` ::: ## Start Here {#quick-start} ```typescript import { createOtelInterceptor, initProvider } from '@connectum/otel'; initProvider({ serviceName: 'orders-service' }); const interceptor = createOtelInterceptor({ serverPort: 5000 }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `initProvider` | Initialize trace, metric, and log providers. | | `createOtelInterceptor` | Instrument inbound RPCs. | | `createOtelClientInterceptor` | Instrument outbound RPCs and propagate context. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/observability) * **Configure:** [Task and configuration guidance](/en/guide/observability/backends) * **API reference:** [Exact options and symbols](/en/api/@connectum/otel/interfaces/OtelInterceptorOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/otel/) * **Source:** [@connectum/otel on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/otel) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/protoc-gen-catalog.md --- [Connectum API Reference](../../index.md) / @connectum/protoc-gen-catalog # @connectum/protoc-gen-catalog protoc-gen-connectum-catalog — executable entry point. Buf/protoc invoke this binary, passing a `CodeGeneratorRequest` on stdin and reading a `CodeGeneratorResponse` from stdout. The plugin itself is exported from `./plugin.ts` for programmatic use and testing. --- --- url: /en/packages/protoc-gen-catalog.md description: >- Buf/protoc plugin that generates typed Connectum service-catalog call and stream maps. --- # @connectum/protoc-gen-catalog Buf/protoc plugin that generates typed Connectum service-catalog call and stream maps. ## Install {#installation} ::: pm \== npm ```bash npm install -D @connectum/protoc-gen-catalog ``` \== pnpm ```bash pnpm add -D @connectum/protoc-gen-catalog ``` \== bun ```bash bun add -d @connectum/protoc-gen-catalog ``` ::: ## Start Here {#quick-start} ```yaml plugins: - local: protoc-gen-connectum-catalog strategy: all out: gen opt: - import_extension=.ts ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `protoc-gen-connectum-catalog` | Generate catalog descriptors and TypeScript module augmentation. | | `protocGenCatalog` | Plugin definition exported for tooling integration. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/service-communication/service-catalog) * **Configure:** [Task and configuration guidance](/en/guide/service-communication/service-catalog) * **API reference:** [Exact options and symbols](/en/api/@connectum/protoc-gen-catalog/) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/protoc-gen-catalog/) * **Source:** [@connectum/protoc-gen-catalog on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/protoc-gen-catalog) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- 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 v1 and v1alpha for discovery and tooling. --- # @connectum/reflection gRPC Server Reflection v1 and v1alpha for discovery and tooling. ## Install {#installation} ::: pm \== npm ```bash npm install @connectum/reflection ``` \== pnpm ```bash pnpm add @connectum/reflection ``` \== bun ```bash bun add @connectum/reflection ``` ::: ## Start Here {#quick-start} ```typescript import { Reflection } from '@connectum/reflection'; const server = createServer({ services: [greeterService], protocols: [Reflection()], }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `Reflection` | Register reflection as a server protocol. | | `collectFileProtos` | Collect transitive file descriptors for reflection. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/protocols/reflection) * **Configure:** [Task and configuration guidance](/en/guide/protocols/reflection) * **API reference:** [Exact options and symbols](/en/api/@connectum/reflection/functions/Reflection) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/reflection/) * **Source:** [@connectum/reflection on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/reflection) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/test-fixtures.md --- [Connectum API Reference](../../index.md) / @connectum/test-fixtures # @connectum/test-fixtures ## Modules * [index](index/index.md) * [types](types/index.md) --- --- url: /en/packages/test-fixtures.md description: >- Low-level mock requests, descriptors, streams, next functions, and assertions for framework and tooling tests. --- # @connectum/test-fixtures Low-level mock requests, descriptors, streams, next functions, and assertions for framework and tooling tests. ## Install {#installation} ::: pm \== npm ```bash npm install -D @connectum/test-fixtures ``` \== pnpm ```bash pnpm add -D @connectum/test-fixtures ``` \== bun ```bash bun add -d @connectum/test-fixtures ``` ::: ## Start Here {#quick-start} ```typescript import { createMockNext, createMockRequest } from '@connectum/test-fixtures'; const request = createMockRequest(); const next = createMockNext({ message: { ok: true } }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `createMockRequest` | Build a ConnectRPC request fixture. | | `createFakeService` | Build descriptor-compatible fake services. | | `createMockStream` | Create deterministic streaming inputs. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/testing) * **Configure:** [Task and configuration guidance](/en/guide/interceptors/custom) * **API reference:** [Exact options and symbols](/en/api/@connectum/test-fixtures/types/interfaces/MockRequestOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/test-fixtures/) * **Source:** [@connectum/test-fixtures on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/test-fixtures) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- url: /en/api/@connectum/testing.md --- [Connectum API Reference](../../index.md) / @connectum/testing # @connectum/testing ## Modules * [index](index/index.md) * [types](types/index.md) --- --- url: /en/packages/testing.md description: >- Supported service-test helpers, local clients, parity scenarios, in-memory telemetry collectors, and assertions. --- # @connectum/testing Supported service-test helpers, local clients, parity scenarios, in-memory telemetry collectors, and assertions. ## Install {#installation} ::: pm \== npm ```bash npm install -D @connectum/testing ``` \== pnpm ```bash pnpm add -D @connectum/testing ``` \== bun ```bash bun add -d @connectum/testing ``` ::: ## Start Here {#quick-start} ```typescript import { createClient } from '@connectrpc/connect'; import { withTestServer } from '@connectum/testing'; await withTestServer({ services: [greeterService] }, async (server) => { const client = createClient(GreeterService, server.transport); const response = await client.sayHello({ name: 'Ada' }); }); ``` For a complete, source-verified workflow, continue with the focused guide below. ## Key Entry Points | Entry point | Use it to | |---|---| | `createTestServer` | Create an isolated Connectum server for a test. | | `withTestServer` | Manage test-server setup and teardown. | | `createLocalClient` | Exercise handlers through the in-process transport. | Runtime boundaries and extension seams remain in [Connectum Runtime Architecture](/en/guide/production/architecture). ## Learn / Configure / API Reference {#api-reference} * **Learn:** [Focused guide](/en/guide/testing) * **Configure:** [Task and configuration guidance](/en/guide/testing) * **API reference:** [Exact options and symbols](/en/api/@connectum/testing/types/interfaces/CreateTestServerOptions) * **Package API index:** [Generated TypeDoc](/en/api/@connectum/testing/) * **Source:** [@connectum/testing on GitHub](https://github.com/Connectum-Framework/connectum/tree/main/packages/testing) ## Related Modules {#related-packages} [Compare all Connectum packages](/en/packages/) by capability. --- --- 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) > **Update**: The consumer Node.js floor referenced below as `>=18.0.0` was later raised to **`>=22.13.0`** (Node.js 20 reached end-of-life on 2026-04-30). See the [migration guide](/en/migration/) ("Minimum Node.js raised to 22.13.0"). The historical decision body is preserved unchanged. ## 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 ├── events/ # Layer 1: EventBus core, router, middleware, MemoryAdapter ├── cli/ # Layer 2: CLI tooling ├── otel/ # Layer 2: OpenTelemetry instrumentation ├── testing/ # Layer 2: Testing utilities ├── events-nats/ # Layer 2: NATS JetStream adapter ├── events-kafka/ # Layer 2: Kafka/Redpanda adapter └── events-redis/ # Layer 2: Redis Streams adapter ``` ### 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 | | 2026-03-07 | Claude | Added @connectum/events (Layer 1), @connectum/events-nats, @connectum/events-kafka, @connectum/events-redis (Layer 2): EventBus with pluggable broker adapters (ADR-026). Package count: 8 -> 12 | --- --- 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 class validation accent class handler positive ``` ### 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)): ```mermaid flowchart LR Error["1 · errorHandler"] --> Timeout["2 · timeout"] Timeout --> Bulkhead["3 · bulkhead"] Bulkhead --> Breaker["4 · circuitBreaker"] Breaker --> Retry["5 · retry"] Retry --> Fallback["6 · fallback"] Fallback --> Validation["7 · validation"] Validation --> Serializer["8 · serializer"] ``` **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-023](/en/contributing/adr/023-uniform-registration-api), 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 ```mermaid flowchart TD Request --> Validation Validation -->|invalid| Invalid[Reject · 400 Invalid Argument] Validation -->|valid| Timeout Timeout -->|timeout| Deadline[Reject · 504 Deadline Exceeded] Timeout -->|in time| Breaker[Circuit Breaker] Breaker -->|open| Unavailable[Reject · 503 Unavailable] Breaker -->|closed| Bulkhead Bulkhead -->|exhausted| Exhausted[Reject · 503 Resource Exhausted] Bulkhead -->|available| Security[Security / Redact] Security --> Error[Error Handler] Error --> Observability Observability --> Retry Retry -->|ResourceExhausted| Wait Wait --> Retry 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: ```mermaid flowchart LR Proto["proto/*.proto"] --> Protoc["protoc + protoc-gen-es"] Protoc --> GeneratedTs["gen-ts/*.ts"] GeneratedTs --> Tsc[tsc] Tsc --> GeneratedJs["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)): ```mermaid flowchart LR Error[Error Handler] --> Validation Validation --> Serializer Serializer --> Logger Logger --> Tracing Tracing --> Redact Redact --> Custom[Custom Interceptors] Custom --> 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): ```mermaid flowchart TD Request["user.v1.UserService/GetUser"] Request --> Global["* → logRequest"] Global --> Service["user.v1.UserService/* → service interceptors, if defined"] Service --> Exact["user.v1.UserService/GetUser → checkAuth, enrichUser"] Exact --> Next["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 > **Note**: The `ServiceRoute` type referenced in this ADR was later removed; service registration is now described by the catalog model. See [ADR-028](/en/contributing/adr/028-service-catalog). The historical decision body is preserved unchanged. ## 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: protocol packages" HC["@connectum/healthcheck
Healthcheck()"] Ref["@connectum/reflection
Reflection()"] end subgraph "Layer 1: @connectum/interceptors" Defaults["defaults.ts
createDefaultInterceptors()"] Factories["errorHandler, timeout,
bulkhead, circuitBreaker,
retry, validation, serializer"] end Server -->|"defines interface
ProtocolRegistration"| Types HC -->|"implements"| Types Ref -->|"implements"| Types Defaults --> Factories class Server,Types accent ``` > **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: | Surface | Responsibility | |---|---| | `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:** ```mermaid flowchart TD Resolve["1 · Resolve proto options"] --> Public{"2 · public?"} Public -->|yes| Allow[Allow without authn] Public -->|no| Context["3 · Read auth context lazily"] Context --> Requires{"4 · requires?"} Requires -->|yes, no context| Unauthenticated[Throw Unauthenticated] Requires -->|yes, context| Check[Check roles and scopes] Requires -->|no| Policy{"5–6 · policy?"} Check --> Policy Policy -->|allow| Allow Policy -->|deny| Deny[Deny] Policy -->|unset| Rules["7 · Evaluate programmatic rules"] Rules --> Callback["8 · authorize callback"] Callback --> Default["9 · Apply defaultPolicy"] ``` **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**: ```mermaid flowchart LR Error[errorHandler] --> Authn[AUTH] Authn --> Authz[AUTHZ] Authz --> Timeout[timeout] Timeout --> Bulkhead[bulkhead] Bulkhead --> Breaker[circuitBreaker] Breaker --> Retry[retry] Retry --> Fallback[fallback] Fallback --> Validation[validation] Validation --> Serializer[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. > **Update (1.0.0):** `@connectum/otel` is now shipped, and `createOtelInterceptor()` is a standard interceptor. Place observability interceptors **after** auth/authz (and before validation) — i.e. `errorHandler → auth → authz → otel → validation` — so `getAuthContext()` is already populated when the span is recorded. Putting OTel before auth means spans cannot carry `enduser.*` attributes. This keeps the §4 rule intact (auth/authz immediately after `errorHandler`, before validation). ### 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 12 `@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 13 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 13 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"] Events["@connectum/events"] Testing["@connectum/testing
(private)"] end subgraph "Layer 2: Tools" Otel["@connectum/otel
(no @connectum deps)"] CLI["@connectum/cli
(devDeps only)"] EventsNats["@connectum/events-nats"] EventsKafka["@connectum/events-kafka"] EventsRedis["@connectum/events-redis"] end Interceptors -- "dependency" --> Core Healthcheck -. "peerDependency" .-> Core Reflection -. "peerDependency" .-> Core Auth -- "dependency" --> Core Events -- "dependency" --> Core Testing -- "dependency" --> Core EventsNats -- "dependency" --> Events EventsKafka -- "dependency" --> Events EventsRedis -- "dependency" --> Events class Core accent class Testing muted ``` ### The Problem With the current fixed strategy, **all** 13 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/events` | Optional EventBus; consumers may use external message brokers directly | | `@connectum/events-nats` | NATS adapter for EventBus; depends on `@connectum/events` | | `@connectum/events-kafka` | Kafka adapter for EventBus; depends on `@connectum/events` | | `@connectum/events-redis` | Redis Streams adapter for EventBus; depends on `@connectum/events` | | `@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"] E["@connectum/events
v1.0.1"] EN["@connectum/events-nats
v1.0.0"] EK["@connectum/events-kafka
v1.0.0"] ER["@connectum/events-redis
v1.0.0"] O["@connectum/otel
v1.0.3
(no @connectum deps)"] CLI["@connectum/cli
v1.3.0
(devDeps only)"] end C -.->|"same version
always"| I C -.->|"same version
always"| H C -.->|"same version
always"| R A -->|"dep: core ^1.0.0"| C E -->|"dep: core ^1.0.0"| C EN -->|"dep: events ^1.0.0"| E EK -->|"dep: events ^1.0.0"| E ER -->|"dep: events ^1.0.0"| E class C,I,H,R accent ``` #### 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 13 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/026-eventbus-architecture.md --- # ADR-026: EventBus Architecture ## Status Accepted -- 2026-03-07 ## Context Connectum is a universal gRPC/ConnectRPC framework (see [ADR-003](./003-package-decomposition.md) for package structure). Services built with Connectum use synchronous RPC for request-response communication, but many production scenarios require **asynchronous event-driven communication** between microservices -- event sourcing, CQRS, domain event propagation, integration events, and background processing. ### The Problem The ecosystem lacks a proto-first, adapter-based event system that integrates naturally with ConnectRPC patterns. Existing solutions fall into two categories: 1. **Full ESB / message bus frameworks** (e.g., NestJS CQRS, MassTransit) -- overly complex for simple pub/sub, bring heavy abstractions, and are not proto-first. 2. **Bare broker clients** (kafkajs, nats.js, ioredis) -- minimal abstraction, require per-broker boilerplate, no type safety, no standardized routing. What we need: * A minimal adapter interface that abstracts broker differences * Proto-first event routing with type safety (reusing the protobuf investment from ConnectRPC) * Familiar API for developers already using Connectum's `createServer()` + `ConnectRouter` * Built-in middleware pipeline (retry, DLQ) composable like ConnectRPC interceptors * Server lifecycle integration (start/stop with `createServer()`) * In-memory adapter for testing without external infrastructure ### Inspiration The Go ecosystem's [Watermill](https://github.com/ThreeDotsLabs/watermill) library demonstrates that a minimal Publisher/Subscriber interface (5 methods) can abstract any message broker while keeping the surface area small. This ADR adapts that philosophy to Node.js and protobuf-es. ## Decision Implement a pluggable adapter-based EventBus as a set of 5 packages following the existing layer architecture: ``` @connectum/events # Layer 1: Core EventBus, router, middleware, MemoryAdapter @connectum/events-nats # Layer 2: NATS JetStream adapter @connectum/events-kafka # Layer 2: Kafka/Redpanda adapter (KafkaJS) @connectum/events-redis # Layer 2: Redis Streams adapter (ioredis) @connectum/events-amqp # Layer 2: AMQP/RabbitMQ adapter (amqplib) ``` ### Key Architectural Decisions #### 1. EventAdapter Interface -- 5 Core Methods The adapter interface is intentionally minimal, inspired by Watermill: ```typescript interface AdapterContext { readonly serviceName?: string; // e.g., "order.v1@pod-abc123" } interface EventAdapter { readonly name: string; connect(context?: AdapterContext): Promise; disconnect(): Promise; publish(eventType: string, payload: Uint8Array, options?: PublishOptions): Promise; subscribe(patterns: string[], handler: RawEventHandler, options?: RawSubscribeOptions): Promise; } ``` Broker-specific configuration (connection strings, TLS, consumer options) lives in each adapter's constructor options, not in the interface methods. This keeps the interface stable while allowing full broker-level tuning. The `connect()` method accepts an optional `AdapterContext` containing service-level metadata. The EventBus derives `serviceName` from registered proto service descriptors using `deriveServiceName()` -- extracting unique package names and appending the hostname for replica disambiguation. Adapters use this for broker-level client identification (Kafka `clientId`, NATS connection `name`, Redis `CLIENT SETNAME`). Explicit adapter options always take priority. #### 2. Proto-First Event Routing Event handlers are registered using protobuf service/method descriptors, mirroring the ConnectRouter pattern. The `EventRouter.service()` method iterates service methods, resolves topic names from proto descriptors, and creates typed route entries: ```typescript // Define event handlers (mirrors ConnectRPC service implementation) const myEventRoutes: EventRoute = (events) => { events.service(OrderEventsDesc, { orderCreated: async (event, ctx) => { // event is typed as MessageShape console.log(event.orderId); await ctx.ack(); }, orderShipped: async (event, ctx) => { // event is typed as MessageShape await ctx.ack(); }, }); }; ``` This provides: * **Full type safety** -- handler input type is inferred from the proto method's input message * **Compile-time completeness** -- `ServiceEventHandlers` requires all service methods to have handlers * **Familiar API** -- mirrors `router.service(ServiceDesc, impl)` from ConnectRPC #### 3. Custom Topic Proto Option Default topic naming uses `method.input.typeName` (e.g., `mypackage.OrderCreated`). For cases where the topic must differ from the proto type name, a custom proto option is provided: ```protobuf // proto/connectum/events/v1/options.proto extend google.protobuf.MethodOptions { optional EventOptions event = 50102; } message EventOptions { optional string topic = 1; } ``` Usage in service proto: ```protobuf rpc OrderCreated(OrderCreatedEvent) returns (google.protobuf.Empty) { option (connectum.events.v1.event).topic = "orders.created.v2"; } ``` The `resolveTopicName()` function checks for the custom option first, falling back to `method.input.typeName`. #### 4. Middleware Pipeline Composable middleware using an onion model (Express/Koa style), applied via `composeMiddleware()` with dispatch: ```mermaid flowchart LR Custom[custom · outermost] --> DLQ DLQ --> Retry[retry · innermost] Retry --> Handler ``` Built-in middleware: * **retryMiddleware** -- configurable backoff (exponential, linear, fixed), max retries, retryable error filter * **dlqMiddleware** -- publishes failed events to DLQ topic with error metadata, then acks original * **Typed error classes** -- `NonRetryableError` and `RetryableError` provide declarative retry control via `Symbol.for()` branding Custom middleware follows the standard signature: ```typescript type EventMiddleware = (event: RawEvent, ctx: EventContext, next: EventMiddlewareNext) => Promise; ``` #### 5. EventContext with Explicit Ack/Nack Each event handler receives an `EventContext` with idempotent `ack()` / `nack(requeue?)` operations: ```typescript interface EventContext { readonly signal: AbortSignal; readonly eventId: string; readonly eventType: string; readonly publishedAt: Date; readonly attempt: number; readonly metadata: ReadonlyMap; ack(): Promise; nack(requeue?: boolean): Promise; } ``` Supports explicit ack/nack. Auto-ack on successful handler completion if neither called. #### 6. Wildcard Topic Matching Two wildcard tokens for flexible subscription patterns: * `*` matches exactly one dot-separated segment * `>` matches one or more trailing segments ```typescript matchPattern("user.*", "user.created") // true matchPattern("user.*", "user.created.v2") // false matchPattern("user.>", "user.created") // true matchPattern("user.>", "user.created.v2") // true ``` Adapters translate these patterns to broker-native equivalents (NATS subjects, Kafka regex, Redis stream keys). #### 7. Server Lifecycle Integration via EventBusLike `@connectum/core` defines a minimal `EventBusLike` interface: ```typescript interface EventBusLike { start(): Promise; stop(): Promise; } ``` `createEventBus()` returns `EventBus & EventBusLike`, allowing seamless integration with the server: ```typescript const bus = createEventBus({ adapter: NatsAdapter({ servers: "nats://localhost:4222" }), routes }); const server = createServer({ services: [routes], eventBus: bus, // Lifecycle managed by server }); ``` The server starts the event bus after transport is ready and stops it during graceful shutdown (via `ShutdownManager`). During graceful shutdown, the EventBus tracks in-flight message handlers via an `inFlight` Set. The `stop()` method follows a drain sequence: (1) stop accepting new messages (nack with requeue), (2) wait for in-flight handlers up to `drainTimeout` (default: 30s), (3) force-abort remaining via AbortSignal if timeout exceeded, (4) disconnect adapter. The `drainTimeout: 0` option skips the drain for immediate shutdown. #### 8. MemoryAdapter for Testing An in-memory adapter is included in `@connectum/events` (not in a separate package) to enable unit testing without any external broker: ```typescript const bus = createEventBus({ adapter: MemoryAdapter(), routes: [myRoutes], }); ``` The MemoryAdapter delivers events synchronously to matching subscribers using the same wildcard matching as production adapters. ### Adapter Implementations | Package | Broker | Client Library | Key Features | |---------|--------|---------------|-------------| | `@connectum/events-nats` | NATS JetStream | `@nats-io/transport-node`, `@nats-io/jetstream` | Durable consumers, wildcard subjects, ack/nak per message | | `@connectum/events-kafka` | Apache Kafka / Redpanda | `kafkajs` | Consumer groups, regex topic subscription, compression | | `@connectum/events-redis` | Redis Streams / Valkey | `ioredis` | XREADGROUP with consumer groups, dedicated blocking connection, MAXLEN trimming | | `@connectum/events-amqp` | RabbitMQ 3.x+ / LavinMQ | `amqplib` | Topic exchanges, competing consumers, dead letter exchange (DLX), wildcard binding | All adapters implement the same `EventAdapter` interface and are interchangeable. ### Package Dependency Graph ```mermaid graph TB subgraph "Layer 0: Foundation" Core["@connectum/core"] end subgraph "Layer 1: Events Core" Events["@connectum/events"] end subgraph "Layer 2: Broker Adapters" Nats["@connectum/events-nats"] Kafka["@connectum/events-kafka"] Redis["@connectum/events-redis"] Amqp["@connectum/events-amqp"] end Events -- "dependency" --> Core Nats -- "peerDependency" --> Events Kafka -- "peerDependency" --> Events Redis -- "peerDependency" --> Events Amqp -- "peerDependency" --> Events class Core accent ``` ## Consequences ### Positive 1. **Consistent API across all brokers** -- switching from NATS to Kafka requires changing only the adapter constructor, not application code 2. **Easy to test** -- MemoryAdapter enables fast, deterministic unit tests without Docker or external services 3. **Proto-first type safety** -- event schemas are defined in protobuf, serialization/deserialization is automatic, handler types are inferred 4. **Familiar pattern** -- `EventRouter.service()` mirrors ConnectRPC's `ConnectRouter.service()`, reducing learning curve for existing Connectum users 5. **Server integration** -- `EventBusLike` interface ensures event bus lifecycle is managed alongside the gRPC transport 6. **Composable middleware** -- retry, DLQ, and custom middleware compose using the same onion model as ConnectRPC interceptors 7. **Minimal adapter surface** -- 5 methods to implement for a new broker, broker-specific config in constructor ### Negative 1. **Adapter abstraction limits broker-specific features** -- advanced features like Kafka exactly-once semantics, NATS Key-Value, or Redis Streams XCLAIM require escape hatches or adapter-specific extensions 2. **Additional complexity for simple pub/sub** -- the proto-first approach requires proto definitions even for simple fire-and-forget events 3. **Each new broker requires a separate package** -- new adapter packages must be created, published, and maintained independently 4. **Auto-ack on success** -- handlers auto-ack on successful completion if neither `ack()` nor `nack()` is called explicitly; explicit control is available when needed 5. **Single consumer group per EventBus** -- all routes in one EventBus share the same consumer group; multiple groups require multiple EventBus instances ### Risks * **Proto-first routing may not cover all event patterns** -- mitigated by custom topic proto option and direct adapter access for advanced use cases * **Broker-specific tuning may require escape hatches** -- each adapter's constructor options provide full broker-level configuration; the adapter interface intentionally does not restrict this * **Wildcard translation across brokers** -- NATS-style wildcards (`*`, `>`) must be translated to broker-native equivalents; tested per adapter with comprehensive pattern matching tests ## Alternatives Considered ### Alternative 1: Direct Broker Clients (No Abstraction) **Rating:** 4/10 Let each service use broker-specific clients directly (kafkajs, nats.js, ioredis). **Pros:** * Full access to broker-specific features * No adapter overhead * Simpler for single-broker deployments **Cons:** * No code reuse across services using different brokers * No type safety from proto definitions * No standardized middleware pipeline * No server lifecycle integration * Testing requires real broker instances ### Alternative 2: Full Event Sourcing Framework **Rating:** 3/10 Implement a complete event sourcing / CQRS framework (like NestJS CQRS module). **Pros:** * Complete solution for event-driven architectures * Built-in aggregate support, event store, projections **Cons:** * Massive scope increase -- far beyond the framework's purpose * Forces specific architectural patterns on consumers * High complexity for simple pub/sub use cases * Contradicts Connectum's philosophy of minimal, composable packages ### Alternative 3: CloudEvents Standard **Rating:** 5/10 Use the CloudEvents specification as the event envelope instead of raw protobuf. **Pros:** * Industry standard for event metadata (id, source, type, time) * Interoperability with non-Connectum services * Well-defined envelope format **Cons:** * Adds a serialization layer on top of protobuf (CloudEvents JSON/proto + protobuf payload) * Not all brokers have native CloudEvents support * Increases message size with additional envelope fields * Can be added later as an optional middleware without changing the core architecture ## References 1. [ADR-003: Package Decomposition](./003-package-decomposition.md) -- package structure and layer rules 2. [ADR-023: Uniform Registration API](./023-uniform-registration-api.md) -- ConnectRouter service registration pattern 3. [ADR-025: Package Versioning Strategy](./025-package-versioning-strategy.md) -- versioning for independent packages 4. [Watermill (Go)](https://github.com/ThreeDotsLabs/watermill) -- inspiration for minimal adapter interface 5. [protobuf-es](https://github.com/bufbuild/protobuf-es) -- DescMessage, DescService, MessageShape types *** ## Changelog | Date | Author | Change | |------|--------|--------| | 2026-03-07 | Software Architect | Initial ADR: EventBus architecture with pluggable adapter pattern | | 2026-03-09 | Software Architect | Added typed error classes (#48) and graceful drain (#47) | | 2026-03-22 | Software Architect | Added AdapterContext for automatic broker-level client identification | --- --- url: /en/contributing/adr/027-external-contracts-vs-eventbus.md --- # ADR-027: External Message Contracts vs the Internal EventBus ## Status Accepted -- 2026-06-12 ## Context [ADR-026](./026-eventbus-architecture.md) established the EventBus as a proto-first, adapter-based internal event system: `EventBus.publish()` takes a protobuf message, serializes it with `toBinary`, and routes it by proto type. The four adapters (NATS, Kafka, Redis, AMQP) implement a deliberately minimal `EventAdapter` interface that moves opaque `Uint8Array` payloads. A production adopter then needed to publish to a **partner's** broker under an externally agreed AsyncAPI contract: a named exchange and durable queue, `contentType: application/json`, `deliveryMode=2`, `mandatory`, and per-message publisher confirms. This drove the `events-amqp-external-contract` change, which added adapter-level serialization (`contentType` + `encode`/`decode`), explicit topology declaration, `queueOverrides`, automatic recovery, and reliable publishing to `@connectum/events-amqp`. That work surfaced a recurring design question that will reappear for every adapter: * Should the **core** `@connectum/events` interfaces grow to express external-contract variability — per-publish `contentType`, per-subscribe queue names, a `wireFormat: "binary" | "json"` switch on `EventBus`? * Or do external contracts belong to a different layer than the internal bus? Without a stated principle, each adapter would answer this differently, and the core interfaces would accrete passthrough options that weaken the typed proto contract. ## Decision **The EventBus is an internal protobuf bus. External message contracts are served at the adapter layer (or by publishing directly through an adapter), not by extending the core EventBus interfaces.** Concretely: 1. **`EventBus.publish()` stays protobuf-only.** No `wireFormat` switch is introduced. Putting JSON (or any non-protobuf encoding) on the bus would force every subscriber, middleware, retry, and DLQ path to understand multiple wire formats, and would make `RawEvent.payload` ambiguous. protobuf-es `toJson` only solves the producer side; the consumer side would require format detection (content-sniffing), which the AMQP change already rejected as implicit magic that must fail loudly instead. 2. **External contracts are produced through the adapter.** When an application must emit JSON (or a partner's contentType) against an external contract, it serializes the bytes itself and publishes through the adapter, which controls the wire `contentType` and optional `encode`/`decode` transcoding. This is an outbound producer to a foreign system — it does not need the EventBus's typed routing, middleware, or DLQ. 3. **Core interfaces stay minimal; variability lives in adapter options.** `PublishOptions` / `RawSubscribeOptions` are not extended with generic passthrough bags, declaration merging, or a viral `EventBus` generic. Contract variability that is genuinely whole-channel (the whole queue is JSON, the whole group reads a named queue) is expressed by adapter-level options (`serialization`, `queueOverrides`, `topology`). A future need for genuine *per-message* variability — if one ever arises with a concrete use case — would be added as typed options on a specific adapter's constructor, not on the core interface. 4. **`PublishOptions.sync` is removed.** It was a no-op across all four adapters: each already confirms publishes per-message (NATS `PubAck`, Kafka `producer.send`, Redis `XADD`, AMQP per-message broker ack with typed errors). A resolved `publish()` already means the broker accepted the message; there was no fire-and-forget mode to opt out of. Removed ahead of the first stable release. ## Consequences ### Positive * The internal bus keeps one wire format; subscribers, middleware, retry, and DLQ reason about exactly one payload shape. * Core `EventAdapter` / `PublishOptions` interfaces stay small and typed — no passthrough erosion across four heterogeneous adapters. * External-contract concerns (content type, topology, confirms, recovery) are isolated to the adapter that actually talks to the foreign broker. ### Negative / Trade-offs * **Direct adapter publishing bypasses EventBus middleware** (producer-side retry / DLQ). For an external at-least-once producer this is compensated by the adapter's per-message confirms and typed error taxonomy (`AmqpUnroutableError`, `AmqpPublishNackError`, `AmqpPublishTimeoutError`, …), which let the application implement an "advance cursor after confirm" loop. This trade-off must be stated explicitly in the events-amqp guide. * Per-message wire-format variability is intentionally unsupported. If a real use case appears, it is added per-adapter, not retrofitted into the core interface. ## Alternatives Considered * **`adapterOptions?: Record` passthrough on `PublishOptions`** — rejected: kills type safety, silently ignores typos, becomes a junk drawer; contradicts the project's fail-fast discipline. * **Module augmentation / declaration merging per adapter** — rejected: fragile under tsup `.d.ts` compilation, augmentations from two adapters in one app collide, poor discoverability. * **Generic `EventBus`** — rejected: the generic goes viral through `EventBusOptions`, `createServer({ eventBus })`, and middleware for disproportionate cost. * **`wireFormat: "binary" | "json"` on EventBus** — rejected as an anti-feature (see Decision 1). ## References * [ADR-026: EventBus Architecture](./026-eventbus-architecture.md) * [ADR-003: Package Decomposition](./003-package-decomposition.md) (layer rules) * Change: `events-amqp-external-contract` --- --- url: /en/contributing/adr/028-service-catalog.md --- # ADR-028: Service Catalog ## Status Accepted -- 2026-06-15 ## Context [ADR-026](./026-eventbus-architecture.md) and [ADR-027](./027-external-contracts-vs-eventbus.md) cover the asynchronous, event-driven path between services. The synchronous, request-response path between services was left to each adopter to assemble by hand on top of the transport layer. A prior `in-process-transport` change had already delivered the transport primitives: `createLocalTransport`, `server.client(Desc, { fallback })`, and `server.hasService`. But the transport layer is only a third of what a hybrid (monolith ↔ microservices) deployment needs. Analysing three production code bases that build on Connectum surfaced a recurring, duplicated **DX layer** that everyone re-implements on top of the transport: * **fintech-monolith** -- 8 services in one process, pure dependency-injection composition, no cross-service RPC inside handlers. A catalog is **not needed** here. The hard constraint this case imposes: today's `createServer({ services })` must keep working with no catalog, no resolver, and no `enabledServices`. * **meshai** -- 5+ "skill" services behind k8s + a service mesh, where one proto descriptor is reused for N different endpoints. ~60 lines of copy-pasted boilerplate (a gateway-auth interceptor, an env-driven endpoint registry, a hand-rolled `Map` cache, a custom resolver function) repeated across 3 files. * **AnyLabel** -- 8 services over docker/k8s with fixed routing, 18 environment variables (`INGEST_API_URL`, `MALLENOM_BASE_URL`, …) and 5 files with a duplicated `createDefaultInterceptors + createOtelInterceptor` boot section. The unifying observation: a catalog describes **deployment topology** (what is local, what is remote, and at what address) layered on top of **proto contracts** (which must not know about topology). Therefore all of the catalog / resolver / `enabledServices` description must live exclusively in runtime/boot code, never in proto. The goal of this ADR is a single declarative DX layer for cross-service calls that satisfies all three cases without forcing topology into proto and without breaking the catalog-free monolith. Because the framework's first stable release (1.0.0) is not yet published, removals required to land a clean API are pre-publish breaking and do not require a major version bump. ## Decision Add a **service catalog** to `@connectum/core` plus a buf code-generation plugin `@connectum/protoc-gen-catalog`. The catalog is a plain registry of proto descriptors; topology is supplied at boot; handlers make declarative cross-service calls through a typed `ctx.call` / `ctx.stream` surface on the handler context. ### 1. Declarative cross-service calls via `ctx.call` / `ctx.stream` on a Connectum `Context` Handlers registered through `defineService` receive a Connectum `Context` (`packages/core/src/context.ts`) as their second argument instead of the raw ConnectRPC `HandlerContext`. `Context extends HandlerContext`, so every existing field (`signal`, `timeoutMs()`, `requestHeader`, `values`, …) keeps working, and it adds two members: * `call(method, request, options?)` -- invokes a unary service in the catalog and returns a `Promise`. * `stream(method)` -- opens a streaming call, returning a kind-specific factory (server-streaming yields an `AsyncIterable`; client- and bidi-streaming return push handles). The call/stream key is `"/"` (e.g. `"orders.v1.OrdersService/GetOrder"`). The transport is chosen automatically per call: an in-process call when the target is mounted locally, otherwise the resolver-supplied transport. **Rationale.** `Context` is implemented as a thin wrapper over ConnectRPC's `HandlerContext` rather than by stuffing the catalog primitives into ConnectRPC's `ContextValues`. `HandlerContext` is ConnectRPC-owned and effectively read-only -- it is not an extension point the framework can add typed methods to -- and `ContextValues` is a data-only bag with no place for the call dispatch logic. Wrapping keeps the public surface honest: a handler that makes no cross-service calls sees no augmentation, so `keyof ConnectumCallMap` is `never` and `ctx.call` is statically uncallable -- the correct default. The framework performs a single internal cast at mount time (the `wrapHandlers` injection point) to substitute `Context` for `HandlerContext`; this is the only public-surface cast in the design. The positional `ctx.call(method, request, options)` shape mirrors ConnectRPC's `client.method(request, options)` and Moleculer's `ctx.call(action, params, opts)`; the object-argument alternative was rejected as call-site noise. **Consequence.** Handlers gain a uniform, type-safe cross-service call API regardless of where the target is deployed, and existing `HandlerContext` usage is preserved. The cost is one framework-owned cast and a second context type that mirrors ConnectRPC's `MethodImpl` / `ServiceImpl` (`ConnectumMethodImpl` / `ConnectumServiceImpl`). ### 2. `defineService` closure-register replacing `ServiceRoute` A service is registered with `defineService(descriptor, handlers)` (or `defineLazyService(descriptor, factory)` for DI-heavy monoliths). Both return an opaque `ServiceDefinition`: ```ts interface ServiceDefinition { readonly descriptor: DescService; readonly register: (router: ConnectRouter, ctx: RegisterContext) => void; } ``` `createServer({ services })` accepts `readonly ServiceDefinition[]`. The old `ServiceRoute = (router) => void` form is **removed**. **Rationale.** Pairing the proto `DescService` with the registration closure lets the framework build the catalog, drive `enabledServices` activation, and validate the transport without re-deriving service identity from the router. Crucially, the construction generic `S extends DescService` lives only on the `defineService` / `defineLazyService` boundary; once a service is defined, its handlers are captured inside the `register` closure and nothing generic leaks out. The framework can iterate `ServiceDefinition[]` with no variance problems. The brand-based phantom-type alternative was rejected because it would expose a public generic parameter that users would have to erase (e.g. `ServiceDefinition`) on arrays. `ctx.call` type inference is unaffected because `ConnectumCallMap` is a global augmentation independent of `ServiceDefinition` generics. The framework supplies a `RegisterContext` (carrying `wrapHandlers`) to the closure at mount time, so `defineService` needs no server reference. `defineLazyService`'s factory runs only when the service is actually mounted locally (in `enabledServices`, or `enabledServices === undefined`), so a service routed to a remote process never instantiates its local dependencies. **Consequence.** Removing `ServiceRoute` is a pre-publish breaking change; all existing examples migrate to `defineService(...)`. This is acceptable because nothing is published yet -- the removal would be GA-breaking if deferred past 1.0.0, so it must land before publish. ### 3. Catalog shape, augmentation maps, and code generation The catalog is `type ServiceCatalog = Readonly>` -- a plain registry mapping a proto `typeName` to its descriptor. It carries no topology. Typing for `ctx.call` / `ctx.stream` comes from **two** single, global module-augmentation interfaces in `@connectum/core`: `ConnectumCallMap` (unary RPCs) and `ConnectumStreamMap` (streaming RPCs), both keyed `"/"`. Both start empty, so a project with no generated catalog still type-checks (calls are then untyped rather than a hard error). `@connectum/protoc-gen-catalog` is a buf/protoc plugin that emits **one `catalog.gen.ts` per buf module** containing a runtime `serviceCatalog` object plus the two augmentations in one `declare module "@connectum/core"` block. The plugin classifies methods via `DescMethod.methodKind`: `unary` → `ConnectumCallMap`; `server_streaming` / `client_streaming` / `bidi_streaming` → `ConnectumStreamMap` with a kebab-cased `kind` discriminator (`"server-stream"`, `"client-stream"`, `"bidi"`). Two code-generation invariants are mandatory: * Every `catalog.gen.ts` emits `import type {} from "@connectum/core";`. Without it, `tsc` rejects the augmentation in cross-package builds with `TS2664: Invalid module name in augmentation`. * The generated file must be loaded by the contracts package (re-export it from `index.ts`, or add a top-level `import "./catalog.gen.ts";`). A named-only consumer that omits this gets **silently missing keys** in `ConnectumCallMap` -- no compile error. This footgun makes the entry-point load mandatory. The plugin is **not** a cross-package aggregator: each buf module emits its own `catalog.gen.ts`, and cross-package composition is done at runtime by the consumer via `mergeCatalogs(a, b, c)`. `mergeCatalogs` **throws** (a `CatalogConfigError`) on a duplicate `typeName`. **Rationale.** A plain record keeps the proto contract and the runtime API 1:1 -- no aliases, no proto extensions, no DSL. A single global `ConnectumCallMap` was validated to work across npm packages (per-package maps would break consumer-side `ctx.call` type-safety). The runtime `mergeCatalogs` throw is mandatory, not an optional convenience: TypeScript does **not** catch a duplicate `typeName` when two contracts packages declare an identical-shape entry under the same key (it treats this as a redeclaration of an identical interface property and silently merges). Only a runtime check defends against a silent collision routing calls to the wrong service. Because TypeScript cannot catch identical-shape duplicates, a documented naming convention (`{org}.{team}.{domain}.v{N}.{Service}`) plus a code-review checkpoint is the recommended additional guard. **Consequence.** Codegen plugs into the existing `buf.gen.yaml` flow and adds no runtime dependency for consumers. The single-object `serviceCatalog` does not tree-shake (a measured ~357–377 bytes per service after minification), which is the accepted cost of the single-catalog design; very large catalogs (>1000 services) should split into per-domain packages. Duplicate-typeName protection is split across a compile-time naming convention and a runtime `mergeCatalogs` throw. ### 4. RemoteResolver -- synchronous, lazy transport, cached A `RemoteResolver` maps a remote service identity to a ConnectRPC `Transport`: ```ts type RemoteResolver = (ctx: { typeName: string; endpoint?: string }) => Transport | null; ``` The resolver **must be synchronous** and **must not perform network I/O** (no TCP dial, no DNS lookup at resolution time). It only maps an identity to a lazily-connecting `Transport`. Returning `null` means "no route" → the call fails with `Code.Unavailable`. The framework caches the result per unique `(typeName, endpoint)` key, so the resolver runs once per distinct route. `CallOptions.endpoint` is an opaque hint the core does not interpret -- the resolver decides what it means (URL, k8s service name, sharding key). Built-in resolvers: `singleTransportResolver`, `mapResolver`, `dnsResolver`, `perServiceEnvResolver`. **Rationale.** The transport already represents a lazy connection, so an async resolver would add an `await` to every call site for no practical gain. Synchronous resolution plus lazy transport keeps DNS and dialing off the boot critical path -- there is no startup network I/O. The object-shaped context (`{ typeName, endpoint }`) makes future field additions non-breaking. Startup validation is a **shape check only**: when a `catalog` is configured, every `enabledServices` entry must be a known catalog key, else `createServer(...).start()` throws `CatalogConfigError`. The framework does **not** probe resolvers at startup -- a resolver is not even invoked until the first `server.client()` / `ctx.call` for a remote service, so route reachability is never validated eagerly. **Consequence.** Cold start stays fast even with a large catalog. The trade-off is that resolver/route misconfiguration is not caught at startup: a non-local service with no `remoteResolver` surfaces as `CatalogConfigError`, and a resolver returning `null` surfaces as `ConnectError(Code.Unavailable)` -- both at `server.client()` construction (or, for `ctx.call`, at dispatch). Only the catalog/`enabledServices` shape is validated eagerly. ### 5. Cascade defaults: signal, deadline, trace, headers `ctx.call` / `ctx.stream` cascade context from the inbound request unless overridden via `CallOptions`: * **signal** -- when `CallOptions.signal` is omitted, the inbound `ctx.signal` is injected, so cancelling the inbound RPC cancels every in-flight `ctx.call`. A supplied signal **replaces** the cascade (it is not linked with `ctx.signal`). * **deadline** -- when `CallOptions.timeoutMs` is omitted, the remaining inbound deadline (`ctx.timeoutMs()`) is injected. A caller may **shorten** the deadline, never extend it. * **trace** -- propagated **implicitly** by the `@connectum/otel` client interceptor mounted in `outgoingInterceptors`, which reads the active span from `AsyncLocalStorage` and emits `traceparent`. The user does not configure a trace field. `@connectum/core` stays Layer 0 and does **not** auto-instantiate OpenTelemetry -- trace propagation only happens if the user mounts the otel interceptor. * **headers** -- opt-in. The default propagates **no** inbound headers (`propagateHeaders: []`). `defaultPropagateHeaders` (`packages/core/src/propagateHeaders.ts`) is a ready-made set containing the W3C trace-context headers (`traceparent`, `tracestate`) that a user can spread and extend. Explicit `CallOptions.headers` always take precedence over propagated values. **Rationale.** Signal and deadline cascade make cancellation and deadline budgets correct by default -- a slow downstream cannot outlive its caller, and shorten-only deadline prevents a child from extending its parent's budget. Trace is kept implicit (and out of core) to avoid a hidden L0 dependency on OpenTelemetry and to avoid a double `traceparent`: the otel interceptor overwrites any propagated header with the active span's context, so the otel value wins. Header propagation defaults to empty to avoid two failure modes a non-empty default would introduce -- a stale parent correlation header "sticking" to an outgoing call, and a double-injection / custom-propagator conflict with the otel interceptor. `authorization` is deliberately excluded even from the convenience default, because forwarding credentials is a security-sensitive choice that must be explicit. **Consequence.** Cancellation and deadlines are correct out of the box; distributed tracing requires mounting `@connectum/otel`; arbitrary correlation headers require an explicit `propagateHeaders` allow-list. The exact allow-list ergonomics (glob support, policy lists) were deferred to validation against the reference examples and start from the empty, lowest-risk default. ### 6. `outgoingInterceptors` typed as `@connectrpc/connect.Interceptor` The single new client-side cross-cutting field is `outgoingInterceptors?: readonly Interceptor[]`, typed directly as `@connectrpc/connect`'s `Interceptor` -- no Connectum-specific wrapper. **Rationale.** Every Connectum interceptor (`@connectum/interceptors`, `@connectum/otel`) is already compatible with the ConnectRPC `Interceptor` contract. Reusing that contract lets users mount existing interceptors -- and third-party ConnectRPC interceptors -- with no adapter. A wrapper would add surface area for no benefit and would block direct import of external interceptors. `clientIdentity` is intentionally **not** a separate API concept; users add identity through a closure in their own interceptor. **Consequence.** The auth/identity copy-paste that the meshai and AnyLabel cases duplicated across files collapses into one shared interceptor chain. ### 7. Split error model Catalog failures use two distinct error types depending on whether the fault is a developer mistake or a runtime condition (`packages/core/src/catalogErrors.ts`): * **`CatalogConfigError extends Error`** -- a configuration mistake, detected eagerly at construction or startup. Examples: `server.client(Desc)` on a non-local service with no resolver, `enabledServices` that is not a subset of the catalog, a duplicate `typeName` during `mergeCatalogs`. It fails loud with a stack trace and a clean prototype chain across compiled targets. * **`ConnectError`** -- an operational `ctx.call` / `ctx.stream` failure, with the appropriate Connect status code: `FailedPrecondition` (`ctx.call` / `ctx.stream` invoked when no catalog is configured), `Unimplemented` (a genuine runtime dispatch miss -- `ctx.call("unknown.Type/Method")`), `Unavailable` (resolver returned `null`), `Internal` (resolver threw). **Rationale.** A configuration bug and a runtime RPC failure call for different handling. A stack trace is more useful than a gRPC code for a misconfiguration that should never reach production; conversely, operational failures must flow through the existing `ConnectError`-based interceptor and error-handler machinery. Reserving `Code.Unimplemented` strictly for a runtime dispatch miss keeps the two classes cleanly separable: a configuration mistake fails eagerly as a `CatalogConfigError`, a `ctx.call` against an unconfigured catalog is `FailedPrecondition`, and an unknown `typeName` at dispatch is `Unimplemented`. **Consequence.** The error surface is slightly larger -- callers must know which operations can throw `CatalogConfigError` versus a `ConnectError` -- but the boundary between "fix your config" and "handle this at runtime" becomes unambiguous. ### 8. Streaming partial failure -- deliver-then-error On a mid-stream transport failure, the iterator returned by `ctx.stream` delivers the messages received so far and **then** throws the terminal `ConnectError`. Messages are not silently dropped, and the error is not raised before the buffered messages are consumed. **Rationale.** Surfacing the failure through the iterator's final error keeps the already-received data usable while still signalling failure unambiguously. Silently dropping buffered messages would hide progress; raising eagerly would discard valid data. **Consequence.** Consumers handle a stream that may yield N valid messages and then throw. A fail-fast mode (raise immediately, discard buffered messages) is a noted future option behind an explicit opt-in, not a v1 default. ### 9. Runtime / codegen compatibility -- `.js` import extension The buf plugin emits `.js` extensions in the relative specifiers inside `catalog.gen.ts` (`import { GreeterService } from "./greeter_pb.js";`), matching `protoc-gen-es` output (use the same `import_extension`). Pre-compiled distribution (tsup/tsc → `dist/*.js` + `dist/*.d.ts`) is the **recommended** path for published contracts packages and works across runtimes out of the box. Raw-source `.ts` distribution is supported on runtimes with native or strip-types TypeScript: Bun, Node.js 22+ (`--experimental-strip-types`), Node.js 25.2+ (native). `allowImportingTsExtensions: true` is **not** required in the typical case and is not forced by the codegen template; it remains an escape hatch for edge cases (e.g. a `verbatimModuleSyntax: true` workspace with no compile step). **Rationale.** `.js` extensions are the minimal common denominator: they work for pre-compiled output (where `.ts` extensions would break because compiled JS cannot resolve `.ts` files) and for runtime-native TS (where the loader resolves the corresponding `.ts`). The `.ts`-extension alternative was rejected because it breaks pre-compiled distribution and forces every consumer to enable `allowImportingTsExtensions`. **Consequence.** Generated catalogs work for both compiled and source distribution without per-consumer tsconfig changes. Deno and edge runtimes (Cloudflare Workers, Vercel Edge) are out of scope. ## Non-goals / v1 limitations These are explicit limitations of the v1 catalog, not merely "future nice-to-haves." Documentation and the migration guide must state them. * **Catalog versioning for mixed deployments.** During a rolling update where a new version adds or removes methods, a method missing on the old version produces `Code.Unimplemented` on the old side. The v1 coordination strategy is **forward/backward-compatible additive changes only** (add methods; do not remove or rename) until a future `catalog-versioning` change ships. This must be documented in the migration guide and the resolver-patterns guide. * **Vendor-proto exclusion.** The plugin generates services from buf's `files-to-generate` only, with no selective skip annotation. Vendor protos land in the output if they are part of the input. The v1 workaround is a separate buf module or a manual `pick` via `mergeCatalogs`. Selective exclusion is a future enhancement. * **Async resolvers.** The resolver is synchronous by contract (lazy transport covers the vast majority of cases). An async resolver would add an `await` to every call site and is out of v1; if a concrete use case appears, it is a separate change. * **Hot-reload of topology.** `enabledServices` and `remoteResolver` configuration are read only at `server.start()`. Changing topology requires a restart. ## Consequences ### Positive 1. **One declarative DX layer** replaces the per-adopter boilerplate (endpoint registry, transport cache, custom resolver, per-call-site interceptor chains). 2. **Catalog-free monolith is unchanged** -- catalog, resolver, and `enabledServices` are all optional; the fintech-monolith case keeps using today's `createServer({ services })`. 3. **Type-safe cross-service calls** via positional `ctx.call` / `ctx.stream`, generated from proto, with no aliases or proto topology. 4. **Transport-transparent** -- the same call hits an in-process transport when the target is local and a resolver-supplied transport when remote, through the same interceptor chain (local↔local has parity with local↔remote). 5. **Correct cancellation and deadlines by default** via signal/deadline cascade (shorten-only deadline). 6. **No hidden L0 dependencies** -- trace stays in `@connectum/otel`; header propagation is opt-in and empty by default. 7. **Reuses the ConnectRPC `Interceptor` contract** -- existing and third-party interceptors mount with no adapter. ### Negative 1. **Pre-publish breaking removals** -- `ServiceRoute` and the `in-process-transport` `fallback` parameter are removed; all examples migrate. (Free now; GA-breaking if deferred past 1.0.0.) 2. **Larger error surface** -- callers must distinguish `CatalogConfigError` (config bug) from `ConnectError` (operational). 3. **Single-object `serviceCatalog` does not tree-shake** -- accepted cost; very large catalogs (>1000 services) should split into per-domain packages. 4. **A second context type** (`Context`, `ConnectumMethodImpl`, `ConnectumServiceImpl`) mirrors ConnectRPC's, plus one framework-owned cast at mount time. ### Risks * **Identical-shape duplicate typeNames** are invisible to TypeScript across packages -- mitigated by the mandatory runtime `mergeCatalogs` throw plus a documented naming convention and code-review checkpoint. * **Mixed-deployment method drift** during rolling updates surfaces as `Code.Unimplemented` -- mitigated by the additive-only coordination strategy until `catalog-versioning`, documented as a v1 limitation. * **Endpoint-specific resolver misconfiguration** is not caught at startup (only the default route is probed) -- surfaces lazily on first endpoint-specific call; documented. ## Alternatives Considered * **Proto extensions for deployment metadata** (`option (connectum.deployment).local = true`) -- rejected: mixes contract and topology and makes proto depend on runtime configuration. * **Alias namespaces in the catalog** (`{ greeter: { typeName: "acme.v1.GreeterService" } }`) -- rejected: introduces proto↔runtime divergence and an extra indirection layer; the catalog stays 1:1 with proto `typeName`. * **Async resolver** -- rejected: adds an `await` to every call site with no practical benefit since the transport is already lazy. * **Object-argument call API** (`ctx.call({ method, request, options })`) -- rejected: noisier at the call site and diverges from the ConnectRPC client API. * **`catalogs: ServiceCatalog[]` array** -- rejected: an internal merge is unavoidable, so `mergeCatalogs(...)` makes it explicit. * **Per-package `ConnectumCallMap`** -- rejected after cross-package validation: a single global map merges correctly across packages, while per-package maps would break consumer-side `ctx.call` type-safety. * **Brand-based phantom type on `ServiceDefinition`** -- rejected: exposes a public generic users must erase on arrays; the closure-register pattern removes handlers from the type entirely. * **Connectum-specific wrapper for `outgoingInterceptors`** -- rejected: adds surface area and blocks reuse of existing ConnectRPC interceptors. * **`.ts` import extensions in generated catalogs** -- rejected: breaks pre-compiled distribution and forces `allowImportingTsExtensions` on every consumer. ## References 1. [ADR-003: Package Decomposition](./003-package-decomposition.md) -- package structure and layer rules. 2. [ADR-023: Uniform Registration API](./023-uniform-registration-api.md) -- `createDefaultInterceptors()`, explicit interceptor control. 3. [ADR-024: Auth/Authz Strategy](./024-auth-authz-strategy.md) -- context propagation patterns. 4. [ADR-026: EventBus Architecture](./026-eventbus-architecture.md) -- the asynchronous counterpart to synchronous cross-service calls. 5. [ConnectRPC](https://connectrpc.com/) -- `HandlerContext`, `Interceptor`, `Transport`, `ConnectRouter`. 6. [protobuf-es](https://github.com/bufbuild/protobuf-es) -- `DescService`, `DescMethod`, `MessageShape`. *** ## Changelog | Date | Author | Change | |------|--------|--------| | 2026-06-15 | Software Architect | Initial ADR: service catalog (declarative cross-service calls, `defineService`, resolver, cascade defaults, split error model, buf codegen) | --- --- url: /en/contributing/adr/029-internal-service-to-service-auth.md --- # ADR-029: Internal (service-to-service) auth, distinct from `public` ## Status Accepted -- 2026-06-21 (design ratified; implementation is a follow-up) Tracks [#171](https://github.com/Connectum-Framework/connectum/issues/171). Extends [ADR-024](./024-auth-authz-strategy.md) (auth/authz strategy). ## Context [ADR-024](./024-auth-authz-strategy.md) defines two authorization states for an RPC, expressed as proto options consumed by `createProtoAuthzInterceptor` / the JWT auth interceptor's `skipMethods`: * **gated** -- a `default_policy: "allow"`/`"deny"` service plus per-method policies; the JWT auth interceptor must authenticate the caller. * **`public`** -- `service_auth { public: true }` or `method_auth { public: true }`: authentication is skipped entirely; anyone reachable on the wire may call it. A third, real situation has no first-class expression: an **internal, service-to-service** call that carries no end-user JWT but is *not* meant to be world-open. It surfaced while building the `car-sharing` Temporal example (Phase 2): * The trip saga runs in a **Temporal worker** (a separate process, not a Connectum `Server`). Its activities call internal RPCs (`TripService.RecordTrip`, `EndTrip`) over the network with **no `Authorization` header** -- Connectum does not auto-propagate inbound headers across a worker's client. * To let those calls through, the methods are annotated **`method_auth { public: true }`**. But the real trust boundary is the **mesh** (Istio mTLS `STRICT` + an `AuthorizationPolicy` that admits only the trips ServiceAccount). The proto now says "public" -- over-stating exposure and reading wrong to anyone auditing the contract. So today **`public` is overloaded**: it means both "intentionally world-open" (a health probe) and "internal, trusted by the network boundary" (a worker-only RPC). They have different security postures and should be auditable as different things. The same gap exists for any out-of-process internal caller: schedulers, batch jobs, the new `createCatalogClient` ([#170](https://github.com/Connectum-Framework/connectum/issues/170)). Existing building block: `createGatewayAuthInterceptor` (`@connectum/auth`) already authorizes a request from **identity injected by a trusted upstream** (a gateway/mesh) via a `trustSource` predicate + header stripping. That trust-source machinery is most of what an internal interceptor needs -- it just is not wired to a proto-level "this method is internal" marker. ## Decision (proposed) Introduce a first-class **`internal`** marker, distinct from `public`, plus an interceptor that authorizes internal calls by a configurable **trust source** rather than by an end-user token. ### 1. Proto annotation Add an `internal` boolean to the existing auth options (alongside `public`): ```proto // connectum/auth/v1/options.proto message ServiceAuth { optional bool public = ...; optional bool internal = ...; /* ... */ } message MethodAuth { optional bool public = ...; optional bool internal = ...; } ``` `internal: true` means: **skip end-user (JWT) authentication, but require an internal trust marker** (see §2). `public` keeps its meaning (no auth at all). A method is at most one of `public` / `internal` / gated; `resolveMethodAuth` (the existing service+method merge in `@connectum/auth`) is extended to surface `internal`. ### 2. `createInternalAuthInterceptor` — a **pluggable per-service trust source** A new interceptor in `@connectum/auth` that, for `internal` methods, authorizes the call from a configurable **trust source** (a predicate returning `AuthContext | null`, reusing the `createGatewayAuthInterceptor` pattern) and rejects anything lacking it as `Unauthenticated`. The credential must be **per-service**, so compromising one microservice cannot forge another's identity — a single static shared secret is explicitly **not** the default. Three factories: * **(a) `meshIdentityTrust` (production default — inherently per-service).** Verify the mesh-forwarded peer identity (the sidecar terminates mTLS and forwards a header — an Istio short-form ServiceAccount `cluster.local/ns//sa/` or a SPIFFE id) against an allow-list; allow-list entries carry the caller's roles/scopes. The mesh issues each workload its **own** mTLS identity, so this is per-service by construction. * **(b) `signedTokenTrust` (non-mesh containment path — per-service, NOT a shared secret).** Each caller signs a short-lived JWT with its **own** private key; the interceptor verifies it via that service's public key (JWKS), reusing the existing `createJwtAuthInterceptor` JWKS machinery. Compromising service A's key forges only A. * **Hard security requirement (verified empirically with `jose`):** the JWKS lookup MUST be **issuer-bound** — select the key from `jwksByIssuer[iss]` (or run N verifiers, each pinned to one `jwksUri` + a fixed `issuer`). A single shared JWKS holding multiple services' keys does **not** contain compromise: `jose` resolves the signing key by `kid` independently of the `iss` claim, so a token claiming `iss: "B"` signed with A's key (header `kid: kid_A`) is accepted against a shared keyset. Without issuer-binding, (b) is **weaker than an honest shared secret** because it advertises containment it does not deliver. The framework ships only the **verification** primitive; key issuance/rotation/JWKS publication belong to the deployment (SPIRE / the IdP / the mesh) — Connectum adds no key-management subsystem. * **(c) `sharedSecretTrust` (documented dev-only fallback).** A single loaded secret, constant-time compared. Simplest, but **not** per-service — one compromise forges all — so it is for local/dev only and labeled as such. For non-`internal`, non-`public` methods the interceptor is a no-op. **Chain ordering is load-bearing:** the internal interceptor (and the JWT interceptor) run **before** `createProtoAuthzInterceptor` — they populate the `AuthContext` that proto-authz then consumes — i.e. `errorHandler → (jwtAuth | internalAuth) → protoAuthz → …`, not "alongside". ### 3. Inclusive composition with the existing authz model `internal` is a boolean sibling of `public` in `service_auth`/`method_auth`; roles compose through the **existing** `requires { roles, scopes }` option — there is **no** parallel `requires_identity` mechanism. The internal interceptor sets a normal `AuthContext` (subject = the service identity; roles/scopes from the trust source). `createProtoAuthzInterceptor` gains one rule so `internal` composes inclusively within its current flow: * `internal` + identity present + **no** `requires` → **allow** (an internal method with no role gate is reachable by any trusted internal caller). (Without this, an `internal`-only method falls through to the existing `default_policy: "deny"` and is wrongly rejected.) * `internal` + `requires {roles/scopes}` → fall through to the **existing** roles/scopes check against the `AuthContext` (one model, inclusive — the internal identity's roles gate the call exactly like a JWT caller's). * `internal` + **no** identity → `Unauthenticated`. `resolveMethodAuth` is extended to surface `internal`; the JWT auth interceptor skips internal methods via a new `getInternalMethods` (mirroring `getPublicMethods`). Promoting a method `public → internal` thus **removes** world-open exposure (it now requires the internal trust marker, not merely any authenticated JWT). ## Options considered 1. **New `internal` annotation + interceptor (recommended).** Auditable in the contract, distinct posture from `public`, reuses the proven trust-source machinery. Cost: a proto option + a new interceptor + docs. 2. **Document `createGatewayAuthInterceptor` for service-to-service; no new annotation.** Cheapest, but leaves the proto saying `public` -- the audit/over-exposure problem (the actual motivation) remains. 3. **mTLS-only (no annotation), authorize every call by peer identity.** Strong, but couples the framework to mTLS termination and does not express intent in the contract; many deployments terminate mTLS at the mesh sidecar, not the app. ## Consequences * **Positive:** internal calls stop masquerading as `public`; the contract is auditable; out-of-process callers (worker, scheduler, `createCatalogClient`) have a sanctioned, non-world-open path; the mesh stays the enforcement layer while the app expresses intent. * **Negative / cost:** a new proto option (additive; BSR contract update per the repo rule), a new `@connectum/auth` interceptor + tests + docs, and a migration note for examples currently using `public` for worker-internal RPCs (e.g. `car-sharing` `RecordTrip`/`EndTrip`). * **Compatibility:** purely additive (new option defaulting false, new interceptor opt-in). Existing `public` and gated behavior unchanged. ## Ratified decisions (2026-06-21) 1. **Trust source — both (a) and (b), per-service.** Ship `meshIdentityTrust` (a, production default) + `signedTokenTrust` (b, non-mesh, per-service JWT/JWKS **with mandatory issuer-bound key selection**) + `sharedSecretTrust` (c, dev-only fallback). No single static shared secret as the recommended mode. (b)'s issuer-binding is a hard security requirement (§2), not optional. 2. **Annotation surface — stay in the model.** `internal` is a boolean sibling of `public`; roles compose **inclusively** through the existing `requires {roles,scopes}` option (§3). The richer `requires_identity` is rejected. 3. **mTLS SAN reading (option c — app reads the peer cert) — DEFER.** ConnectRPC interceptors have no peer-cert access and `@connectum/core` exposes no peer-cert surface, so app-level SAN reading needs new core plumbing. In mesh deployments the sidecar terminates mTLS and forwards identity as a header (that **is** option (a)), so (a)+(b) cover the motivating worker case and the absence of (c) blocks nothing there. App-terminated mTLS without a mesh is the only case (c) would serve; deferred to a follow-up that designs the core peer-cert surface. 4. **Example migration (`car-sharing` `RecordTrip`/`EndTrip` `public` → `internal`) — separate follow-up**, not part of this ADR. The ADR stays purely additive. ## Implementation notes * `connectum/auth/v1/options.proto`: add `internal` to `ServiceAuth`/`MethodAuth` — additive; the auth proto path is already in `release.yml`'s "Check proto changes" list (confirm at implementation). * New `@connectum/auth` exports: `createInternalAuthInterceptor`, the three trust-source factories, `getInternalMethods`; extend `resolveMethodAuth` + `createProtoAuthzInterceptor` (the one inclusive rule of §3). * Tests: per-service containment (the issuer-bound (b) MUST reject A-signed-as-B — the empirical case from §2), the inclusive role composition, and the `internal`-no-identity → `Unauthenticated` path. --- --- url: /en/contributing/adr/030-openapi-authz-generation.md --- # ADR-030: OpenAPI generation with proto-authz overlay ## Status Accepted -- 2026-06-23 (reference pattern shipped in the `car-sharing` example; a framework-level CLI command is a follow-up) Extends [ADR-024](./024-auth-authz-strategy.md) (auth/authz strategy) and [ADR-029](./029-internal-service-to-service-auth.md) (the `internal` marker). ## Context Connectum services speak gRPC/Connect, but their contract often has to be consumed by audiences that do not: REST/HTTP clients, API gateways, Swagger UI, client/SDK generators, and external API catalogs. The lingua franca for those is an **OpenAPI** document. A generator already exists -- [`protoc-gen-connect-openapi`](https://github.com/sudorandom/protoc-gen-connect-openapi) (available as the buf remote plugin `buf.build/community/sudorandom-connect-openapi`) -- and it produces a faithful OpenAPI v3.1 description of the Connect API surface (paths, request/response schemas, `connectrpc` framing). But it is **blind to Connectum's authorization model**. Connectum expresses authz as proto options consumed at runtime by `createProtoAuthzInterceptor` ([ADR-024](./024-auth-authz-strategy.md)): * `service_auth` / `method_auth` with `public`, `requires { roles, scopes }`, `policy`, `default_policy`; * and, from [ADR-029](./029-internal-service-to-service-auth.md), the `internal` marker. So a bare OpenAPI document says nothing about which operations need a token, which roles/scopes they demand, or which are intentionally world-open. A consumer reading the published contract would not know how to call the secured methods, and an auditor could not see the security posture from the document. Worse, if the security section were filled in by hand, it would be a **second source of truth** that drifts from what the interceptor actually enforces. The question this ADR settles: *how should an OpenAPI contract be produced so that it reflects Connectum authz without introducing drift, and at what layer of the project should that live right now?* ## Decision Generate OpenAPI in **two decoupled steps**, and ship it as a **reference pattern in the `car-sharing` example** rather than as a framework feature -- for now. ### 1. Base spec — buf remote plugin A dedicated buf template (`buf.gen.openapi.yaml`, separate from the offline `buf.gen.yaml`) runs `protoc-gen-connect-openapi` to emit OpenAPI v3.1 under `openapi/`. Keeping it in its own template means the **network-dependent remote plugin never runs during the offline `buf:generate`** (TS codegen and the test suite stay offline and deterministic). ### 2. Authz overlay — one resolver, no drift A post-processor (`scripts/openapi-authz.ts`) reads the proto authz options through **`resolveMethodAuth`** from `@connectum/auth/proto` -- *the same reader `createProtoAuthzInterceptor` uses at runtime* -- and patches each operation in the generated document. One resolver drives **both** runtime enforcement and the published contract, so the two cannot disagree. The mapping from Connectum authz to OpenAPI: | Connectum authz (proto) | `resolveMethodAuth` result | OpenAPI patch on the operation | |---|---|---| | `public: true` | `auth.public === true` | `security: []` (explicitly open) + `x-connectum-public: true` | | gated (default / `requires` / `policy`) | `auth.public === false` | `security: [{ bearerAuth: [] }]` | | `requires { roles: [...] }` | `auth.requires.roles` | `x-connectum-required-roles: [...]` | | `requires { scopes: [...] }` | `auth.requires.scopes` | `x-connectum-required-scopes: [...]` | | `internal: true` (1.1.0, [ADR-029](./029-internal-service-to-service-auth.md)) | `auth.internal === true` | `x-internal: true` | A single `bearerAuth` (`http`/`bearer`/JWT) security scheme is added to `components.securitySchemes`, matching the gateway's `createJwtAuthInterceptor` contract. `x-connectum-*` are vendor extensions: they are advisory metadata for humans, gateways, and catalogs (the wire enforcement remains the interceptor's job), while `security` is standard OpenAPI that off-the-shelf tooling already understands. ### 3. Layer — example-level reference pattern, not a shipped CLI The whole pattern is **example code plus codegen config**. It reads proto and works against the **published `@connectum/auth` 1.0.0** -- it does **not** modify any published package. A first-class `connectum openapi` CLI command that generalises the overlay across arbitrary services is recorded as a **follow-up**, deferred because the generic mechanism is not yet validated and would touch published packages. ## Consequences ### Positive * **Single source of truth.** Authz is declared once in proto; the runtime interceptor and the published OpenAPI both derive from it via the same resolver -- no drift by construction. * **Audit-friendly contract.** Reviewers and external consumers see, per operation, whether a token is required and which roles/scopes it demands, in standard OpenAPI plus explicit extensions. * **Off-the-shelf tooling.** The `security` requirements and `bearerAuth` scheme are consumed as-is by Swagger UI, client generators, and gateways. * **Offline build stays offline.** The remote plugin is isolated to its own template, so `buf:generate`/tests do not gain a network dependency. * **No published-package risk.** Shipping it as an example proves the pattern end-to-end before committing the framework to an API. ### Negative * **Network dependency for generation.** `pnpm openapi` invokes a buf remote plugin; it is not usable fully offline (mitigated by committing the generated `openapi/*.yaml` as the showcase output). * **Streaming RPCs get no operation** in the base spec unless the plugin's `with-streaming` opt is set (OpenAPI's request/response model does not fit server-/client-/bidi-streaming; an inherent limitation, called out in the guide). * **Not yet a framework feature.** Each service that wants this today copies the example's overlay; the reusable CLI is still a follow-up. * **Vendor extensions are advisory.** `x-connectum-*` document intent but do not themselves enforce anything; enforcement remains the interceptor's responsibility. ## Alternatives Considered * **Hand-write the OpenAPI security sections.** Rejected: a second source of truth that drifts from the interceptor -- exactly the failure this ADR avoids. * **Ship a `connectum openapi` CLI command now.** Deferred: the generic, multi-service mechanism is unvalidated, and baking it into a published package before the pattern is proven would be premature. Kept as a follow-up. * **Rely solely on the plugin's `google.api.http` / security handling.** Insufficient: the plugin understands standard annotations, not Connectum's `connectum.auth.v1` options, so it cannot derive `security` from our authz model. * **[`protodocs`](https://github.com/sudorandom/protodocs) for a docs site.** Deferred: the author states it is not yet ready for use; revisit when it stabilises. ## References * Reference implementation: the `car-sharing` example (`buf.gen.openapi.yaml`, `scripts/openapi-authz.ts`, committed `openapi/*.yaml`). * Guide: [OpenAPI](/en/guide/openapi). * [ADR-024: Auth/Authz Strategy](./024-auth-authz-strategy.md), [ADR-029: Internal Service-to-Service Auth](./029-internal-service-to-service-auth.md). * [`protoc-gen-connect-openapi`](https://github.com/sudorandom/protoc-gen-connect-openapi). --- --- url: /en/reference.md description: >- Find Connectum modules, exact API symbols, compatibility information, and migration guidance. --- # API and Reference Reference documentation currently describes the **{{ site.documentedVersion }}** release line. Historical version switching is not available yet; compare this line with the versions installed in your application before applying an option or migration. ## Find the right surface ## Common exact lookups * [`CreateServerOptions`](/en/api/@connectum/core/types/interfaces/CreateServerOptions) * [`JwtAuthInterceptorOptions`](/en/api/@connectum/auth/interfaces/JwtAuthInterceptorOptions) * [`DefaultInterceptorOptions`](/en/api/@connectum/interceptors/defaults/interfaces/DefaultInterceptorOptions) * [`EventBusOptions`](/en/api/@connectum/events/types/interfaces/EventBusOptions) * [`OtelInterceptorOptions`](/en/api/@connectum/otel/interfaces/OtelInterceptorOptions) ## Browse by module --- --- 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 | | 026 | [EventBus Architecture](/en/contributing/adr/026-eventbus-architecture) | 2026-03-07 | Proto-first EventBus with pluggable broker adapters | | 027 | [External Contracts vs EventBus](/en/contributing/adr/027-external-contracts-vs-eventbus) | 2026-06-12 | External contracts at adapter layer; EventBus stays protobuf-only; remove `sync` | | 028 | [Service Catalog](/en/contributing/adr/028-service-catalog) | 2026-06-15 | Declarative `ctx.call`/`ctx.stream`, `defineService`, sync `RemoteResolver`, split error model, buf codegen | | 029 | [Internal Service-to-Service Auth](/en/contributing/adr/029-internal-service-to-service-auth) | 2026-06-21 | First-class `internal` marker distinct from `public`; per-service trust-source interceptor (mesh identity / issuer-bound JWKS) for worker/out-of-process callers | | 030 | [OpenAPI generation with proto-authz overlay](/en/contributing/adr/030-openapi-authz-generation) | 2026-06-23 | Generate OpenAPI v3.1 (buf remote plugin) + overlay reading the same `resolveMethodAuth` the runtime uses → contract reflects authz, no drift; reference pattern in `car-sharing`, framework CLI deferred | ## 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\_CONNECTUM\_TRANSPORT](variables/ATTR_CONNECTUM_TRANSPORT.md) * [ATTR\_CONNECTUM\_TRANSPORT\_METRIC](variables/ATTR_CONNECTUM_TRANSPORT_METRIC.md) * [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) * [CONNECTUM\_INTERNAL\_TRANSPORT\_HEADER](variables/CONNECTUM_INTERNAL_TRANSPORT_HEADER.md) * [CONNECTUM\_INTERNAL\_TRANSPORT\_IN\_PROCESS](variables/CONNECTUM_INTERNAL_TRANSPORT_IN_PROCESS.md) * [RPC\_MESSAGE\_EVENT](variables/RPC_MESSAGE_EVENT.md) * [RPC\_SYSTEM\_CONNECT\_RPC](variables/RPC_SYSTEM_CONNECT_RPC.md) --- --- url: /en/guide/auth.md description: >- Choose authentication, authorization, propagation, and testing paths for a Connectum service. --- # Auth and Authz Authentication establishes an `AuthContext`; authorization decides whether that identity may invoke a method. Keep those decisions separate in the interceptor chain even when they are configured together. ## Choose the trust boundary | Boundary | Start here | |---|---| | Clients send bearer JWTs directly | [JWT authentication](/en/guide/auth/jwt) | | A trusted gateway verifies credentials first | [Gateway authentication](/en/guide/auth/gateway) | | A web application resolves a session | [Session authentication](/en/guide/auth/session) | | Internal services attach outgoing identity | [Client interceptors](/en/guide/auth/client-interceptors) | | Handlers need the authenticated identity | [Auth context](/en/guide/auth/context) | ## Choose authorization ownership Use [proto-based authorization](/en/guide/auth/proto-authz) when access policy belongs with the RPC contract. It keeps public methods, roles, scopes, and the runtime resolver on the same generated descriptor. Use [code-based authorization](/en/guide/auth/authorization) for dynamic rules or legacy services whose policy cannot live in proto options. ```typescript interceptors: [ createErrorHandlerInterceptor(), createJwtAuthInterceptor({ jwksUri, skipMethods }), createProtoAuthzInterceptor({ defaultPolicy: 'deny' }), ...createDefaultInterceptors({ errorHandler: false }), ] ``` The security invariant is `error handling → authentication → authorization → remaining behavior`. Public-method discovery and exact factory fields are deliberately not duplicated here; use the focused guide and generated interfaces such as [`JwtAuthInterceptorOptions`](/en/api/@connectum/auth/interfaces/JwtAuthInterceptorOptions). ## Learn, configure, inspect * Learn the model here and in [ADR-024](/en/contributing/adr/024-auth-authz-strategy). * Configure a concrete trust boundary in the focused guides above. * Use the [`@connectum/auth` module hub](/en/packages/auth) and [generated API](/en/api/@connectum/auth/) for exact symbols. --- --- 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'`) | | `expiresAt` | `Date \| undefined` | Credential expiration time, when available | ## 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, }); ``` To filter which claims are propagated in the `x-auth-claims` header, use the `propagatedClaims` option. It is available on `createAuthInterceptor` (generic) and `createSessionAuthInterceptor` -- not on `createJwtAuthInterceptor`: ```typescript const sessionAuth = createSessionAuthInterceptor({ verifySession: (token, headers) => auth.api.getSession({ headers }), mapSession: (session) => ({ /* ... */ }), 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` | JSON-encoded roles array | | `x-auth-scopes` | Space-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 ::: runtime bun Import the runner from `bun:test` instead of `node:test` and run the file with `bun test`. Everything else is identical -- `node:assert` works under Bun. ::: ```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: ```mermaid flowchart LR Error[errorHandler] --> Authn[AUTH] Authn --> Authz[AUTHZ] Authz --> Timeout[timeout] Timeout --> Bulkhead[bulkhead] Bulkhead --> Breaker[circuitBreaker] Breaker --> Retry[retry] Retry --> More["..."] ``` 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: [ createErrorHandlerInterceptor(), jwtAuth, // immediately after errorHandler authz, // immediately after authentication ...createDefaultInterceptors({ errorHandler: false }), ], }); ``` ## Full Example ```typescript import { createServer } from '@connectum/core'; import { createDefaultInterceptors, createErrorHandlerInterceptor } 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], // Recommended order: errorHandler -> AUTH -> AUTHZ -> rest. interceptors: [ createErrorHandlerInterceptor(), jwtAuth, authz, ...createDefaultInterceptors({ errorHandler: false }), ], }); 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/quickstart.md description: >- Create a typed service, generate its proto contract, run it, and verify a successful RPC. --- # Build Your First Connectum Service Build your first service from a new project to one successful RPC. You will define a proto contract, generate TypeScript, implement a handler, start a Connectum server, and verify both the happy path and request validation. **Outcome:** a runnable Greeter service with health checks, reflection, graceful shutdown, error handling, and proto validation. Production capabilities such as TLS, authentication, observability, and resilience are focused next steps rather than prerequisites for your first call. ::: tip Prefer a scaffold? `connectum init` downloads the version-pinned official `getting-started` base and can add selected modules. Follow [Scaffolding a Service](/en/guide/scaffolding) for that route. Continue here when you want to understand each file yourself. ::: ## Prerequisites ::: runtime \== node * **Node.js >= 25.2.0** -- native TypeScript via [type stripping](https://nodejs.org/api/typescript.html) * **pnpm >= 11** -- `corepack enable && corepack prepare pnpm@latest --activate` * **buf** -- installed automatically via `@bufbuild/buf` npm package \== bun * **Bun >= 1.3.6** -- TypeScript runs natively, no loader needed * **buf** -- installed automatically via `@bufbuild/buf` npm package ::: :::: runtime node ::: 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 >= 22.13.0**. See [Runtime Support](/en/guide/typescript/runtime-support). ::: :::: ::: runtime bun Bun executes your TypeScript sources directly, and `@connectum/*` packages ship compiled JavaScript, so nothing else is required. See [Runtime Compatibility](/en/guide/runtime-compatibility) for the current support level. ::: ## 1. Project Setup ```bash mkdir greeter-service && cd greeter-service ``` ::: pm \== npm ```bash npm init -y ``` \== pnpm ```bash pnpm init ``` \== bun ```bash bun init -y -m ``` ::: The Bun form passes `-m` so `bun init` writes only `package.json` and `tsconfig.json` instead of also scaffolding a sample entry point and README. Install dependencies: ::: pm \== npm ```bash # Core framework npm install @connectum/core @connectum/healthcheck @connectum/reflection @connectum/interceptors # ConnectRPC runtime npm install @connectrpc/connect @connectrpc/connect-node @bufbuild/protobuf # Validation (recommended: @connectrpc/validate) npm install @bufbuild/protovalidate @connectrpc/validate # Dev dependencies (buf + code generation) npm install -D typescript @types/node @bufbuild/buf @bufbuild/protoc-gen-es ``` \== pnpm ```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 ``` \== bun ```bash # Core framework bun add @connectum/core @connectum/healthcheck @connectum/reflection @connectum/interceptors # ConnectRPC runtime bun add @connectrpc/connect @connectrpc/connect-node @bufbuild/protobuf # Validation (recommended: @connectrpc/validate) bun add @bufbuild/protovalidate @connectrpc/validate # Dev dependencies (buf + code generation) bun add -d typescript @types/node @bufbuild/buf @bufbuild/protoc-gen-es ``` ::: Configure `package.json`: ::: runtime \== node ```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": ">=22.13.0" } } ``` \== bun ```json { "name": "greeter-service", "version": "1.0.0", "type": "module", "imports": { "#gen/*": "./gen/*", "#*": "./src/*" }, "scripts": { "start": "bun src/index.ts", "dev": "bun --watch src/index.ts", "typecheck": "bunx tsc --noEmit", "build:proto": "buf generate proto" } } ``` ::: Create `tsconfig.json` (type checking only -- no compilation): ```json { "compilerOptions": { "noEmit": true, "target": "esnext", "module": "nodenext", "moduleResolution": "nodenext", "allowImportingTsExtensions": 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: ::: pm \== npm ```bash npx buf dep update ``` \== pnpm ```bash pnpm exec buf dep update ``` \== bun ```bash bunx 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: ::: pm \== npm ```bash npm run build:proto ``` \== pnpm ```bash pnpm run build:proto ``` \== bun ```bash bun 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-enums). ::: ## 4. Service Handler Create `src/services/greeterService.ts`: ```typescript import { create } from '@bufbuild/protobuf'; import { defineService } from '@connectum/core'; import { GreeterService, SayHelloResponseSchema } from '#gen/greeter_pb.ts'; import type { SayHelloRequest } from '#gen/greeter_pb.ts'; export const greeterService = defineService(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 { greeterService } from './services/greeterService.ts'; const server = createServer({ services: [greeterService], 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 ::: runtime \== node ```bash # Node.js 25+ (native TypeScript) pnpm dev # tsx (Node.js 22+) npx tsx src/index.ts ``` \== bun ```bash bun --watch 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 | | **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 | Resilience interceptors (timeout, bulkhead, circuit breaker, retry) are **opt-in**. Add them after the first call works by following the [built-in interceptor guide](/en/guide/interceptors/built-in). ## 7. Verify Validation {#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 is required: proto constraints are validated before your handler runs. See [Validation](/en/guide/validation) for constraint setup and failure handling. ## Next Steps Your first service is complete. Choose the next task; none is required to finish this tutorial. | Goal | Continue with | |---|---| | Protect traffic | [TLS and mTLS](/en/guide/security), then [authentication and authorization](/en/guide/auth) | | Observe the service | [Tracing, metrics, and logging](/en/guide/observability) | | Tune lifecycle behavior | [Graceful Shutdown](/en/guide/server/graceful-shutdown) | | Add resilience deliberately | [Built-in Interceptor Chain](/en/guide/interceptors/built-in) | | Sync reflected contracts | [Server Reflection](/en/guide/protocols/reflection) and [`@connectum/cli`](/en/packages/cli) | | Call another service | [Choosing a Communication Mechanism](/en/guide/service-communication/choosing-a-mechanism) | | Test the service | [Testing](/en/guide/testing) | | Deploy it | [Docker](/en/guide/production/docker) and [Kubernetes](/en/guide/production/kubernetes) | ## Troubleshooting * Generation fails: review the `buf.yaml`, `buf.gen.yaml`, and import extension, then see [TypeScript and proto generation](/en/guide/typescript). * The server starts but calls fail: confirm port, plaintext/TLS mode, and the fully-qualified service name with [Server Reflection](/en/guide/protocols/reflection). * Node.js or Bun behaves differently: use the canonical [Runtime Compatibility](/en/guide/runtime-compatibility) matrix. --- --- url: /en/guide/events/getting-started.md description: >- Publish and consume one typed event through a lifecycle-managed Connectum EventBus. --- # Getting Started with Events This guide walks you through setting up event-driven communication between Connectum microservices using the EventBus. You finish with one published event handled successfully; broker selection and middleware tuning are optional next steps. ## Prerequisites * A working Connectum project with `@connectum/core` * Proto tooling configured (`buf` for protobuf generation) * A message broker (or use `MemoryAdapter` for local development) ## Step 1: Install Packages Install `@connectum/events` plus the adapter for your broker: | Broker | Adapter package | |---|---| | NATS JetStream | `@connectum/events-nats` | | Kafka / Redpanda | `@connectum/events-kafka` | | Redis Streams / Valkey | `@connectum/events-redis` | | AMQP / RabbitMQ | `@connectum/events-amqp` | | Memory (testing) | built in -- no extra package | The commands below install the NATS adapter; substitute the package from the table for another broker, or drop it entirely to start with the in-memory adapter. ::: pm \== npm ```bash npm install @connectum/events @connectum/events-nats ``` \== pnpm ```bash pnpm add @connectum/events @connectum/events-nats ``` \== bun ```bash bun add @connectum/events @connectum/events-nats ``` ::: ## Step 2: Define Proto Event Handlers Create a proto service that defines your event handlers. Each RPC method represents a handler for one event type. The input message is the event payload; the return type should be `google.protobuf.Empty`. ```protobuf // proto/notifications/v1/events.proto syntax = "proto3"; package notifications.v1; import "google/protobuf/empty.proto"; // Events published by the User service message UserCreated { string id = 1; string email = 2; string name = 3; } message UserDeleted { string id = 1; } // Event handler service for the Notification service service NotificationEventHandlers { rpc OnUserCreated(UserCreated) returns (google.protobuf.Empty); rpc OnUserDeleted(UserDeleted) returns (google.protobuf.Empty); } ``` Generate the TypeScript code: ```bash pnpm run build:proto ``` ## Step 3: Create an Adapter Create the adapter instance with broker-specific configuration: ::: code-group ```typescript [NATS JetStream] import { NatsAdapter } from '@connectum/events-nats'; const adapter = NatsAdapter({ servers: process.env.NATS_URL ?? 'nats://localhost:4222', stream: 'notifications', }); ``` ```typescript [Kafka / Redpanda] import { KafkaAdapter } from '@connectum/events-kafka'; const brokers = (process.env.KAFKA_BROKERS ?? 'localhost:9092').split(','); const adapter = KafkaAdapter({ brokers, clientId: 'notification-service', }); ``` ```typescript [Redis Streams] import { RedisAdapter } from '@connectum/events-redis'; const adapter = RedisAdapter({ url: process.env.REDIS_URL ?? 'redis://localhost:6379', }); ``` ```typescript [AMQP / RabbitMQ] import { AmqpAdapter } from '@connectum/events-amqp'; const adapter = AmqpAdapter({ url: process.env.AMQP_URL ?? 'amqp://localhost:5672', }); ``` ```typescript [Memory] import { MemoryAdapter } from '@connectum/events'; const adapter = MemoryAdapter(); ``` ::: ## Step 4: Implement Event Handlers Define typed event handlers using the `EventRoute` pattern. This mirrors ConnectRPC's router registration: ```typescript // src/events/notificationEvents.ts import type { EventRoute } from '@connectum/events'; import { NotificationEventHandlers } from '#gen/notifications/v1/events_pb.js'; export const notificationEvents: EventRoute = (events) => { events.service(NotificationEventHandlers, { onUserCreated: async (msg, ctx) => { console.log(`Sending welcome email to ${msg.email}`); // ... send email logic await ctx.ack(); }, onUserDeleted: async (msg, ctx) => { console.log(`Cleaning up notifications for user ${msg.id}`); // ... cleanup logic await ctx.ack(); }, }); }; ``` ::: tip Acknowledgment Successful handler completion auto-acks the event. The [Events concept guide](/en/guide/events#eventcontext) owns acknowledgment and metadata semantics. ::: ## Step 5: Create the EventBus Wire everything together with `createEventBus()`: ```typescript // src/eventBus.ts import { createEventBus } from '@connectum/events'; import { NatsAdapter } from '@connectum/events-nats'; import { notificationEvents } from './events/notificationEvents.js'; const adapter = NatsAdapter({ servers: process.env.NATS_URL ?? 'nats://localhost:4222', stream: 'notifications', }); export const eventBus = createEventBus({ adapter, routes: [notificationEvents], group: 'notification-service', handlerTimeout: 30_000, // Per-event handler timeout (default: 30s) drainTimeout: 15_000, // Wait up to 15s for in-flight handlers on shutdown middleware: { retry: { maxRetries: 3, backoff: 'exponential' }, dlq: { topic: 'notification-service.dlq' }, }, }); ``` ## Step 6: Integrate with Connectum Server Pass the EventBus to `createServer()` for automatic lifecycle management: ```typescript // src/index.ts import { createServer } from '@connectum/core'; import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck'; import { eventBus } from './eventBus.js'; import { routes } from './services/routes.js'; const server = createServer({ services: [routes], eventBus, protocols: [Healthcheck({ httpEnabled: true })], shutdown: { autoShutdown: true }, }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); console.log(`Notification service ready on port ${server.address?.port}`); }); await server.start(); ``` The server now owns the EventBus lifecycle. See the [Events concept guide](/en/guide/events#architecture) for that lifecycle boundary. ## Step 7: Publish Events From another service (e.g., User Service), publish typed events: ```typescript import { createEventBus } from '@connectum/events'; import { KafkaAdapter } from '@connectum/events-kafka'; import { UserCreatedSchema } from '#gen/notifications/v1/events_pb.js'; const eventBus = createEventBus({ adapter: KafkaAdapter({ brokers: ['localhost:9092'], clientId: 'user-service' }), group: 'user-service', }); await eventBus.start(); // Publish a typed event -- serialized as protobuf, routed by schema.typeName await eventBus.publish(UserCreatedSchema, { id: '123', email: 'alice@example.com', name: 'Alice', }); ``` The topic defaults to the message's `typeName` (e.g., `notifications.v1.UserCreated`). See [Custom Topics](/en/guide/events/custom-topics) to override this. ## Full Working Example Here is a minimal two-service setup using the MemoryAdapter for local testing: ```typescript import { createEventBus, MemoryAdapter } from '@connectum/events'; import type { EventRoute } from '@connectum/events'; import { NotificationEventHandlers, UserCreatedSchema, } from '#gen/notifications/v1/events_pb.js'; // Shared in-memory adapter (for testing only) const adapter = MemoryAdapter(); // Consumer: Notification Service const notificationEvents: EventRoute = (events) => { events.service(NotificationEventHandlers, { onUserCreated: async (msg, ctx) => { console.log(`Welcome email sent to ${msg.email}`); await ctx.ack(); }, onUserDeleted: async (msg, ctx) => { console.log(`Notifications cleaned for user ${msg.id}`); await ctx.ack(); }, }); }; const consumerBus = createEventBus({ adapter, routes: [notificationEvents], group: 'notification-service', }); // Producer: User Service const producerBus = createEventBus({ adapter }); await consumerBus.start(); await producerBus.start(); // Publish — the consumer handler fires synchronously with MemoryAdapter await producerBus.publish(UserCreatedSchema, { id: '1', email: 'alice@example.com', name: 'Alice', }); // Output: "Welcome email sent to alice@example.com" await consumerBus.stop(); await producerBus.stop(); ``` ## Next Steps * [Custom Topics](/en/guide/events/custom-topics) -- override default topic naming via proto options * [Middleware](/en/guide/events/middleware) -- configure retry, DLQ, and write custom middleware * [Adapters](/en/guide/events/adapters) -- choose the right broker adapter for your deployment * [with-events-redpanda](https://github.com/Connectum-Framework/examples/tree/main/with-events-redpanda) -- full saga pattern example * [with-events-dlq](https://github.com/Connectum-Framework/examples/tree/main/with-events-dlq) -- DLQ example with NATS JetStream --- --- url: /en/guide/interceptors/built-in.md --- # Built-in Interceptors Connectum provides 8 production-ready interceptors via `createDefaultInterceptors()`. They form a fixed-order chain that covers error handling, resilience, validation, and serialization. ## The Default Chain ```mermaid flowchart LR Error[errorHandler] --> Timeout[timeout] Timeout --> Bulkhead[bulkhead] Bulkhead --> Breaker[circuitBreaker] Breaker --> Retry[retry] Retry --> Fallback[fallback] Fallback --> Validation[validation] Validation --> Serializer[serializer] ``` | # | Interceptor | Purpose | Default | |---|-------------|---------|---------| | 1 | **errorHandler** | Normalizes errors into `ConnectError` | Enabled | | 2 | **timeout** | Limits request execution time | **Opt-in** (30s when enabled) | | 3 | **bulkhead** | Limits concurrent requests | **Opt-in** (capacity 10, queue 10 when enabled) | | 4 | **circuitBreaker** | Prevents cascading failures (outbound pattern, see below) | **Opt-in** (threshold 5 when enabled) | | 5 | **retry** | Retries transient failures with exponential backoff | **Opt-in** (3 retries when enabled) | | 6 | **fallback** | Graceful degradation | **Opt-in** (requires a handler) | | 7 | **validation** | Validates via `@connectrpc/validate` | Enabled | | 8 | **serializer** | JSON serialization for protobuf | **Opt-in** | The order is deliberate: `errorHandler` is outermost (catches everything), `serializer` is innermost (closest to the handler). The order applies to whichever interceptors you enable. In particular, `circuitBreaker` wraps `retry`, so one logical request increments the failure counter at most once regardless of retry attempts. ::: warning No hidden behavioral logic Only structural interceptors (errorHandler, validation) are enabled by default. Resilience interceptors (timeout, bulkhead, circuitBreaker, retry) alter request behavior and must be enabled explicitly with `true` or an options object — implicitly enabled resilience caused a confirmed production incident (a server-side circuit breaker tripped by expected business errors). ::: ## Circuit Breaker: Placement and Error Classification The circuit breaker is an **outbound/client-side pattern**: it protects the caller from a sick upstream (fail fast instead of waiting on timeouts) and gives that upstream room to recover. On a server's inbound stack it degenerates into error-rate load shedding — for inbound protection prefer explicit `timeout` + `bulkhead`. ```typescript // Recommended: circuit breaker on an outbound client transport import { createConnectTransport } from '@connectrpc/connect-node'; import { createCircuitBreakerInterceptor } from '@connectum/interceptors'; const transport = createConnectTransport({ baseUrl: 'http://upstream:5000', interceptors: [ createCircuitBreakerInterceptor({ threshold: 5, halfOpenAfter: 30_000 }), ], }); ``` **Error classification.** By default only infrastructure errors count as circuit failures: `Unknown`, `DeadlineExceeded`, `Internal`, `Unavailable`, `DataLoss`, `ResourceExhausted` (plus any non-`ConnectError` thrown value). Business codes (`invalid_argument`, `not_found`, `failed_precondition`, `already_exists`, ...) are expected responses of a healthy service: they never open the breaker, and in half-open state they close it. Customize with `failurePredicate(error, defaultPredicate)` — the default predicate (exported as `defaultFailurePredicate`) is passed in for composition: ```typescript import { Code, ConnectError } from '@connectrpc/connect'; import { createCircuitBreakerInterceptor } from '@connectum/interceptors'; // Exclude upstream per-client rate limits from tripping the breaker createCircuitBreakerInterceptor({ failurePredicate: (err, def) => def(err) && !(err instanceof ConnectError && err.code === Code.ResourceExhausted), }); // Restore legacy behavior (every error trips the breaker) createCircuitBreakerInterceptor({ failurePredicate: () => true }); ``` ::: tip When to enable the serializer Enable the serializer when your service uses the **Connect protocol** (HTTP/1.1 JSON) and you need automatic protobuf ↔ JSON conversion. Not needed for pure **gRPC** services (binary protobuf format). ```typescript // Connect protocol service with JSON responses — enable serializer const interceptors = createDefaultInterceptors({ serializer: true, }); // gRPC service (binary protobuf) — serializer not needed (default) const interceptors = createDefaultInterceptors(); // Custom serializer options const interceptors = createDefaultInterceptors({ serializer: { alwaysEmitImplicit: true, ignoreUnknownFields: false, }, }); ``` ::: ## 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. Pass `true` or an options object to enable an opt-in interceptor; set one of the default-enabled interceptors to `false` to disable it: ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; const interceptors = createDefaultInterceptors({ timeout: { duration: 10_000 }, // Enable timeout (10s) bulkhead: { capacity: 20, queueSize: 20 }, // Enable bulkhead with custom limits // errorHandler and validation remain enabled by default }); 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: ```mermaid sequenceDiagram participant Client participant I1 as interceptor1 participant I2 as interceptor2 participant I3 as interceptor3 participant Handler Client->>I1: Request I1->>I2: Request I2->>I3: Request I3->>Handler: Request Handler-->>I3: Response I3-->>I2: Response I2-->>I1: Response I1-->>Client: Response ``` 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/guide/events/adapters.md description: >- Compare Connectum event brokers, choose one for the workload, and reach its exact configuration API. --- # Choose an Event Adapter Every Connectum EventBus uses the same `EventAdapter` contract. Choose a broker from workload and operational requirements, then keep broker-specific tuning in that adapter's generated API reference. ## Broker Selection Matrix {#adapter-comparison} | Adapter | Persistence | Consumer coordination | Ordering scope | Best starting fit | |---|---|---|---|---| | Memory | No | None | Publish call | Unit tests and local prototypes | | NATS JetStream | Yes | Durable consumers | Subject | Lightweight, low-latency service events | | Kafka / Redpanda | Yes | Native consumer groups | Partition | High-throughput streams and retained logs | | Redis Streams / Valkey | Configurable by Redis | Consumer groups | Stream | Teams already operating Redis-compatible infrastructure | | AMQP / RabbitMQ / LavinMQ | Durable queues/exchanges when configured | Competing consumers | Queue | Routing topologies and external AMQP contracts | All production adapters implement at-least-once delivery semantics. Handlers must be idempotent and acknowledge only after their side effects are complete. Broker configuration determines actual durability and retention. ## Memory {#memory-adapter} `MemoryAdapter()` ships with `@connectum/events`. It has no external dependency, persistence, or consumer groups and is intended for tests and local development. ```typescript import { MemoryAdapter } from '@connectum/events'; const adapter = MemoryAdapter(); ``` ## NATS JetStream {#nats-jetstream-adapter} Choose NATS for durable subjects, wildcard routing, and a compact operational footprint. ```typescript import { NatsAdapter } from '@connectum/events-nats'; const adapter = NatsAdapter({ servers: 'nats://localhost:4222' }); ``` * [Module hub](/en/packages/events-nats) * [`NatsAdapterOptions`](/en/api/@connectum/events-nats/types/interfaces/NatsAdapterOptions) ## Kafka or Redpanda {#kafka-adapter} Choose Kafka-compatible infrastructure for partitioned ordering, retained logs, and high-throughput stream processing. ```typescript import { KafkaAdapter } from '@connectum/events-kafka'; const adapter = KafkaAdapter({ brokers: ['localhost:9092'], clientId: 'orders-service', }); ``` * [Module hub](/en/packages/events-kafka) * [`KafkaAdapterOptions`](/en/api/@connectum/events-kafka/types/interfaces/KafkaAdapterOptions) * [Redpanda example](https://github.com/Connectum-Framework/examples/tree/main/with-events-redpanda) ## Redis Streams or Valkey {#redis-streams-adapter} Choose Redis Streams when the team already operates Redis-compatible infrastructure and needs stream consumer groups without a separate broker stack. ```typescript import { RedisAdapter } from '@connectum/events-redis'; const adapter = RedisAdapter({ url: 'redis://localhost:6379' }); ``` * [Module hub](/en/packages/events-redis) * [`RedisAdapterOptions`](/en/api/@connectum/events-redis/types/interfaces/RedisAdapterOptions) ## AMQP or RabbitMQ {#amqp--rabbitmq-adapter} Choose AMQP for exchange/queue topology, competing consumers, or integration with an externally governed AMQP contract. ```typescript import { AmqpAdapter } from '@connectum/events-amqp'; const adapter = AmqpAdapter({ url: 'amqp://localhost:5672', exchange: 'events', exchangeType: 'topic', }); ``` * [Module hub](/en/packages/events-amqp) * [`AmqpAdapterOptions`](/en/api/@connectum/events-amqp/types/interfaces/AmqpAdapterOptions) * [AMQP example](https://github.com/Connectum-Framework/examples/tree/main/with-events-amqp) ## Automatic Client Identification When an adapter-specific client or connection name is not supplied, the EventBus derives a service identifier from registered proto service names and the host. An explicit adapter option always wins. Use explicit names when broker ACLs, dashboards, or support procedures depend on stable identifiers. ## Custom Adapters {#eventadapter-interface} Implement the generated [`EventAdapter`](/en/api/@connectum/events/types/interfaces/EventAdapter) contract when a broker is not covered. Keep serialization, connection lifecycle, subscription cancellation, acknowledgement behavior, and error semantics explicit. ## Next Steps * [Publish and Subscribe](/en/guide/events/getting-started) * [Event Middleware](/en/guide/events/middleware) * [`@connectum/events`](/en/packages/events) --- --- url: /en/guide/service-communication/choosing-a-mechanism.md --- # Choosing a Communication Mechanism A microservice rarely works alone. When one service needs another, Connectum gives you **three orthogonal mechanisms** — and the hard part is not wiring any one of them, it is **picking the right one for each interaction**. They are not competitors; a single request flow often uses all three, each for the job it is best at. | Mechanism | Shape | Use it when | Connectum API | |---|---|---|---| | **`ctx.call` / `ctx.stream`** | synchronous request → response | you need the **answer now** to continue (validation, a lookup, a pre-check) | built in — the [service catalog](/en/guide/service-communication/service-catalog) | | **EventBus** | asynchronous fire-and-forget | you want to **announce a fact** and let any number of consumers react, decoupled in time | built in — [`@connectum/events`](/en/guide/events) | | **Durable saga** | long, multi-step transaction with rollback | a workflow **spans several services** and partial progress must be **compensated** on failure | the framework serves the RPCs; an external durable engine ([Temporal](https://temporal.io)) owns the orchestration | The decision is about **coupling in time** and **failure semantics**, not about performance. Ask, in order: 1. **Do I need the reply to proceed?** → `ctx.call` (synchronous). 2. **Am I just announcing that something happened?** → EventBus (fire-and-forget). 3. **Is this a multi-step transaction that must roll back as a unit?** → a durable saga. ::: tip Connectum stays thin Two of the three mechanisms ship **in the framework** (`ctx.call`, EventBus). The third — durable orchestration — is deliberately **not** reinvented: Connectum serves the RPCs and you bring a best-of-breed engine (Temporal). The [examples](#reference-examples) show all three composed in one codebase without the framework growing a workflow engine of its own. ::: ## Synchronous: `ctx.call` / `ctx.stream` Use it when the caller **cannot continue without the answer** — validating that an entity exists, reading a value, a pre-check before committing to work. The call is typed by the generated [service catalog](/en/guide/service-communication/service-catalog) and **auto-routes**: in-process when the target service is mounted locally, over the network via a [remote resolver](/en/guide/service-communication/resolvers) when it lives in another process — the **handler code is identical either way**. ```typescript // TimeOffService validates the employee before approving a leave request. // In a monolith this dispatches in-process; split across pods it goes over // the network — same line of code. const employee = await ctx.call( 'directory.v1.DirectoryService/GetEmployee', create(GetEmployeeRequestSchema, { id: req.employeeId }), ); // A Code.NotFound from the directory propagates straight back to the caller. ``` The inbound deadline and cancellation signal **cascade** to the downstream call, so a client that gives up tears down the whole chain. For request-response chains, fan-out / fan-in, and streaming, see [Communication Patterns](/en/guide/service-communication/patterns). **Trade-off:** synchronous calls **couple availability** — if the callee is down, the caller's request fails now. That is correct for a validation you cannot skip, and wrong for a notification that can wait. ## Asynchronous: the EventBus Use it when a service **announces a fact** and does not care who reacts — or whether anyone reacts yet. The publisher emits an event on a topic; subscribers consume it independently, decoupled in time and (with a broker) across processes. ```typescript // After approving the leave, TimeOffService publishes a fact and moves on — // it does not call payroll, and does not wait for it. await eventBus.publish( LeaveApprovedSchema, create(LeaveApprovedSchema, { leaveRequestId, employeeId: req.employeeId, days: req.days }), { topic: LEAVE_APPROVED_TOPIC }, ); ``` ```typescript // PayrollService subscribes to the topic and reacts on its own schedule. events.service(PayrollEventHandlers, { async onLeaveApproved(event, ctx) { decrementBalance(event.employeeId, event.days); await ctx.ack(); }, }); ``` The adapter is pluggable — an in-memory adapter for tests, NATS / Kafka / Redis / AMQP in production (see [Adapters](/en/guide/events/adapters)). The publisher and subscriber never reference each other; they agree only on the **topic**. **Trade-off:** you gain decoupling and resilience, but lose the immediate answer and the simple call-stack. There is **no return value** and **no built-in rollback** — which is exactly why a multi-step transaction needs the third tool. ## Durable: a saga with compensations Use it when a single business operation **spans several services** and partial progress is unacceptable — onboarding a hire (create the record, set up payroll, grant time off, provision access) or a trip lifecycle (reserve, record, bill, settle). Neither `ctx.call` (no durability if the process dies mid-flow) nor the EventBus (no rollback) fits. This is the **saga** pattern: run the forward steps, and on any failure run each completed step's **compensation** in reverse (LIFO) order. Connectum does **not** ship a workflow engine — it serves the RPCs and you drive the saga from a durable orchestrator. The examples use [Temporal](https://temporal.io): * The orchestration (the forward steps, the compensation stack, retries) lives in a **workflow** run by a dedicated **worker** process. The worker is the only process that loads the native Temporal addon; the RPC roles stay no-build. * Each step is an **activity** — one ordinary `ctx.call`-style RPC against a role service. A step's **business** failure (e.g. a duplicate id → `AlreadyExists`) is made **non-retryable** so the workflow fails fast with nothing to undo; transient failures keep retrying (the durability the saga buys you). * The compensations are **idempotent**, so an unwind after a partially-applied step is safe. ```mermaid flowchart LR Employee[createEmployee] --> Payroll[setupPayroll] Payroll --> TimeOff[grantTimeOff] TimeOff --> Access[provisionAccess] Access --> Activate[activate] Activate --> Complete[COMPLETED] Failure[Any activity fails] -.-> Reverse[Compensations run in reverse] Reverse --> Revoke[revoke access] Revoke --> Teardown[teardown payroll] Teardown --> Offboard[offboard employee] Offboard --> Failed[FAILED] ``` A thin **gateway** RPC starts the workflow and exposes its status, so callers see an ordinary service while the durable machinery runs behind it. The gateway can still run a **synchronous pre-check** with `ctx.call` *before* starting the workflow — so an invalid request is rejected immediately, with no durable run created. **Trade-off:** the most powerful and the most operationally heavy option — it adds an external dependency and a worker process. Reach for it only when the transaction genuinely spans services and must be atomic; a single-service mutation does not need a saga. ## Combining them The three are **complementary**, and a real flow uses each where it fits. In the HRIS reference example, one codebase runs all three: ```mermaid flowchart LR C["client"] -->|"OnboardEmployee"| G["Onboarding gateway"] G -->|"ctx.call pre-check"| D["Directory"] G -.->|"start durable saga"| W["Temporal worker"] W ==>|"activities (RPC per step)"| S["Directory · Payroll · TimeOff · Access"] TO["TimeOff"] -->|"publish LeaveApproved"| B(("EventBus")) B -->|"deliver"| P["Payroll subscriber"] ``` * **`ctx.call`** validates the new hire's id and an employee before approving leave. * The **EventBus** broadcasts `LeaveApproved`, which payroll consumes to decrement the balance. * The **durable saga** provisions the hire across four services with automatic compensation. ## Reference examples Two end-to-end examples put these mechanisms to work — clone, read, and run them: * **[car-sharing](https://github.com/Connectum-Framework/examples/tree/main/car-sharing)** — split microservices behind a JWT / proto-authz gateway, cross-service `ctx.call`, and a **durable trip saga** (reserve → record → bill → settle, with compensation), on Kubernetes + Istio. * **[hris](https://github.com/Connectum-Framework/examples/tree/main/hris)** — one codebase that runs as a monolith *or* microservices by env, demonstrating **all three** mechanisms side by side: `ctx.call` validation, an EventBus `LeaveApproved` flow, and a **durable onboarding saga**. ## Related * [Communication Patterns](/en/guide/service-communication/patterns) — request-response chains, fan-out / fan-in, streaming, error handling * [Service Catalog](/en/guide/service-communication/service-catalog) — how `ctx.call` / `ctx.stream` are typed * [Remote Resolvers](/en/guide/service-communication/resolvers) — routing a call to a remote process * [Events](/en/guide/events) — the EventBus, topics, middleware, and adapters * [Connectum Runtime Architecture](/en/guide/production/architecture) — process boundaries and local/remote routing --- --- 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) * [defaultFailurePredicate](functions/defaultFailurePredicate.md) --- --- url: /en/api/@connectum/events-amqp/classes/AmqpAdapterError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpAdapterError # Class: AmqpAdapterError Defined in: [packages/events-amqp/src/errors.ts:30](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L30) Base class for all AMQP adapter errors. ## Extends * `Error` ## Extended by * [`AmqpConnectionError`](AmqpConnectionError.md) * [`AmqpPublishNackError`](AmqpPublishNackError.md) * [`AmqpPublishTimeoutError`](AmqpPublishTimeoutError.md) * [`AmqpSerializationError`](AmqpSerializationError.md) * [`AmqpTopologyError`](AmqpTopologyError.md) * [`AmqpUnroutableError`](AmqpUnroutableError.md) ## Constructors ### Constructor > **new AmqpAdapterError**(`message`, `options?`): `AmqpAdapterError` Defined in: [packages/events-amqp/src/errors.ts:31](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L31) #### Parameters ##### message `string` ##### options? ###### cause? `unknown` #### Returns `AmqpAdapterError` #### Overrides `Error.constructor` ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from `Error.cause` *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from `Error.message` *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from `Error.name` *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from `Error.stack` *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.stackTraceLimit` ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.captureStackTrace` *** ### 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 `Error.isError` *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.prepareStackTrace` --- --- url: /en/api/@connectum/events-amqp/classes/AmqpConnectionError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpConnectionError # Class: AmqpConnectionError Defined in: [packages/events-amqp/src/errors.ts:45](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L45) Connection is absent, lost, or recovery is in progress / exhausted. Publishes during a disconnected window fail fast with this error (unless the opt-in `publishRetry` is enabled — connection-class failures are then retried in place within the AUTO-RETRY boundary, see `isAutoRetriablePublishError`); in-flight confirms are rejected with it on connection loss. ## Extends * [`AmqpAdapterError`](AmqpAdapterError.md) ## Constructors ### Constructor > **new AmqpConnectionError**(`message`, `options?`): `AmqpConnectionError` Defined in: [packages/events-amqp/src/errors.ts:31](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L31) #### Parameters ##### message `string` ##### options? ###### cause? `unknown` #### Returns `AmqpConnectionError` #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`constructor`](AmqpAdapterError.md#constructor) ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`cause`](AmqpAdapterError.md#cause) *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`message`](AmqpAdapterError.md#message) *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`name`](AmqpAdapterError.md#name) *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`stack`](AmqpAdapterError.md#stack) *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`stackTraceLimit`](AmqpAdapterError.md#stacktracelimit) ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`captureStackTrace`](AmqpAdapterError.md#capturestacktrace) *** ### 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 [`AmqpAdapterError`](AmqpAdapterError.md).[`isError`](AmqpAdapterError.md#iserror) *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`prepareStackTrace`](AmqpAdapterError.md#preparestacktrace) --- --- url: /en/api/@connectum/events-amqp/classes/AmqpPublishNackError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpPublishNackError # Class: AmqpPublishNackError Defined in: [packages/events-amqp/src/errors.ts:61](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L61) The broker negatively acknowledged (nacked) a published message. ## Extends * [`AmqpAdapterError`](AmqpAdapterError.md) ## Constructors ### Constructor > **new AmqpPublishNackError**(`message`, `options?`): `AmqpPublishNackError` Defined in: [packages/events-amqp/src/errors.ts:31](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L31) #### Parameters ##### message `string` ##### options? ###### cause? `unknown` #### Returns `AmqpPublishNackError` #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`constructor`](AmqpAdapterError.md#constructor) ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`cause`](AmqpAdapterError.md#cause) *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`message`](AmqpAdapterError.md#message) *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`name`](AmqpAdapterError.md#name) *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`stack`](AmqpAdapterError.md#stack) *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`stackTraceLimit`](AmqpAdapterError.md#stacktracelimit) ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`captureStackTrace`](AmqpAdapterError.md#capturestacktrace) *** ### 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 [`AmqpAdapterError`](AmqpAdapterError.md).[`isError`](AmqpAdapterError.md#iserror) *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`prepareStackTrace`](AmqpAdapterError.md#preparestacktrace) --- --- url: /en/api/@connectum/events-amqp/classes/AmqpPublishTimeoutError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpPublishTimeoutError # Class: AmqpPublishTimeoutError Defined in: [packages/events-amqp/src/errors.ts:68](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L68) No broker outcome (ack/nack/return/connection loss) arrived within `publishTimeoutMs`. The message state is UNKNOWN — it may or may not have been routed; an at-least-once producer should republish. ## Extends * [`AmqpAdapterError`](AmqpAdapterError.md) ## Constructors ### Constructor > **new AmqpPublishTimeoutError**(`message`, `options?`): `AmqpPublishTimeoutError` Defined in: [packages/events-amqp/src/errors.ts:31](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L31) #### Parameters ##### message `string` ##### options? ###### cause? `unknown` #### Returns `AmqpPublishTimeoutError` #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`constructor`](AmqpAdapterError.md#constructor) ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`cause`](AmqpAdapterError.md#cause) *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`message`](AmqpAdapterError.md#message) *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`name`](AmqpAdapterError.md#name) *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`stack`](AmqpAdapterError.md#stack) *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`stackTraceLimit`](AmqpAdapterError.md#stacktracelimit) ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`captureStackTrace`](AmqpAdapterError.md#capturestacktrace) *** ### 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 [`AmqpAdapterError`](AmqpAdapterError.md).[`isError`](AmqpAdapterError.md#iserror) *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`prepareStackTrace`](AmqpAdapterError.md#preparestacktrace) --- --- url: /en/api/@connectum/events-amqp/classes/AmqpSerializationError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpSerializationError # Class: AmqpSerializationError Defined in: [packages/events-amqp/src/errors.ts:115](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L115) Payload encoding/decoding failed in a custom serialization hook. ## Extends * [`AmqpAdapterError`](AmqpAdapterError.md) ## Constructors ### Constructor > **new AmqpSerializationError**(`message`, `options?`): `AmqpSerializationError` Defined in: [packages/events-amqp/src/errors.ts:31](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L31) #### Parameters ##### message `string` ##### options? ###### cause? `unknown` #### Returns `AmqpSerializationError` #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`constructor`](AmqpAdapterError.md#constructor) ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`cause`](AmqpAdapterError.md#cause) *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`message`](AmqpAdapterError.md#message) *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`name`](AmqpAdapterError.md#name) *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`stack`](AmqpAdapterError.md#stack) *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`stackTraceLimit`](AmqpAdapterError.md#stacktracelimit) ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`captureStackTrace`](AmqpAdapterError.md#capturestacktrace) *** ### 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 [`AmqpAdapterError`](AmqpAdapterError.md).[`isError`](AmqpAdapterError.md#iserror) *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`prepareStackTrace`](AmqpAdapterError.md#preparestacktrace) --- --- url: /en/api/@connectum/events-amqp/classes/AmqpTopologyError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpTopologyError # Class: AmqpTopologyError Defined in: [packages/events-amqp/src/errors.ts:96](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L96) Topology declaration or verification failed: missing exchange/queue in `check`/`skip` mode, or a conflicting redeclare (PRECONDITION\_FAILED) in `assert` mode. `object` identifies the failing topology object structurally (known at the declare/check site — no broker-reply text parsing needed for CI drift checks or observability). `object.kind` says WHAT was being declared; failure classification (WHY it failed) stays with the error class and `cause`. ## Extends * [`AmqpAdapterError`](AmqpAdapterError.md) ## Constructors ### Constructor > **new AmqpTopologyError**(`message`, `options?`): `AmqpTopologyError` Defined in: [packages/events-amqp/src/errors.ts:102](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L102) #### Parameters ##### message `string` ##### options? ###### cause? `unknown` ###### object? [`AmqpTopologyObject`](../type-aliases/AmqpTopologyObject.md) #### Returns `AmqpTopologyError` #### Overrides [`AmqpAdapterError`](AmqpAdapterError.md).[`constructor`](AmqpAdapterError.md#constructor) ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`cause`](AmqpAdapterError.md#cause) *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`message`](AmqpAdapterError.md#message) *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`name`](AmqpAdapterError.md#name) *** ### object? > `readonly` `optional` **object?**: [`AmqpTopologyObject`](../type-aliases/AmqpTopologyObject.md) Defined in: [packages/events-amqp/src/errors.ts:100](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L100) *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`stack`](AmqpAdapterError.md#stack) *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`stackTraceLimit`](AmqpAdapterError.md#stacktracelimit) ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`captureStackTrace`](AmqpAdapterError.md#capturestacktrace) *** ### 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 [`AmqpAdapterError`](AmqpAdapterError.md).[`isError`](AmqpAdapterError.md#iserror) *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`prepareStackTrace`](AmqpAdapterError.md#preparestacktrace) --- --- url: /en/api/@connectum/events-amqp/classes/AmqpUnroutableError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpUnroutableError # Class: AmqpUnroutableError Defined in: [packages/events-amqp/src/errors.ts:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L51) The broker returned a `mandatory` message as unroutable (`basic.return`): no queue is bound for the routing key. ## Extends * [`AmqpAdapterError`](AmqpAdapterError.md) ## Constructors ### Constructor > **new AmqpUnroutableError**(`message`, `routingKey`): `AmqpUnroutableError` Defined in: [packages/events-amqp/src/errors.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L54) #### Parameters ##### message `string` ##### routingKey `string` #### Returns `AmqpUnroutableError` #### Overrides [`AmqpAdapterError`](AmqpAdapterError.md).[`constructor`](AmqpAdapterError.md#constructor) ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`cause`](AmqpAdapterError.md#cause) *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`message`](AmqpAdapterError.md#message) *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`name`](AmqpAdapterError.md#name) *** ### routingKey > `readonly` **routingKey**: `string` Defined in: [packages/events-amqp/src/errors.ts:52](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L52) *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from [`AmqpAdapterError`](AmqpAdapterError.md).[`stack`](AmqpAdapterError.md#stack) *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`stackTraceLimit`](AmqpAdapterError.md#stacktracelimit) ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`captureStackTrace`](AmqpAdapterError.md#capturestacktrace) *** ### 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 [`AmqpAdapterError`](AmqpAdapterError.md).[`isError`](AmqpAdapterError.md#iserror) *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 [`AmqpAdapterError`](AmqpAdapterError.md).[`prepareStackTrace`](AmqpAdapterError.md#preparestacktrace) --- --- 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/main/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/main/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/main/packages/auth/src/errors.ts#L29) *** ### cause > **cause**: `unknown` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.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/main/packages/auth/src/errors.ts#L27) #### Implementation of `SanitizableError.clientMessage` *** ### code > `readonly` **code**: `Code` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.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.2\_@bufbuild+protobuf@2.13.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.2\_@bufbuild+protobuf@2.13.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.2\_@bufbuild+protobuf@2.13.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.2\_@bufbuild+protobuf@2.13.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/main/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.9.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/main/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.2\_@bufbuild+protobuf@2.13.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.2\_@bufbuild+protobuf@2.13.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.2\_@bufbuild+protobuf@2.13.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.9.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.2\_@bufbuild+protobuf@2.13.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.9.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/core/classes/CatalogConfigError.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / CatalogConfigError # Class: CatalogConfigError Defined in: [packages/core/src/catalogErrors.ts:18](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogErrors.ts#L18) ## Extends * `Error` ## Constructors ### Constructor > **new CatalogConfigError**(`message`): `CatalogConfigError` Defined in: [packages/core/src/catalogErrors.ts:21](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogErrors.ts#L21) #### Parameters ##### message `string` #### Returns `CatalogConfigError` #### Overrides `Error.constructor` ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from `Error.cause` *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from `Error.message` *** ### name > `readonly` **name**: `"CatalogConfigError"` = `"CatalogConfigError"` Defined in: [packages/core/src/catalogErrors.ts:19](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogErrors.ts#L19) #### Overrides `Error.name` *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from `Error.stack` *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.stackTraceLimit` ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.captureStackTrace` *** ### 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 `Error.isError` *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.prepareStackTrace` --- --- url: /en/api/@connectum/events/classes/EventRouterImpl.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / EventRouterImpl # Class: EventRouterImpl Defined in: [packages/events/src/EventRouter.ts:17](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/EventRouter.ts#L17) EventRouter implementation that collects route entries. ## Implements * [`EventRouter`](../types/interfaces/EventRouter.md) ## Constructors ### Constructor > **new EventRouterImpl**(): `EventRouterImpl` #### Returns `EventRouterImpl` ## Properties ### entries > `readonly` **entries**: [`EventRouteEntry`](../types/interfaces/EventRouteEntry.md)\[] = `[]` Defined in: [packages/events/src/EventRouter.ts:18](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/EventRouter.ts#L18) *** ### serviceNames > `readonly` **serviceNames**: `string`\[] = `[]` Defined in: [packages/events/src/EventRouter.ts:19](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/EventRouter.ts#L19) ## Methods ### service() > **service**<`S`>(`serviceDesc`, `handlers`): `void` Defined in: [packages/events/src/EventRouter.ts:21](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/EventRouter.ts#L21) Register event handlers for a service #### Type Parameters ##### S `S` *extends* `DescService` #### Parameters ##### serviceDesc `S` ##### handlers [`ServiceEventHandlers`](../types/type-aliases/ServiceEventHandlers.md)<`S`> #### Returns `void` #### Implementation of [`EventRouter`](../types/interfaces/EventRouter.md).[`service`](../types/interfaces/EventRouter.md#service) --- --- url: >- /en/api/@connectum/healthcheck/@connectum/healthcheck/classes/HealthcheckManager.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/healthcheck](../../../index.md) / [@connectum/healthcheck](../index.md) / HealthcheckManager # Class: HealthcheckManager Defined in: [HealthcheckManager.ts:79](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L79) Healthcheck manager Manages health status for all registered services and components. Module-level singleton. Import `healthcheckManager` from the package. ## Examples **RPC service status (after server.start())** ```typescript import { healthcheckManager, ServingStatus } from '@connectum/healthcheck'; healthcheckManager.update(ServingStatus.SERVING); ``` **RPC-less worker (poller, publisher, exporter)** ```typescript import { healthcheckManager, ServingStatus } from '@connectum/healthcheck'; healthcheckManager.register('process'); // before or after start server.on('ready', () => healthcheckManager.set('process', ServingStatus.SERVING)); server.on('stopping', () => healthcheckManager.set('process', ServingStatus.NOT_SERVING)); ``` ## Constructors ### Constructor > **new HealthcheckManager**(): `HealthcheckManager` #### Returns `HealthcheckManager` ## Methods ### areAllHealthy() > **areAllHealthy**(): `boolean` Defined in: [HealthcheckManager.ts:206](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L206) Check if all services and components are healthy (SERVING) #### Returns `boolean` True if all entries are SERVING; false for an empty registry *** ### clear() > **clear**(): `void` Defined in: [HealthcheckManager.ts:254](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L254) Clear all services and components #### Returns `void` *** ### getAllStatuses() > **getAllStatuses**(): `Map`<`string`, [`ServiceStatus`](../types/interfaces/ServiceStatus.md)> Defined in: [HealthcheckManager.ts:193](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L193) Get all services health status #### Returns `Map`<`string`, [`ServiceStatus`](../types/interfaces/ServiceStatus.md)> Map of service/component name to health status *** ### getStatus() > **getStatus**(`service`): [`ServiceStatus`](../types/interfaces/ServiceStatus.md) | `undefined` Defined in: [HealthcheckManager.ts:183](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L183) Get service health status #### Parameters ##### service `string` Service or component name #### Returns [`ServiceStatus`](../types/interfaces/ServiceStatus.md) | `undefined` Service status or undefined if not found *** ### initialize() > **initialize**(`serviceNames`): `void` Defined in: [HealthcheckManager.ts:229](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L229) Initialize the RPC service slice of the registry. Affects only `service`-kind entries: * names in `serviceNames` are added (UNKNOWN) or preserved with their current status; * `service` entries absent from `serviceNames` are removed — pollers on `watch` observe SERVICE\_UNKNOWN for them afterwards; * `component` entries are never touched, so components registered before `server.start()` survive protocol initialization. Called by the Healthcheck protocol on server start; not intended for application code. #### Parameters ##### serviceNames `string`\[] Array of RPC service typeNames to track #### Returns `void` *** ### register() > **register**(`component`, `initialStatus?`): `void` Defined in: [HealthcheckManager.ts:123](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L123) Register an application health component. A registered component is a readiness gate: it participates in `areAllHealthy()`, gRPC `Check`/`Watch`, and `/healthz` exactly like an RPC service. Registering an already-registered component does NOT reset its status. Component names must be non-empty and dot-free. #### Parameters ##### component `string` Component name (e.g. "process", "amqp") ##### initialStatus? `HealthCheckResponse_ServingStatus` = `ServingStatus.UNKNOWN` Initial status (default UNKNOWN) #### Returns `void` #### Throws Error on invalid name or when the name belongs to an RPC service *** ### set() > **set**(`component`, `status`): `void` Defined in: [HealthcheckManager.ts:149](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L149) Set a component's status (upsert). Unlike `update()`, does not throw for unknown names: the component is registered first if absent. Component names must be non-empty and dot-free. #### Parameters ##### component `string` Component name ##### status `HealthCheckResponse_ServingStatus` New serving status #### Returns `void` #### Throws Error on invalid name or when the name belongs to an RPC service *** ### unregister() > **unregister**(`component`): `void` Defined in: [HealthcheckManager.ts:166](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L166) Remove a registered component. #### Parameters ##### component `string` Component name #### Returns `void` #### Throws Error when the name belongs to an RPC service *** ### update() > **update**(`status`, `service?`): `void` Defined in: [HealthcheckManager.ts:93](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L93) Update service health status When called without a service name, updates ALL registered entries (services and components alike). 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 entries) #### Returns `void` #### Throws Error if service name is provided but not registered --- --- url: /en/api/@connectum/testing/index/classes/InMemoryMetricCollector.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / InMemoryMetricCollector # Class: InMemoryMetricCollector Defined in: [testing/src/otel-collectors.ts:191](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L191) In-memory metric collector. Owns its own `MeterProvider` and periodic reader. `flush()` performs a forced collect+export cycle synchronously (via `forceFlush`) and returns the normalized data. ## Constructors ### Constructor > **new InMemoryMetricCollector**(): `InMemoryMetricCollector` Defined in: [testing/src/otel-collectors.ts:196](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L196) #### Returns `InMemoryMetricCollector` ## Properties ### exporter > `readonly` **exporter**: `InMemoryMetricExporter` Defined in: [testing/src/otel-collectors.ts:192](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L192) *** ### provider > `readonly` **provider**: `MeterProvider` Defined in: [testing/src/otel-collectors.ts:193](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L193) *** ### reader > `readonly` **reader**: `PeriodicExportingMetricReader` Defined in: [testing/src/otel-collectors.ts:194](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L194) ## Methods ### dispose() > **dispose**(): `Promise`<`void`> Defined in: [testing/src/otel-collectors.ts:229](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L229) #### Returns `Promise`<`void`> *** ### flush() > **flush**(): `Promise`<[`NormalizedMetric`](../interfaces/NormalizedMetric.md)\[]> Defined in: [testing/src/otel-collectors.ts:208](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L208) #### Returns `Promise`<[`NormalizedMetric`](../interfaces/NormalizedMetric.md)\[]> *** ### reset() > **reset**(): `void` Defined in: [testing/src/otel-collectors.ts:225](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L225) #### Returns `void` --- --- url: /en/api/@connectum/testing/index/classes/InMemorySpanCollector.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / InMemorySpanCollector # Class: InMemorySpanCollector Defined in: [testing/src/otel-collectors.ts:147](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L147) In-memory span collector. Owns its own `BasicTracerProvider` so that different scenarios cannot cross-contaminate. Callers wishing to register the provider globally (so that `trace.getTracer(...)` resolves here) should call registerGlobal — and pair it with [InMemorySpanCollector.dispose](#dispose) when done. ## Constructors ### Constructor > **new InMemorySpanCollector**(): `InMemorySpanCollector` Defined in: [testing/src/otel-collectors.ts:151](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L151) #### Returns `InMemorySpanCollector` ## Properties ### exporter > `readonly` **exporter**: `InMemorySpanExporter` Defined in: [testing/src/otel-collectors.ts:148](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L148) *** ### provider > `readonly` **provider**: `BasicTracerProvider` Defined in: [testing/src/otel-collectors.ts:149](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L149) ## Methods ### dispose() > **dispose**(): `Promise`<`void`> Defined in: [testing/src/otel-collectors.ts:181](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L181) #### Returns `Promise`<`void`> *** ### flush() > **flush**(): [`NormalizedSpan`](../interfaces/NormalizedSpan.md)\[] Defined in: [testing/src/otel-collectors.ts:165](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L165) Returns normalized finished spans collected so far. Spans are sorted by `(name, kind, sorted-attributes)` so that scenarios which emit multiple spans concurrently produce a deterministic order for the parity structural diff. #### Returns [`NormalizedSpan`](../interfaces/NormalizedSpan.md)\[] *** ### reset() > **reset**(): `void` Defined in: [testing/src/otel-collectors.ts:177](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L177) Clear the internal buffer. #### Returns `void` --- --- 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/main/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/main/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/main/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/main/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/main/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/main/packages/auth/src/cache.ts#L41) #### Parameters ##### key `string` ##### value `T` #### Returns `void` --- --- url: /en/api/@connectum/events/classes/NonRetryableError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / NonRetryableError # Class: NonRetryableError Defined in: [packages/events/src/errors.ts:27](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/errors.ts#L27) Error that should never be retried. When thrown inside an event handler, the retry middleware skips all retries and re-throws immediately — regardless of the `retryableErrors` predicate. ## Example ```typescript throw new NonRetryableError("Invalid payload", { cause: validationError }); ``` ## Extends * `Error` ## Constructors ### Constructor > **new NonRetryableError**(`message`, `options?`): `NonRetryableError` Defined in: [packages/events/src/errors.ts:30](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/errors.ts#L30) #### Parameters ##### message `string` ##### options? `ErrorOptions` #### Returns `NonRetryableError` #### Overrides `Error.constructor` ## Properties ### \[NON\_RETRYABLE] > `readonly` **\[NON\_RETRYABLE]**: `true` = `true` Defined in: [packages/events/src/errors.ts:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/errors.ts#L28) *** ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from `Error.cause` *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from `Error.message` *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from `Error.name` *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from `Error.stack` *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.stackTraceLimit` ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.captureStackTrace` *** ### 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 `Error.isError` *** ### isNonRetryable() > `static` **isNonRetryable**(`error`): `error is { [NON_RETRYABLE]: true }` Defined in: [packages/events/src/errors.ts:39](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/errors.ts#L39) Check if an error is branded as non-retryable. Works across realms (Symbol.for is global). #### Parameters ##### error `unknown` #### Returns `error is { [NON_RETRYABLE]: true }` *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.prepareStackTrace` --- --- url: /en/api/@connectum/events/classes/RetryableError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / RetryableError # Class: RetryableError Defined in: [packages/events/src/errors.ts:56](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/errors.ts#L56) Error that should always be retried. When thrown inside an event handler, the retry middleware retries the handler — even if the `retryableErrors` predicate would otherwise reject it. ## Example ```typescript throw new RetryableError("Temporary DB connection lost", { cause: dbError }); ``` ## Extends * `Error` ## Constructors ### Constructor > **new RetryableError**(`message`, `options?`): `RetryableError` Defined in: [packages/events/src/errors.ts:59](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/errors.ts#L59) #### Parameters ##### message `string` ##### options? `ErrorOptions` #### Returns `RetryableError` #### Overrides `Error.constructor` ## Properties ### \[RETRYABLE] > `readonly` **\[RETRYABLE]**: `true` = `true` Defined in: [packages/events/src/errors.ts:57](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/errors.ts#L57) *** ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from `Error.cause` *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from `Error.message` *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from `Error.name` *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from `Error.stack` *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.stackTraceLimit` ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.captureStackTrace` *** ### 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 `Error.isError` *** ### isRetryable() > `static` **isRetryable**(`error`): `error is { [RETRYABLE]: true }` Defined in: [packages/events/src/errors.ts:68](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/errors.ts#L68) Check if an error is branded as retryable. Works across realms (Symbol.for is global). #### Parameters ##### error `unknown` #### Returns `error is { [RETRYABLE]: true }` *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.prepareStackTrace` --- --- url: /en/api/@connectum/core/classes/TransportValidationError.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / TransportValidationError # Class: TransportValidationError Defined in: [packages/core/src/TransportValidation.ts:90](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L90) Startup validation error: bidi-streaming methods registered on a transport that cannot carry them. Carries the stable [TRANSPORT\_VALIDATION\_ERROR\_CODE](../variables/TRANSPORT_VALIDATION_ERROR_CODE.md) code and the affected methods. ## Extends * `Error` ## Constructors ### Constructor > **new TransportValidationError**(`message`, `methods`): `TransportValidationError` Defined in: [packages/core/src/TransportValidation.ts:94](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L94) #### Parameters ##### message `string` ##### methods readonly [`StreamingMethodInfo`](../interfaces/StreamingMethodInfo.md)\[] #### Returns `TransportValidationError` #### Overrides `Error.constructor` ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from `Error.cause` *** ### code > `readonly` **code**: `"CONNECTUM_UNSUPPORTED_STREAMING_TRANSPORT"` = `TRANSPORT_VALIDATION_ERROR_CODE` Defined in: [packages/core/src/TransportValidation.ts:91](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L91) *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from `Error.message` *** ### methods > `readonly` **methods**: readonly [`StreamingMethodInfo`](../interfaces/StreamingMethodInfo.md)\[] Defined in: [packages/core/src/TransportValidation.ts:92](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L92) *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from `Error.name` *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from `Error.stack` *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.stackTraceLimit` ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.captureStackTrace` *** ### 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 `Error.isError` *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@25.9.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 `Error.prepareStackTrace` --- --- url: /en/contributing/cli-commands.md --- # CLI Commands Reference ## Overview Complete reference of CLI commands for working with the Connectum monorepo. ::: tip The `connectum` CLI This page covers the monorepo development scripts. The published `@connectum/cli` tool (`connectum init`, `connectum generate service`, `connectum proto sync`) is documented in [Scaffolding a New Service](/en/guide/scaffolding). In short: ```bash npx @connectum/cli init my-service # scaffold a new project npx @connectum/cli generate service x # add a service to an existing project ``` ::: ## Prerequisites * **Node.js**: >=25.2.0 (for development), >=22.13.0 (for consumers) * **pnpm**: 11+ * **Buf**: provided by the `@bufbuild/buf` workspace devDependency (no standalone install); proto generation runs via `pnpm build:proto` ### Installation Check ```bash # Check Node.js version node --version # Should be >= 25.2.0 for development (>= 22.13.0 for consumers) # Check pnpm version pnpm --version # Should be >= 11.0.0 # Buf is bundled as a workspace devDependency; verify proto generation pnpm build:proto ``` ## 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 ``` 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/getting-started/src/index.ts # Development mode with watch node --watch examples/getting-started/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 <=22.13.0" } } EOF # Create tsconfig.json cat > packages/my-package/tsconfig.json <&1 | grep -q "Error" || echo "Type stripping works!" # Verify pnpm workspace pnpm list --depth 0 # Verify proto generation (Buf via the @bufbuild/buf workspace devDependency) pnpm build:proto # 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: ```mermaid flowchart LR AServer["Service A · server span"] --> AClient["Service A · client span"] AClient -->|Injected trace context| BServer["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. ## Auth Client Interceptors `@connectum/auth` ships two client-side factories for outbound service-to-service authentication. Use them on any `createGrpcTransport` / `createConnectTransport` instead of hand-rolling header wiring. ```typescript import { createGrpcTransport } from '@connectrpc/connect-node'; import { createClientBearerInterceptor, createClientGatewayInterceptor, } from '@connectum/auth'; // Forward a user's Bearer token (or a refreshable service token) to the upstream const upstream = createGrpcTransport({ baseUrl: 'http://upstream-service:5000', httpVersion: '2', interceptors: [ createClientBearerInterceptor({ token: async () => (await getAccessToken()).accessToken, }), ], }); // Trusted service-to-service call behind a shared-secret gateway const internal = createGrpcTransport({ baseUrl: 'http://internal-service:5000', httpVersion: '2', interceptors: [ createClientGatewayInterceptor({ secret: process.env.GATEWAY_SECRET!, subject: 'order-service', roles: ['service', 'order-writer'], }), ], }); ``` * `createClientBearerInterceptor` sets `Authorization: Bearer `; the token may be a static string or an async factory invoked before each request (useful for refresh flows). * `createClientGatewayInterceptor` sets `x-gateway-secret`, `x-auth-subject`, and optionally `x-auth-roles` so the receiving service can reconstruct the `AuthContext` via [`createGatewayAuthInterceptor`](/en/guide/auth/gateway) without re-authenticating. Full guidance: [Client-Side Auth Interceptors](/en/guide/auth/client-interceptors). ## 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: { threshold: 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 `threshold` 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: { threshold: 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: { threshold: 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 * [Gateway Authentication](/en/guide/auth/gateway) -- server side of `createClientGatewayInterceptor` * [Client-Side Auth Interceptors](/en/guide/auth/client-interceptors) -- bearer and gateway credentials * [@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/guide/auth/client-interceptors.md --- # Client-Side Auth Interceptors Server-side interceptors (`createGatewayAuthInterceptor`, `createJwtAuthInterceptor`, etc.) validate incoming requests. **Client-side** interceptors do the opposite -- they attach credentials to outgoing requests. Connectum provides two client interceptor factories for common auth patterns: | Factory | Purpose | Header set | |---------|---------|------------| | `createClientBearerInterceptor` | Attach a Bearer token | `Authorization: Bearer ` | | `createClientGatewayInterceptor` | Service-to-service trust | `x-gateway-secret`, `x-auth-subject`, `x-auth-roles` | ## Bearer Token Use `createClientBearerInterceptor` when your service calls another service that expects a Bearer token (JWT, opaque token, API key in Bearer format). ### Static token ```typescript import { createClientBearerInterceptor } from '@connectum/auth'; import { createConnectTransport } from '@connectrpc/connect-node'; const transport = createConnectTransport({ baseUrl: 'http://internal-api:5000', interceptors: [ createClientBearerInterceptor({ token: process.env.API_TOKEN! }), ], }); ``` ### Async token factory (refresh flow) When the token may expire, pass an async function. It is called before every request: ```typescript import { createClientBearerInterceptor } from '@connectum/auth'; const bearerAuth = createClientBearerInterceptor({ token: async () => { const { accessToken } = await refreshTokenIfNeeded(); return accessToken; }, }); ``` `token` accepts either a static credential or an async factory. See [`ClientBearerInterceptorOptions`](/en/api/@connectum/auth/interfaces/ClientBearerInterceptorOptions) for the exact contract. ## Gateway (Service-to-Service) Use `createClientGatewayInterceptor` when calling a service behind a gateway that uses `createGatewayAuthInterceptor` on the server side. The client interceptor sets the trust headers that the server interceptor reads. ```typescript import { createClientGatewayInterceptor } from '@connectum/auth'; import { createConnectTransport } from '@connectrpc/connect-node'; const transport = createConnectTransport({ baseUrl: 'http://order-service:5001', interceptors: [ createClientGatewayInterceptor({ secret: process.env.GATEWAY_SECRET!, subject: 'task-coordinator', roles: ['service', 'order-writer'], }), ], }); ``` Provide a secret that matches the server trust source and a stable service subject; roles are optional. See [`ClientGatewayInterceptorOptions`](/en/api/@connectum/auth/interfaces/ClientGatewayInterceptorOptions) for exact fields. ### Header mapping The client interceptor sets these headers: | Header | Source | Server reads via | |--------|--------|-----------------| | `x-gateway-secret` | `options.secret` | `trustSource.header` | | `x-auth-subject` | `options.subject` | `headerMapping.subject` | | `x-auth-roles` | `JSON.stringify(options.roles)` | `headerMapping.roles` | When `roles` is omitted or empty, the `x-auth-roles` header is not set. ::: warning headerMapping must match the client headers `createClientGatewayInterceptor` always sends the fixed header names `x-auth-subject` and `x-auth-roles`. The server's `createGatewayAuthInterceptor` reads whatever names its `headerMapping` declares. The `headerMapping` examples in the [Gateway Authentication](/en/guide/auth/gateway) guide use `x-user-id` / `x-user-roles`, which do **not** match these client headers. To pair the two interceptors out of the box, configure the server with a matching mapping: ```typescript const gatewayAuth = createGatewayAuthInterceptor({ headerMapping: { subject: 'x-auth-subject', roles: 'x-auth-roles', }, trustSource: { header: 'x-gateway-secret', expectedValues: [process.env.GATEWAY_SECRET!], }, }); ``` ::: ## Combining with other interceptors Client auth interceptors compose naturally with other interceptors: ```typescript import { createClientGatewayInterceptor } from '@connectum/auth'; import { createOtelClientInterceptor } from '@connectum/otel'; import { createConnectTransport } from '@connectrpc/connect-node'; const transport = createConnectTransport({ baseUrl: 'http://order-service:5001', interceptors: [ createOtelClientInterceptor(), // tracing createClientGatewayInterceptor({ // auth secret: process.env.GATEWAY_SECRET!, subject: 'api-gateway', roles: ['gateway'], }), ], }); ``` ## Server ↔ Client pairing | Client interceptor | Server interceptor | Trust mechanism | |---|---|---| | `createClientBearerInterceptor` | `createJwtAuthInterceptor` | JWT signature verification | | `createClientBearerInterceptor` | `createSessionAuthInterceptor` | Session token lookup | | `createClientGatewayInterceptor` | `createGatewayAuthInterceptor` | Shared secret header | --- --- 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 { defineService } from '@connectum/core'; 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. // Pass the result to createServer({ services: [orderService] }). const orderService = defineService(OrderService, { async createOrder(req, ctx) { 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 * [Connectum Runtime Architecture](/en/guide/production/architecture) -- process boundaries and local/remote routing * [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/guide/events/middleware.md description: 'Add retry, dead-letter routing, or custom behavior around an EventBus handler.' --- # Middleware The EventBus middleware pipeline wraps event handlers in a composable onion model. Built-in middleware provides retry with configurable backoff and dead letter queue (DLQ) routing. You can add custom middleware for logging, metrics, validation, or any cross-cutting concern. **Outcome:** configure one middleware chain and verify success, retry exhaustion, and DLQ behavior. Broker choice and delivery guarantees are owned by [Adapter selection](/en/guide/events/adapters); exact exported types are in the [`@connectum/events` API](/en/api/@connectum/events/). ## Pipeline Order Middleware executes from outermost to innermost: ```mermaid graph LR E["Raw Event"] --> C1["Custom Middleware"] C1 --> C2["..."] C2 --> D["DLQ Middleware"] D --> R["Retry Middleware"] R --> H["Event Handler"] H -->|ack/nack| R R -->|error after retries| DLQ["DLQ Topic"] ``` Each middleware receives three arguments: ```typescript type EventMiddleware = ( event: RawEvent, ctx: EventContext, next: EventMiddlewareNext, ) => Promise; ``` Call `next()` to pass control to the inner middleware. Errors thrown from `next()` propagate outward through the chain. ## Retry Middleware Retries failed event handlers with configurable backoff strategy. If all attempts are exhausted, the error is re-thrown for the next middleware in the chain (typically DLQ). ### Configuration ```typescript const eventBus = createEventBus({ adapter, routes: [myEvents], middleware: { retry: { maxRetries: 3, backoff: 'exponential', initialDelay: 1000, maxDelay: 30_000, multiplier: 2, }, }, }); ``` ### RetryOptions | Option | Type | Default | Description | |--------|------|---------|-------------| | `maxRetries` | `number` | `3` | Maximum retry attempts before giving up | | `backoff` | `"exponential" \| "linear" \| "fixed"` | `"exponential"` | Backoff strategy between retries | | `initialDelay` | `number` | `1000` | Initial delay in milliseconds | | `maxDelay` | `number` | `30000` | Maximum delay cap in milliseconds | | `multiplier` | `number` | `2` | Multiplier for exponential backoff | | `retryableErrors` | `(error: unknown) => boolean` | `undefined` | Filter function: only retry if it returns `true` | ### Backoff Strategies | Strategy | Delay Formula | Example (initialDelay=1000, multiplier=2) | |----------|--------------|-------------------------------------------| | `exponential` | `initialDelay * multiplier^(attempt-1)` | 1000ms, 2000ms, 4000ms, 8000ms | | `linear` | `initialDelay * attempt` | 1000ms, 2000ms, 3000ms, 4000ms | | `fixed` | `initialDelay` | 1000ms, 1000ms, 1000ms, 1000ms | All strategies are capped at `maxDelay`. ### Selective Retry Use `retryableErrors` to only retry specific error types: ```typescript middleware: { retry: { maxRetries: 3, backoff: 'exponential', retryableErrors: (error) => { // Only retry transient errors if (error instanceof Error) { return error.message.includes('ECONNREFUSED') || error.message.includes('timeout'); } return false; }, }, } ``` Non-retryable errors are thrown immediately without delay. ### Typed Error Classes Instead of (or in addition to) the `retryableErrors` predicate, you can use typed error classes for declarative retry control: ```typescript import { NonRetryableError, RetryableError } from '@connectum/events'; // Handler that uses typed errors const orderEvents: EventRoute = (events) => { events.service(OrderEventHandlers, { onOrderCreated: async (msg, ctx) => { // Validation errors -- never retry if (!msg.orderId) { throw new NonRetryableError('Missing orderId'); } try { await db.insertOrder(msg); } catch (err) { // Transient DB errors -- always retry if (isConnectionError(err)) { throw new RetryableError('DB connection lost', { cause: err }); } throw err; // Other errors use predicate or default behavior } }, }); }; ``` **Priority order** (first match wins): | Priority | Check | Behavior | |----------|-------|----------| | 1 | `NonRetryableError` | Skip retry, throw immediately | | 2 | `RetryableError` | Force retry (ignores `retryableErrors` predicate) | | 3 | `retryableErrors` predicate | Retry if predicate returns `true` | | 4 | Default | Retry all errors | Both classes use `Symbol.for()` branding, so they work across module boundaries and realms. ## DLQ Middleware When a handler fails after all retries are exhausted, the DLQ middleware publishes the failed event to a dedicated dead letter topic and acknowledges the original event. This prevents poison messages from blocking the queue. ### Configuration ```typescript const eventBus = createEventBus({ adapter, routes: [myEvents], middleware: { retry: { maxRetries: 3, backoff: 'exponential' }, dlq: { topic: 'my-service.dlq' }, }, }); ``` ### DlqOptions | Option | Type | Default | Description | |--------|------|---------|-------------| | `topic` | `string` | *required* | Topic name for dead letter events | | `errorSerializer` | `(error: unknown) => string` | `undefined` | Custom error serializer for DLQ metadata. The default serializes `error.name` only; a custom serializer should return a redacted string. | ### DLQ Metadata When an event is routed to the DLQ, the middleware attaches diagnostic metadata: | Key | Description | |-----|-------------| | `dlq.original-topic` | Topic the event was originally published to | | `dlq.original-id` | Unique event ID from the original message | | `dlq.error` | Error message from the last failed handler invocation | | `dlq.attempt` | Number of delivery attempts before DLQ routing | ### Monitoring the DLQ Subscribe directly to the DLQ topic using the adapter to monitor and process failed events: ```typescript adapter.subscribe( ['my-service.dlq'], async (rawEvent) => { const originalTopic = rawEvent.metadata.get('dlq.original-topic') ?? 'unknown'; const error = rawEvent.metadata.get('dlq.error') ?? 'unknown'; console.error(`DLQ event from ${originalTopic}: ${error}`); // Log, alert, or attempt recovery }, { group: 'dlq-monitor' }, ); ``` ## Retry + DLQ Together The most common pattern combines retry and DLQ: retry transient errors, route persistent failures to DLQ. ```typescript const eventBus = createEventBus({ adapter: NatsAdapter({ servers: 'nats://localhost:4222', stream: 'orders' }), routes: [orderEvents], group: 'order-service', middleware: { retry: { maxRetries: 2, backoff: 'fixed', initialDelay: 200 }, dlq: { topic: 'dead-letter-queue' }, }, }); ``` Execution flow on handler error: ```mermaid sequenceDiagram participant Broker participant DLQ as DLQ Middleware participant Retry as Retry Middleware participant Handler Broker->>DLQ: deliver event DLQ->>Retry: next() Retry->>Handler: next() Handler->>Retry: throw Error Note over Retry: Attempt 2 (after delay) Retry->>Handler: next() Handler->>Retry: throw Error Note over Retry: Attempt 3 (after delay) Retry->>Handler: next() Handler->>Retry: throw Error Note over Retry: maxRetries exhausted Retry->>DLQ: re-throw DLQ->>Broker: publish to DLQ topic DLQ->>DLQ: ack original event ``` ## Custom Middleware ### Writing Custom Middleware A middleware is any function matching the `EventMiddleware` signature: ```typescript import type { EventMiddleware } from '@connectum/events'; const loggingMiddleware: EventMiddleware = async (event, ctx, next) => { const start = performance.now(); console.log(`[${ctx.eventType}] Processing event ${ctx.eventId}`); try { await next(); const duration = performance.now() - start; console.log(`[${ctx.eventType}] Completed in ${duration.toFixed(1)}ms`); } catch (error) { const duration = performance.now() - start; console.error(`[${ctx.eventType}] Failed after ${duration.toFixed(1)}ms:`, error); throw error; // Re-throw to let retry/DLQ handle it } }; ``` ### Registering Custom Middleware Pass custom middleware in the `middleware.custom` array. They execute outermost, wrapping retry and DLQ: ```typescript const eventBus = createEventBus({ adapter, routes: [myEvents], middleware: { custom: [loggingMiddleware, metricsMiddleware], retry: { maxRetries: 3 }, dlq: { topic: 'my-service.dlq' }, }, }); ``` Execution order: `loggingMiddleware → metricsMiddleware → DLQ → retry → handler` ### Metrics Middleware Example ```typescript import type { EventMiddleware } from '@connectum/events'; const metricsMiddleware: EventMiddleware = async (event, ctx, next) => { const labels = { event_type: ctx.eventType, attempt: String(ctx.attempt) }; try { await next(); eventCounter.inc({ ...labels, status: 'success' }); } catch (error) { eventCounter.inc({ ...labels, status: 'error' }); throw error; } }; ``` ### Validation Middleware Example ```typescript import type { EventMiddleware } from '@connectum/events'; const payloadSizeMiddleware: EventMiddleware = async (event, ctx, next) => { const MAX_PAYLOAD_SIZE = 1024 * 1024; // 1 MB if (event.payload.length > MAX_PAYLOAD_SIZE) { console.warn(`Event ${ctx.eventId} payload exceeds 1MB, skipping`); await ctx.nack(false); // Don't requeue return; } await next(); }; ``` ## Advanced: Manual Composition For advanced use cases, you can compose middleware manually using `composeMiddleware()`: ```typescript import { composeMiddleware, retryMiddleware, dlqMiddleware } from '@connectum/events'; import type { EventMiddleware } from '@connectum/events'; const middlewares: EventMiddleware[] = [ loggingMiddleware, retryMiddleware({ maxRetries: 3 }), dlqMiddleware({ topic: 'my.dlq' }, adapter), ]; const composed = composeMiddleware(middlewares, async (rawEvent, ctx) => { // Final handler console.log(`Processing ${ctx.eventId}`); await ctx.ack(); }); ``` ## Related * [Events Overview](/en/guide/events) -- architecture and core concepts * [Getting Started](/en/guide/events/getting-started) -- step-by-step setup * [with-events-dlq](https://github.com/Connectum-Framework/examples/tree/main/with-events-dlq) -- full DLQ example with NATS JetStream * [@connectum/events](/en/packages/events) -- Package Guide --- --- url: /en/guide/events/custom-topics.md description: >- Override the default proto message topic only where a stable broker-facing name is required. --- # Custom Topics By default, the EventBus routes events using the protobuf message's `typeName` (e.g., `orders.v1.OrderCreated`). You can override this with a custom topic name using proto options or publish-time overrides. **Outcome:** choose one canonical topic source and verify publishers and handlers resolve the same name. Delivery and broker semantics remain in [Events](/en/guide/events) and [Adapter selection](/en/guide/events/adapters). ## Default Topic Naming When you register event handlers with `events.service()`, the EventRouter resolves the topic for each method: 1. Check for a custom `(connectum.events.v1.event).topic` proto option on the method 2. Fall back to `method.input.typeName` (the fully-qualified protobuf message name) For example, this handler subscribes to the topic `orders.v1.OrderCreated`: ```protobuf service InventoryEventHandlers { // Topic: "orders.v1.OrderCreated" (default — from message typeName) rpc OnOrderCreated(OrderCreated) returns (google.protobuf.Empty); } ``` ## Proto Option: Custom Topic Import the Connectum events options proto and set a custom topic on any method: ```protobuf syntax = "proto3"; package orders.v1; import "google/protobuf/empty.proto"; import "connectum/events/v1/options.proto"; service InventoryEventHandlers { // Default topic: "orders.v1.OrderCreated" rpc OnOrderCreated(OrderCreated) returns (google.protobuf.Empty); // Custom topic: "orders.cancelled" rpc OnOrderCancelled(OrderCancelled) returns (google.protobuf.Empty) { option (connectum.events.v1.event).topic = "orders.cancelled"; } } ``` The `connectum/events/v1/options.proto` file defines the method option: ```protobuf // connectum/events/v1/options.proto syntax = "proto2"; package connectum.events.v1; import "google/protobuf/descriptor.proto"; message EventOptions { optional string topic = 1; } extend google.protobuf.MethodOptions { optional EventOptions event = 50102; } ``` ::: tip The `options.proto` file is included in the `@connectum/events` package proto directory. Add it to your `buf.yaml` dependencies or copy it into your project's proto tree. ::: ## Publishing to Custom Topics The EventBus resolves the publish topic automatically when the publisher process has the relevant service registered — either via `routes` (subscriber side) or via the `publishes` option (publisher-only side). In that case no `PublishOptions.topic` override is needed: ```typescript import { OrderCancelledSchema, OrderEventService } from '#gen/orders/v1/orders_pb.js'; // Publisher-only process: declare the service in `publishes` so the // custom topic from the proto option is resolved automatically. const eventBus = createEventBus({ adapter, publishes: [OrderEventService] }); // Topic resolves to "orders.cancelled" from the proto option — no manual override needed. await eventBus.publish(OrderCancelledSchema, { orderId: 'abc-123', reason: 'Changed my mind', }); ``` If the publisher has neither `routes` nor `publishes` covering the event type, the lookup will be empty and `publish()` falls back to `schema.typeName`. In that case you must pass the topic explicitly: ```typescript // Fallback: no routes/publishes registered — specify the topic manually. await eventBus.publish(OrderCancelledSchema, { orderId: 'abc-123', reason: 'Changed my mind', }, { topic: 'orders.cancelled' }); ``` ::: warning Consistency Required Whichever resolution path you use, the topic must match what the subscriber declared in the proto option. A mismatch means the subscriber will never receive the event. ::: ## Topic Resolution Flow ```mermaid flowchart TD A["EventRouter.service()"] --> B{"Has proto option?"} B -->|Yes| C["Use option topic"] B -->|No| D["Use method.input.typeName"] C --> E["Subscribe to topic"] D --> E F["eventBus.publish()"] --> G{"Has PublishOptions.topic?"} G -->|Yes| H["Use options.topic"] G -->|No| P{"Found in publishTopicMap?\n(from routes or publishes)"} P -->|Yes| Q["Use declared topic from proto option"] P -->|No| I["Use schema.typeName"] H --> J["Publish to topic"] Q --> J I --> J ``` ## Wildcard Topic Matching The MemoryAdapter and NATS adapter support wildcard patterns for topic matching: | Pattern | Matches | Does Not Match | |---------|---------|----------------| | `orders.*` | `orders.created`, `orders.cancelled` | `orders.v1.created` | | `orders.>` | `orders.created`, `orders.v1.created`, `orders.v1.created.eu` | `inventory.reserved` | | `orders.v1.*` | `orders.v1.OrderCreated`, `orders.v1.OrderCancelled` | `orders.v1.sub.topic` | Two wildcard tokens are supported: * **`*`** -- matches exactly one dot-separated segment * **`>`** -- matches one or more trailing segments ::: info Broker Limitations Wildcard patterns are natively supported by NATS. Kafka and Redis Streams do not support server-side wildcards -- the adapter subscribes to exact topic names only. ::: ## Best Practices ### Use default topics for simple cases If your events have unique message types, the default `typeName` works well and requires no extra configuration: ```typescript // Publisher uses the default topic await eventBus.publish(OrderCreatedSchema, data); // Subscriber listens on the default topic automatically events.service(InventoryEventHandlers, { onOrderCreated: async (msg, ctx) => { /* ... */ }, }); ``` ### Use custom topics for shared message types When multiple events share the same message type or when you want domain-oriented naming: ```protobuf service OrderEventHandlers { // Same OrderStatus message, different business events rpc OnOrderConfirmed(OrderStatus) returns (google.protobuf.Empty) { option (connectum.events.v1.event).topic = "orders.confirmed"; } rpc OnOrderShipped(OrderStatus) returns (google.protobuf.Empty) { option (connectum.events.v1.event).topic = "orders.shipped"; } } ``` ### Keep topic naming consistent Adopt a convention across your project: | Convention | Example | When to Use | |------------|---------|-------------| | Default (`typeName`) | `orders.v1.OrderCreated` | Single message type per event | | Domain-based | `orders.created`, `inventory.reserved` | Shared messages, simpler names | | Hierarchical | `domain.aggregate.event` | Complex event taxonomies | ## Related * [Events Overview](/en/guide/events) -- architecture and core concepts * [Getting Started](/en/guide/events/getting-started) -- step-by-step setup * [Adapters](/en/guide/events/adapters) -- wildcard support per adapter * [@connectum/events](/en/packages/events) -- Package Guide --- --- url: /en/guide/security/mtls.md description: >- Require and verify client certificates for service-to-service Connectum traffic. --- # Mutual TLS (mTLS) Mutual TLS enables both client and server to authenticate each other, providing strong identity verification for service-to-service communication. **Outcome:** the server rejects callers without a certificate issued by the configured CA. Certificate loading and basic server TLS are owned by [TLS configuration](/en/guide/security/tls); this page owns client-certificate policy and deployment safety. ## 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/health-checks/protocol.md description: >- Configure health endpoints and model service, component, and dependency status. --- # 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 }) ``` Use `httpEnabled` and `httpPaths` for platform endpoints, `watchInterval` for the stream polling cadence, and `manager` when a test or multi-server process must avoid the singleton. See [`HealthcheckOptions`](/en/api/@connectum/healthcheck/@connectum/healthcheck/types/interfaces/HealthcheckOptions) for the exact field types and defaults. ## 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 entries (services and components). Throws on an unknown name. | | `register(component, initialStatus?)` | Register an application health component (default `UNKNOWN`). Re-registering preserves the current status. | | `set(component, status)` | Set a component's status (upsert: registers the component if absent). | | `unregister(component)` | Remove a registered component. | | `getStatus(service)` | Get status of a specific service or component. Returns `ServiceStatus \| undefined` (`undefined` for an unknown name — does not throw). | | `getAllStatuses()` | Get a Map of all service and component statuses | | `areAllHealthy()` | Check if all services and components report `SERVING` | | `initialize(serviceNames)` | Initialize the RPC service slice (called by the protocol). Replaces only `service` entries; never touches components. | | `clear()` | Clear all services and components | ## 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: ::: runtime bun Import the runner from `bun:test` instead of `node:test` and run the file with `bun test`. ::: ```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/security/tls.md description: Load a server certificate and select the secure Connectum transport behavior. --- # TLS Configuration Configure TLS for secure gRPC/ConnectRPC communication in Connectum services. **Outcome:** the server starts with a trusted key/certificate pair and clients verify it. Client-certificate enforcement is a separate [mTLS task](/en/guide/security/mtls); exact loading fields live in [`TLSOptions`](/en/api/@connectum/core/types/interfaces/TLSOptions). ## TLS Options The `tls` option accepts either explicit key/certificate paths or a directory that contains `server.key` and `server.crt`. Do not copy this shape into application types; use the generated [`TLSOptions`](/en/api/@connectum/core/types/interfaces/TLSOptions) contract. ### 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/guide/observability/tracing.md description: >- Trace server, client, and application work while preserving propagation and data-safety boundaries. --- # 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, }); ``` All server interceptor options are optional. Configure only the filtering, endpoint attributes, signal toggles, or data recording that the service requires; use [`OtelInterceptorOptions`](/en/api/@connectum/otel/interfaces/OtelInterceptorOptions) for the exact fields. Keep message recording disabled unless its privacy and volume impact has been reviewed. ## 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.md description: >- Build production-ready TypeScript microservices with explicit contracts, middleware, security, observability, and operations. --- ```typescript import { createServer } from '@connectum/core'; import { Healthcheck } from '@connectum/healthcheck'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { Reflection } from '@connectum/reflection'; import { greeterService } from './services/greeterService.ts'; const server = createServer({ services: [greeterService], port: 5000, protocols: [Healthcheck({ httpEnabled: true }), Reflection()], interceptors: createDefaultInterceptors(), shutdown: { autoShutdown: true }, }); await server.start(); ``` --- --- 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/events](@connectum/events/index.md) * [@connectum/events-amqp](@connectum/events-amqp/index.md) * [@connectum/events-kafka](@connectum/events-kafka/index.md) * [@connectum/events-nats](@connectum/events-nats/index.md) * [@connectum/events-redis](@connectum/events-redis/index.md) * [@connectum/healthcheck](@connectum/healthcheck/index.md) * [@connectum/interceptors](@connectum/interceptors/index.md) * [@connectum/otel](@connectum/otel/index.md) * [@connectum/protoc-gen-catalog](@connectum/protoc-gen-catalog/index.md) * [@connectum/reflection](@connectum/reflection/index.md) * [@connectum/test-fixtures](@connectum/test-fixtures/index.md) * [@connectum/testing](@connectum/testing/index.md) --- --- url: /en/packages.md description: Choose Connectum modules by the capability your service needs. --- # Connectum Packages Start with `@connectum/core`, then add explicit capabilities as your service needs them. These groups are for finding a module; the dependency layers remain documented in the [architecture guide](/en/guide/production/architecture). ## Not sure where to begin? * Building your first service: [Quickstart](/en/guide/quickstart) * Choosing synchronous calls or events: [Choosing a Communication Mechanism](/en/guide/service-communication/choosing-a-mechanism) * Finding exact fields and signatures: [API and Reference](/en/reference/) * Checking Node.js and Bun support: [Runtime Compatibility](/en/guide/runtime-compatibility) --- --- url: /en/guide/production/architecture.md description: >- Understand the Connectum service-process boundary, shared RPC path, extension seams, and local or remote catalog routing. --- # Connectum Runtime Architecture Connectum is a modular service runtime, not a deployment platform. Its central composition boundary is `createServer()`: it registers routes, protocols, and interceptors; selects the network transport; and coordinates startup, drain, and shutdown for one service process. This page explains the runtime relationships that matter when extending or operating that process. Exact configuration fields remain in the [generated core API](/en/api/@connectum/core/), while deployment products and platforms remain in their focused guides. ## Boundary and ownership The framework owns the behavior inside a Connectum service process: * transport selection and server lifecycle; * registration of typed services and protocol plugins; * the ordered server-interceptor chain; * the `Context` supplied to typed handlers; * local or remote service-call dispatch when a catalog is configured; * lifecycle integration for an explicitly supplied EventBus. Connectum does not own a gateway, service mesh, scheduler, message broker, or telemetry backend. Those systems connect through documented seams and can be selected independently. ## Runtime composition Every inbound network request follows one shared execution path. `TransportManager` owns the selected Node transport, `buildRoutes()` composes services and protocol registrations, the configured server interceptors run in order, and the matched route invokes a typed handler with a Connectum `Context`. Protocol modules such as Health Check and Reflection register additional routes through the same router contract. EventBus and OpenTelemetry are opt-in capabilities: the server coordinates the supplied EventBus lifecycle, while OTel instrumentation attaches through server/client interceptors and its provider. ### Network and in-process parity An in-process client does not open a socket. `createLocalTransport()` calls `createRouterTransport(routes)` and supplies the same `serverInterceptors` before reaching the handler. Client-side interceptors may additionally wrap the in-memory call. This shared route and interceptor boundary is the parity invariant: application behavior should not depend on whether a locally mounted service was reached over HTTP or through the in-process transport. See [In-Process Transport](/en/guide/production/in-process-transport) for the exact guarantee, limitations, and test utilities. ## Catalog call routing Handlers use the generated service catalog through `ctx.call` and `ctx.stream`. The per-server catalog dispatcher resolves the typed method and builds the outgoing call frame from the current handler context. Routing then depends on where the target service is mounted: 1. A locally mounted `typeName` uses the in-process transport and re-enters the same process's `routes`, server interceptors, and typed handler chain. One `createServer()` instance may register several service `typeName` values. 2. A service not mounted locally is passed to `remoteResolver`, which supplies the ConnectRPC `Transport` used for the remote call. The request then crosses the process boundary and enters the remote server through its resolver-supplied `Transport` and `connectNodeAdapter({ routes, interceptors })`. The incoming cancellation signal and remaining deadline cascade to catalog calls unless the caller supplies a stricter override. Inbound headers are not forwarded implicitly; only configured allow-listed headers and explicit call headers are propagated. The catalog is optional. A service process that hosts everything locally and makes no typed cross-service calls does not need one. Configuration and error semantics are owned by the [Service Catalog guide](/en/guide/service-communication/service-catalog), with resolver construction covered by [Remote Resolvers](/en/guide/service-communication/resolvers). ## Extension seams | Capability | Runtime attachment | External dependency | |---|---|---| | Server interceptors | Ordered ConnectRPC request chain | Optional identity, policy, or application services | | Protocol plugins | Register RPC routes and optional HTTP fallbacks | Protocol-specific clients and tooling | | Service catalog | Adds typed `ctx.call` / `ctx.stream` dispatch | Resolver-supplied remote transports | | EventBus | Supplied to `createServer()` and started/stopped with it | None for Memory; NATS, Kafka, Redis, or AMQP broker otherwise | | OpenTelemetry | Server/client interceptors plus provider | OTLP collector or console exporter | These are explicit capabilities rather than hidden runtime defaults. Install and compose only the modules required by the service. ## Deployment boundary Gateways, service meshes, Kubernetes, registries, and telemetry backends surround the process but are not framework prerequisites. Their placement depends on the deployment topology rather than on `createServer()` internals: * [Docker](/en/guide/production/docker) packages the process; * [Kubernetes](/en/guide/production/kubernetes) schedules it and drives probes; * [Envoy Gateway](/en/guide/production/envoy-gateway) provides an optional edge and REST transcoding path; * [Service Mesh](/en/guide/production/service-mesh) provides optional traffic policy and workload mTLS; * [Observability](/en/guide/observability) selects telemetry signals and backends. ## Build-time inputs Proto schemas, generated service descriptors, and the optional generated service catalog are build-time inputs to this runtime. They are deliberately outside the process diagrams: they define and generate the contracts consumed by route and catalog registration, but they do not execute in the request path. Start with [Scaffolding](/en/guide/scaffolding) for generation workflow and [Service Communication](/en/guide/service-communication) for choosing between synchronous catalog calls and asynchronous events. ## Related * [Server](/en/guide/server) — composition and lifecycle entry point * [Transport Matrix](/en/guide/production/transport-matrix) — HTTP/1.1, h2c, TLS/ALPN, and RPC-kind support * [Choosing a Communication Mechanism](/en/guide/service-communication/choosing-a-mechanism) — synchronous calls, events, and durable workflows * [Package decomposition ADR](/en/contributing/adr/003-package-decomposition) — package-boundary rationale --- --- 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. **[Documentation Style Guide](/en/contributing/documentation-style)** -- how to write package READMEs and docs pages 4. **[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 22+) ### 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/protocols/custom.md description: >- Extend the Connectum server with an advanced gRPC registration or HTTP fallback handler. --- # 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. This is an advanced framework extension point. To expose the standard reflection service, follow [Server reflection](/en/guide/protocols/reflection) rather than reimplementing it. ## 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/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 { Code, ConnectError } from '@connectrpc/connect'; 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 (recommended 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 Create a mock `next` function and invoke the interceptor directly: ::: runtime bun Use `bun test` and import the runner from `bun:test` instead of `node:test`. The rest of the example is unchanged -- `node:assert` works under Bun, and the Connectum mock helpers do not depend on `node:test`. ::: ```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 * [with-custom-interceptor example](https://github.com/Connectum-Framework/examples/tree/main/with-custom-interceptor) -- API key auth & rate limiting interceptors --- --- url: /en/contributing/parity-invariant.md --- # Cross-Transport Parity Invariant Connectum exposes two transports for ConnectRPC services: * the **HTTP/2 transport** — the production wire protocol; * the **in-process transport** — `createLocalTransport(server)` / `Server.client(ServiceDesc)`, which delivers requests directly into the registered route handlers without serialization or a socket. The framework guarantees a **Behavioural Parity** invariant between them: > For every observable behaviour of a ConnectRPC service, the in-process > transport produces results that structurally match the HTTP/2 transport. > Observable behaviours include response payloads, response headers and > trailers, `ConnectError` codes / messages / metadata / `details`, the order > of streaming messages, cancellation propagation, OpenTelemetry spans > (modulo the `connectum.transport` attribute), and metric label sets > (modulo the `transport` label). This invariant is **release-blocking**: any PR that breaks it must either be revised or accompanied by a documented carve-out in the spec. ## How the invariant is enforced 1. **`transportParityTest()` driver** (`@connectum/testing/parity`) runs every scenario twice — once over `createGrpcTransport({ baseUrl })` and once over `createLocalTransport(server)` — and performs a structural diff on the normalized result (response, headers, error, OTEL spans, metrics). 2. **Parity suite** — aggregated by `scripts/parity-suite.sh`, covering interceptors, validation, authorization, streaming, error mapping, coexistence, and OTEL. 3. **`parity-gate` CI job** (`.github/workflows/parity-gate.yml`) runs the suite on every pull request and every push to `main`. ## When you MUST add a parity scenario Any change that touches **observable RPC behaviour** must add (or extend) a `transportParityTest()` scenario in the appropriate file under `packages/testing/tests/parity/` (or `packages/otel/tests/parity/` for observability). This includes, but is not limited to: * a new interceptor in `@connectum/interceptors`; * a new authentication or authorization rule in `@connectum/auth` (programmatic or proto-declared); * a new validation rule wired through `protovalidate` / `buf.validate`; * a new OpenTelemetry instrument, attribute, or span event in `@connectum/otel`; * any change to `ConnectError` mapping in `@connectum/core` or interceptors; * any change to header / trailer propagation; * any change to streaming semantics (ordering, cancellation, back-pressure surfaces). If the change is purely transport-local — for example, an HTTP-only header that has no in-process analogue — document the carve-out in the parity test file as a code comment **and** in the spec. ## When parity does not apply A small set of behaviours are explicitly transport-specific and are **not** asserted by the parity driver: * Network-level concerns: TLS, HTTP/2 framing, TCP keepalive, `:authority` pseudo-header, real `req.url` host/port. * Wire-format concerns: gzip/identity content-encoding negotiation, message framing on the wire. * The synthetic origin `https://in-memory//` that the in-process transport injects for interceptors that read `req.url`. * The `connectum.transport` span attribute and the `transport` metric label, which differ by design and are stripped before structural diff. ## Pull request checklist Every PR that touches a service-observable surface must: * \[ ] add or extend a `transportParityTest()` scenario covering the change, **or** * \[ ] explicitly mark the change as `parity: N/A` in the PR description and justify why. The `parity-gate` CI job will block merge on any structural diff. ## References * [In-process transport guide](../guide/production/in-process-transport.md) * [Parity coverage report](./parity-coverage.md) * Internal design notes: see ADR for in-process transport (registry-based local invoke) --- --- url: /en/api/@connectum/interceptors/defaults.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / defaults # defaults Default interceptor chain factory Creates the interceptor chain in a fixed order: errorHandler → timeout → bulkhead → circuitBreaker → retry → fallback → validation → serializer. Only errorHandler and validation are enabled by default; resilience interceptors (timeout, bulkhead, circuitBreaker, retry) are opt-in — no hidden behavioral logic. ## 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 >= 11 ## 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 # >= 11 # 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 >= 22.13.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 A production `Dockerfile` is available in the [car-sharing example](https://github.com/Connectum-Framework/examples/tree/main/car-sharing). ::: ## Multi-Stage Dockerfile ### Recommended Layout Two-stage build: install dependencies in an isolated stage, then copy only production `node_modules` into a slim runtime image (`node:25-slim` on Node.js, `oven/bun:1-slim` on Bun) with a non-root user and health check. See [Dockerfile](https://github.com/Connectum-Framework/examples/blob/main/car-sharing/Dockerfile) for the full listing. Key highlights: ::: runtime \== node * **Stage 1 (deps)** -- `pnpm install --frozen-lockfile --prod` for reproducible, minimal dependencies * **Stage 2 (runtime)** -- non-root `node` user, `curl`-based HEALTHCHECK against `/healthz`, native TypeScript via `node src/index.ts` * Environment defaults: `NODE_ENV=production`, `PORT=5000`, `LOG_FORMAT=json`, health and graceful shutdown enabled \== bun * **Stage 1 (deps)** -- `bun install --frozen-lockfile` for reproducible dependencies * **Stage 2 (runtime)** -- `oven/bun:1-slim`, `curl`-based HEALTHCHECK against `/healthz`, TypeScript executed directly via `bun run src/index.ts` * Environment defaults: `NODE_ENV=production`, `PORT=5000`, `LOG_FORMAT=json`, health and graceful shutdown enabled The reference `Dockerfile` in the examples repository targets Node.js; the Bun variant above mirrors it stage for stage. ::: :::: runtime node ::: 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 22+ 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. ::: :::: ### HEALTHCHECK on a Plaintext h2c Server If your server runs plaintext h2c -- `allowHTTP1: false` without TLS, the recommended posture for internal gRPC services -- the probe **must speak HTTP/2**: ```dockerfile HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 \ CMD curl -fsS --http2-prior-knowledge http://localhost:${PORT:-5000}/healthz || exit 1 ``` ::: danger Do not probe an h2c server with `wget` `wget` speaks HTTP/1.1 only. Against an h2c listener it receives an empty status line and **still exits 0**, so the probe passes for any URL on an open port -- including a path that does not exist, and including a service reporting `NOT_SERVING`. The container is then reported healthy while being unable to serve. Verified against a running server: `wget -q --spider .../healthz` and `wget -q --spider .../does-not-exist` both exit 0, while `curl -fsS --http2-prior-knowledge` returns 200 for the first and fails on the second. `curl` alone is not enough either -- without `--http2-prior-knowledge` it reports `Received HTTP/0.9 when not allowed` and the container never becomes healthy. ::: `-f` makes curl fail on a non-2xx status, which is what distinguishes a healthy service from a sick one: `/healthz` answers **200** for `SERVING` and **503** for `NOT_SERVING` and `UNKNOWN`. On a server that accepts HTTP/1.1 (`allowHTTP1: true`, the default) the plain `curl -fsS http://localhost:${PORT:-5000}/healthz` is correct. If one image serves both postures, chain the two probes with `||`. ### Runtime Command ::: runtime \== node ```dockerfile # Node.js 25+ (native TypeScript for your own .ts files) CMD ["node", "src/index.ts"] # tsx (works on Node.js 22+) CMD ["npx", "tsx", "src/index.ts"] ``` When using **tsx**, you can use any Node.js 22+ base image (e.g., `node:22-slim`, `node:24-slim`). Since `@connectum/*` packages ship compiled JavaScript, no special loader is needed for any runtime. ::: danger tsx must be a regular dependency, not a devDependency A production image installs with `--omit=dev` (or `--prod`), so a tsx left in `devDependencies` is **not in the image**. `npx` then tries to download it from the registry when the container starts: with no network the container fails to start at all, and with network every start silently fetches a package. Verified in a container: with tsx in `devDependencies` and `--network none`, the image exits with `request to https://registry.npmjs.org/tsx failed`. Moving tsx to `dependencies` makes the same image start normally. `connectum init --node-exec tsx` puts tsx in `devDependencies`, which is right for a project that runs from source but wrong for a `--prod` image -- move it before containerising, or pin the run command to the resolved binary (`CMD ["./node_modules/.bin/tsx", "src/index.ts"]`) so a missing dependency fails loudly at build time instead of at start-up. ::: \== bun ```dockerfile FROM oven/bun:1-slim AS runtime CMD ["bun", "run", "src/index.ts"] ``` Code generation runs the same way inside the image -- `RUN bunx buf generate`. Since `@connectum/*` packages ship compiled JavaScript, no loader or register hook is needed. ::: ### Alpine Variant (Node.js Images) If you need a smaller image and do not depend on native modules requiring glibc, use the Alpine variant: swap both `FROM node:25-slim` lines in the [Dockerfile](https://github.com/Connectum-Framework/examples/blob/main/car-sharing/Dockerfile) for `node:25-alpine`, and install `curl` with `apk add --no-cache curl` instead of `apt-get`. Alpine's BusyBox applets differ from the GNU builds, so re-verify the HEALTHCHECK actually reports `unhealthy` for a bad URL rather than only checking that it passes for a good one. ### Image Size Comparison (Node.js Images) | 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. A minimal `.dockerignore` excludes `node_modules`, `**/*.test.ts`, `.git`, and editor/CI files so they never enter the build context. ## Docker Compose for Local Development For local development you typically compose your Connectum services with an observability stack. A representative `docker-compose.yml` wires up: | 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 | Point each service's `OTEL_EXPORTER_OTLP_ENDPOINT` at the collector (`http://otel-collector:4318`) and let the collector fan traces out to Jaeger and metrics to Prometheus. For a runnable observability stack wired to Connectum services, see the [o11y-coroot example](https://github.com/Connectum-Framework/examples/tree/main/o11y-coroot) (it uses Coroot + ClickHouse + Prometheus rather than the Jaeger/Grafana stack tabulated above). ### OTel Collector Configuration A minimal collector config declares an OTLP receiver, a `batch` processor, and exporters for traces and metrics. The collector receives OTLP traces and metrics, batches them, and exports them to your tracing and metrics backends. ## 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. ::: ## Runtime Configuration Keep the container responsible for supplying configuration, not documenting a second copy of every field. The canonical owners are: * [Server configuration](/en/guide/server/configuration) for listen, logging, health, and shutdown environment values; * [Runtime compatibility](/en/guide/runtime-compatibility) for supported Node.js and Bun execution modes; * [Observability backends](/en/guide/observability/backends) for OpenTelemetry exporters and endpoints. At minimum set a production environment, a stable service name, and the intended listen port; pass secrets through the deployment platform rather than the image. ## 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/contributing/documentation-style.md description: >- Content types, ownership, navigation, and review rules for Connectum documentation. --- # Documentation Style Guide Connectum documentation serves two readers at once: someone completing a task for the first time and someone looking up one exact interface or option. Pages stay useful when each has one reader outcome, one content type, and one canonical owner. The source code is the technical oracle. Verify public symbols and defaults against `packages//src`, exports, and `package.json`. Generated TypeDoc is the canonical exact API reference; hand-written pages teach, explain, and route readers to it. ## Content types Choose one type before drafting a page. Do not combine a tutorial, an exhaustive reference, and architecture history on one URL. | Type | Reader outcome | Required content | Exclude | Location | |---|---|---|---|---| | Tutorial | Complete a bounded learning path | Prerequisites, goal, sequential steps, verification, next steps | Optional production hardening, exhaustive options | `en/guide/**` | | How-to | Accomplish one real task | Problem, prerequisites, focused steps, verification, failure path | Product pitch, unrelated alternatives, full API tables | `en/guide/**` | | Concept | Understand a mental model or decision | Context, model, boundaries, links to tasks | Step-by-step setup, exhaustive symbols | `en/guide/**` | | Package hub | Decide whether and how to use one module | Purpose, install variants, minimal example when applicable, load-bearing entry points, Learn / Configure / API links | Exhaustive option/type catalog, copied guide chapters | `en/packages/.md` | | Reference | Look up exact signatures, fields, defaults, or compatibility | Generated symbols or a deliberately maintained matrix | Narrative tutorials, duplicated rationale | `en/api/**` or a named canonical matrix | | Migration | Determine whether an upgrade requires action | Affected versions, required action, before/after, verification | Complete release history and unrelated features | `en/migration/**` | | ADR | Understand why an architectural decision was made | Status, context, decision, consequences, alternatives | Current task instructions that belong in guides | `en/contributing/adr/**` | Contributor workflow pages are a separate audience. They describe how to work on Connectum itself and use the repository's supported Node.js and pnpm toolchain, not consumer-facing runtime or package-manager choices. ## Page templates ### Tutorial ```md --- title: description: docType: tutorial --- # ## Prerequisites ## 1. ## 2. ## Verify the result ## Troubleshooting ## Next steps ``` Keep the mandatory path linear. Production options belong in Next steps unless the tutorial cannot succeed safely without them. ### How-to ```md --- title: description: docType: how-to --- # ## Before you begin ## Configure ## Verify ## Troubleshooting ## Learn / Configure / API reference ``` ### Concept ```md --- title: description: docType: concept --- # ## Why it exists ## Mental model ## Boundaries and trade-offs ## Apply the concept ``` ### Package hub ```md --- title: @connectum/ description: docType: package-hub --- # @connectum/ ## Install ## Start here ## Key entry points ## Learn / Configure / API reference ## Related modules ``` List only entry points a reader needs to orient themselves. Link every exact interface or function to generated TypeDoc. Architecture layer may appear as metadata, but it does not determine reader navigation. ### Migration ```md --- title: description: docType: migration --- # ## Does this apply to you? ## Required changes ## Before and after ## Verify the upgrade ## Related release notes ``` ### ADR Use Status, Context, Decision, Consequences, Alternatives, and References. An ADR records rationale; link to current guides rather than turning the ADR into a second operational manual. ## Canonical ownership When information could appear in several places, these locations win: | Information | Canonical owner | Other pages may contain | |---|---|---| | Function signatures, option fields, exported types, defaults | Generated API reference | A task-essential subset plus a direct API link | | Node.js/Bun support and known runtime limitations | [Runtime Compatibility](/en/guide/runtime-compatibility) | A one-line prerequisite and canonical link | | Request/response versus events choice | [Choosing a Communication Mechanism](/en/guide/service-communication/choosing-a-mechanism) | A contextual recommendation and link | | Broker comparison | [Event Adapters](/en/guide/events/adapters) | Adapter-specific setup only | | Package installation and orientation | Package hub | A command required by the current task | | Required upgrade actions | [Migration](/en/migration/) | A release-note link to the migration | | Architectural rationale | ADR | Current behavior and an ADR link | If two pages contain the same complete table or explanation, keep the better canonical version and replace the other with task context plus a link. ## Learn / Configure / API reference pattern End module and task pages with routes that match the reader's next intent: ```md ## Learn / Configure / API reference - **Learn:** [How authentication fits the request lifecycle](/en/guide/auth) - **Configure:** [Configure JWT authentication](/en/guide/auth/jwt) - **API reference:** [`JwtAuthInterceptorOptions`](/en/api/@connectum/auth/interfaces/JwtAuthInterceptorOptions) ``` A guide may omit one route if it is genuinely irrelevant. A package hub should normally include all three. Link to the exact TypeDoc symbol when the text names one; link to the package API index only when several symbols are equally relevant. ## Page and navigation conventions * Add `title`, `description`, and `docType` frontmatter to hand-written pages. * Use one H1. Keep headings descriptive and preserve established anchors during rewrites; add explicit `{#legacy-anchor}` headings when compatibility requires it. * Use root-relative internal links beginning with `/en/`, without `.md`. * Put every user-facing page in a logical sidebar group, unless it is a documented compatibility page intentionally excluded from navigation. * Use task language in navigation. Package dependency layers belong in architecture content, not as the primary package taxonomy. * Use `::: tip`, `::: info`, `::: warning`, and `::: danger` for meaningful callouts. Do not use a callout as decoration. * Generated files under `en/api/**` are never edited manually. ## Runtime variants Use `::: runtime` only when execution differs between Node.js and Bun. Runtime is independent from the tool used to install dependencies. ````md ::: runtime == node ```bash node --test tests/ ``` == bun ```bash bun test tests/ ``` ::: ```` Rules: * A grouped block contains both `== node` and `== bun` with equivalent outcomes. * Do not put headings inside variant blocks; duplicate headings create unstable outline anchors. * Use a normal block when the commands are identical. * A single-runtime block such as `::: runtime bun` is a short, always-visible compatibility note. * Verify Bun commands before publishing them. * Contributor pages do not use consumer runtime variants. ## Package-manager variants Use `::: pm` for installation or script commands that differ among npm, pnpm, and bun. Each group must contain all three tools and perform the same operation. ````md ::: pm == npm ```bash npm install @connectum/core ``` == pnpm ```bash pnpm add @connectum/core ``` == bun ```bash bun add @connectum/core ``` ::: ```` Hoist commands that do not vary. Do not nest package-manager blocks inside code groups. Build-time validation checks completeness, the command tool, and semantic parity between tabs. ## Source accuracy ### Diagrams and process flows Use Mermaid for process flows, state machines, sequences, and architecture diagrams. Do not represent a diagram as aligned text, Unicode arrows, or ASCII boxes inside a plain code fence. Mermaid diagrams inherit light/dark colors, remain readable at narrow widths, and provide a keyboard-accessible fullscreen view. Use a fenced text block only for literal command output or a file tree. Use a generated image only when the subject cannot be expressed clearly in Mermaid; include useful alt text, preserve the editable source, and verify both themes. #### The shared diagram theme Every diagram is painted by one theme, configured centrally. Write the diagram; do not write its appearance. Typography, node surfaces and borders, subgraph framing, edge routing, arrowheads, edge labels, and the sequence and state primitives all arrive from the shared configuration, in both light and dark mode and in the fullscreen view. Never put a literal color in a diagram. A `fill:#4a90d9` or `stroke:red` becomes an inline attribute on the rendered node, which outranks the theme -- the node then keeps its light-mode color when the reader switches to dark. A per-diagram `%%{init: ...}%%` theme directive is rejected for the same reason. Build validation fails on both. When a node genuinely needs to stand apart, use one of the five shared variants: | Variant | Use for | | --- | --- | | `accent` | the subject of the page, or the foundation everything else builds on | | `positive` | a success destination or a healthy terminal state | | `warning` | a degraded, deferred, or rejected-but-expected path | | `critical` | a failure path or an unrecoverable state | | `muted` | a de-emphasised element: private, deprecated, or out of scope | Apply one with `class` or `:::`, and never let color be the only carrier of the meaning: ```mermaid flowchart LR Request([Request]) --> Validate{Schema valid?} Validate -->|valid| Handler[Service handler] Validate -->|invalid| Reject[Reject: INVALID_ARGUMENT] class Validate accent class Handler positive class Reject warning ``` A reader who cannot distinguish the colors must still get the same answer from the node text, the edge labels, or the subgraph titles. If the distinction is already carried by structure -- a subgraph named `Layer 0: Foundation`, for instance -- leave the nodes unstyled rather than repeating it in color. #### Styling is not layout The shared theme controls how a diagram looks, never where anything sits. Crossing edges, a label crammed against an arrowhead, a route that implies the wrong order, or a node colliding with an unrelated edge are all defects in the diagram source. Fix them by reordering nodes, reversing an edge, changing the direction, splitting the diagram, or shortening a label. Do not add page-local CSS, and do not move anything after render. Give arrows room to be seen. A connection needs a visible line segment and an arrowhead that touches neither node; the shared spacing provides this, so a collapsed arrow means the diagram is packing too much into one rank. Prefer `TD` for anything deeper than about five stages -- a long `LR` chain scales down to unreadable inside the documentation column, even though the fullscreen view can recover it. Documentation contract failures are user-facing defects. 1. Verify every named symbol, option, field, default, and export against current package source and metadata. Do not document removed APIs. 2. Check code blocks, imports, `.env` examples, and diagrams as carefully as prose. 3. The license is Apache-2.0. 4. Published packages support Node.js `>=22.13.0`. Consumer projects that execute TypeScript source directly follow the higher development/runtime prerequisite stated by the current Quickstart and Runtime Compatibility page. 5. The default interceptor order and enabled defaults must agree with current source and [ADR-024](/en/contributing/adr/024-auth-authz-strategy). 6. Consumer examples use the generated import extension configured by their `buf.gen.yaml`; current TypeScript-direct examples use `.ts`. 7. Never infer a claim from a test fixture when production source provides the contract. ## Redirect and retirement rules The site is currently a static GitHub Pages deployment. A moved route therefore keeps a compatibility page unless the hosting layer gains real HTTP redirects. Before changing a URL: 1. Record the old route, target, inbound links, and anchors in the migration matrix. 2. Update internal links to the canonical target. 3. Keep a compatibility page with a canonical URL and `noindex, follow`. 4. Exclude the compatibility page from primary navigation, local search, and LLM navigation outputs. 5. Preserve fragments where the destination still has an equivalent section. Do not describe a client-side compatibility page as a permanent HTTP redirect. ## Review checklist ### Reader and structure * \[ ] The page promises and delivers one reader outcome. * \[ ] `docType` matches the content and location. * \[ ] Beginner steps precede optional production or expert detail. * \[ ] Navigation label, title, H1, and contextual links agree. ### Accuracy and duplication * \[ ] Public symbols, options, defaults, imports, and versions were checked against source. * \[ ] Exact API detail links to TypeDoc instead of being copied. * \[ ] Runtime, broker, migration, and architecture facts defer to their canonical owners. * \[ ] Existing complete explanations were consolidated rather than repeated. ### Variants and accessibility * \[ ] Runtime and package-manager variants are complete and semantically equivalent. * \[ ] Heading order is logical and established anchors remain valid. * \[ ] Images have useful alt text or are explicitly decorative. * \[ ] Process, state, sequence, and architecture diagrams use Mermaid instead of aligned text. * \[ ] Diagrams carry no literal colors; any semantic variant is also readable without color. * \[ ] Diagram labels, arrows, and routes were checked in both themes; collisions were fixed in the source. * \[ ] Links and controls have visible keyboard focus and meaningful labels. * \[ ] Mobile layouts have usable touch targets and no horizontal overflow. * \[ ] Motion is non-essential and respects reduced-motion preferences. ### Delivery * \[ ] Internal links, target anchors, and compatibility routes validate. * \[ ] Local search finds both task phrases and exact symbols. * \[ ] Sitemap and LLM outputs contain canonical pages and exclude compatibility noise. * \[ ] The production build succeeds in light/dark desktop/mobile review. ## Documented version and release checklist `.vitepress/data/site.json` is the single maintained source for the documented Connectum release line. The documentation maintainer updates it in the release documentation change; components must not hard-code a second version string. Runtime floors and environment-specific support belong in the canonical [Runtime Compatibility](/en/guide/runtime-compatibility) matrix rather than shared site metadata. For every release line: 1. Update the documented release line when the portal begins targeting that line. 2. Regenerate TypeDoc from the targeted framework source; do not hand-edit output. 3. Add focused migration instructions for required user action. 4. Review Quickstart, Runtime Compatibility, package hubs, and representative interface links against the released source. 5. Run route/link/module/variant checks and the production build. 6. Verify search, sitemap, LLM outputs, and representative user journeys. 7. Review desktop/mobile light/dark screenshots before deployment. --- --- 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 Standalone Envoy Gateway pattern This guide describes a **standalone Envoy Gateway** for gRPC-JSON transcoding. The Connectum examples repository does not ship a dedicated Envoy Gateway example; the manifests below are illustrative templates you adapt to your cluster. For a service mesh that embeds Envoy as sidecars (mTLS, traffic management), see the [Service Mesh guide](./service-mesh.md) and the [car-sharing/istio](https://github.com/Connectum-Framework/examples/tree/main/car-sharing/istio) example instead. ::: 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`. Define CRUD operations for `OrderService` with REST path mappings (e.g. `POST /v1/orders`, `GET /v1/orders/{order_id}`) and buf validation rules, attaching a `google.api.http` option to each RPC. ::: tip The `google/api/http.proto` and `google/api/annotations.proto` files are available through buf BSR deps. Add `buf.build/googleapis/googleapis` to your `buf.yaml` dependencies and import them directly in your proto files without vendoring. ::: ## Step 2: Generate OpenAPI Spec Generate the canonical OpenAPI artifact with the dedicated [OpenAPI and authz workflow](/en/guide/openapi). Keeping generation in one guide prevents the gateway from drifting to a different plugin, schema version, or authorization mapping. The gateway consumes that artifact; it does not own its generation policy. ## 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 Define 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. ### GRPCRoute (Native gRPC Traffic) Route native gRPC traffic directly to the `OrderService` backend on port 5000 with a `GRPCRoute` matching on the fully qualified gRPC service name. ### HTTPRoute (REST-to-gRPC Transcoding) Map REST paths (`/v1/orders` prefix) to the gRPC backend for transcoding with an `HTTPRoute`, and route `/docs` to the Swagger UI service. Attach the route to both HTTP and HTTPS listeners. ## Step 5: Envoy Filter for gRPC-JSON Transcoding If your Envoy Gateway version supports `EnvoyPatchPolicy`, configure the transcoder filter directly. The 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). Alternatively, store the proto descriptor in a ConfigMap: create a ConfigMap holding the base64-encoded proto descriptor binary and mount it into the Envoy pods. ## Step 6: Swagger UI Deployment Deploy Swagger UI to serve the generated OpenAPI spec: 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. ## Rate Limiting Add rate limiting at the gateway level to protect your Connectum services. A `BackendTrafficPolicy` can apply local rate limiting (e.g. 100 requests per second) to the REST `HTTPRoute`. ## Load Balancing Configure request-level (L7) load balancing for gRPC backends. This is essential because gRPC uses persistent HTTP/2 connections. A `BackendTrafficPolicy` can apply RoundRobin load balancing to the REST `HTTPRoute`. ## 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 The extension used in generated protobuf imports (`#gen/*`) follows the `import_extension` option in your `buf.gen.yaml`. Run-directly projects like this one (and the example projects) generate `.ts` and import with `.ts`, which requires `allowImportingTsExtensions: true` in `tsconfig.json`. Compiled packages that ship JS instead set `import_extension=.js` and import with `.js`. For a run-directly project (`import_extension=.ts`): ```typescript // CORRECT: .ts for generated protobuf files (import_extension=.ts) import { GreeterService } from '#gen/greeter_pb.ts'; import type { SayHelloRequest } from '#gen/greeter_pb.ts'; import routes from '#gen/routes.ts'; // WRONG: extension that does not match buf.gen.yaml import_extension import { GreeterService } from '#gen/greeter_pb.js'; ``` 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/guide/events.md description: >- Understand the Connectum EventBus model and when asynchronous communication fits. --- # Events Connectum EventBus provides event-driven communication between microservices with proto-first routing, pluggable broker adapters, and a composable middleware pipeline. ## Architecture ```mermaid graph LR subgraph Service A HA["Event Handler A"] PA["eventBus.publish()"] end subgraph EventBus AD["EventAdapter"] R["EventRouter"] MW["Middleware Pipeline"] end subgraph Broker["Message Broker"] T1["Topic 1"] T2["Topic 2"] end subgraph Service B HB["Event Handler B"] PB["eventBus.publish()"] end PA -->|"publish(Schema, data)"| AD PB -->|"publish(Schema, data)"| AD AD <-->|"produce / consume"| T1 AD <-->|"produce / consume"| T2 AD -->|"inbound event"| R R -->|"topic to handler"| MW MW --> HA MW --> HB ``` The EventBus sits between your service handlers and the message broker. It handles: * **Serialization** -- automatically serializes/deserializes protobuf messages * **Routing** -- maps proto service methods to topic subscriptions * **Middleware** -- applies retry, DLQ, and custom middleware to every event * **Lifecycle** -- manages adapter connect/disconnect with the server, graceful drain on shutdown ## Core Concepts ### Proto-First Routing Event handlers are defined as proto services, mirroring ConnectRPC's `ConnectRouter` pattern. Each handler method receives a typed protobuf message and an `EventContext`: ```protobuf // proto/orders/v1/events.proto service OrderEventHandlers { rpc OnOrderCreated(OrderCreated) returns (google.protobuf.Empty); rpc OnOrderCancelled(OrderCancelled) returns (google.protobuf.Empty); } ``` ```typescript import type { EventRoute } from '@connectum/events'; import { OrderEventHandlers } from '#gen/orders/v1/events_pb.js'; const orderEvents: EventRoute = (events) => { events.service(OrderEventHandlers, { onOrderCreated: async (msg, ctx) => { console.log(`Order ${msg.orderId} created`); await ctx.ack(); }, onOrderCancelled: async (msg, ctx) => { console.log(`Order ${msg.orderId} cancelled`); await ctx.ack(); }, }); }; ``` ### Adapter Pattern The `EventAdapter` interface abstracts away broker-specific details. Adapters handle connection management, message serialization at the wire level, and subscription lifecycle. Broker-specific configuration (credentials, tuning, stream names) is passed to the adapter constructor: ```typescript // NATS JetStream import { NatsAdapter } from '@connectum/events-nats'; const adapter = NatsAdapter({ servers: 'nats://localhost:4222', stream: 'orders' }); // Kafka / Redpanda import { KafkaAdapter } from '@connectum/events-kafka'; const adapter = KafkaAdapter({ brokers: ['localhost:9092'], clientId: 'my-service' }); // Redis Streams / Valkey import { RedisAdapter } from '@connectum/events-redis'; const adapter = RedisAdapter({ url: 'redis://localhost:6379' }); // AMQP / RabbitMQ / LavinMQ import { AmqpAdapter } from '@connectum/events-amqp'; const adapter = AmqpAdapter({ url: 'amqp://localhost:5672' }); // In-memory (testing) import { MemoryAdapter } from '@connectum/events'; const adapter = MemoryAdapter(); ``` ### Middleware Pipeline Middleware wraps event handlers in an onion model. Built-in middleware provides retry with configurable backoff and dead letter queue routing. See the [middleware pipeline diagram](/en/guide/events/middleware#pipeline-order) for execution and error flow. Each middleware receives the raw event, the event context, and a `next()` function to call the inner handler. ### EventContext Every event handler receives an `EventContext` with explicit acknowledgment control: | Property | Description | |----------|-------------| | `eventId` | Unique event identifier | | `eventType` | Topic / event type name | | `publishedAt` | Publish timestamp | | `attempt` | Delivery attempt number (1-based) | | `metadata` | Event headers as `ReadonlyMap` | | `signal` | `AbortSignal` -- aborted on server shutdown | | `ack()` | Acknowledge successful processing | | `nack(requeue?)` | Negative acknowledge -- request redelivery | Both `ack()` and `nack()` are idempotent -- calling either multiple times after the first call has no effect. ## Adapter Comparison Use the canonical [Event Adapter selection matrix](/en/guide/events/adapters#adapter-comparison) for broker trade-offs and direct links to exact adapter options. This overview owns the EventBus mental model; it does not duplicate broker configuration. ## When to Use Events | Pattern | Use Case | Transport | |---------|----------|-----------| | **Request-response** | Synchronous queries, CRUD operations | gRPC / ConnectRPC | | **Pub/sub events** | Decoupled notifications, saga orchestration | EventBus | | **Streaming** | Real-time data feeds, change data capture | gRPC server streaming | Use EventBus when services need to react to events **asynchronously** without direct coupling. For synchronous communication, use [Service Communication](/en/guide/service-communication) with gRPC clients. ## Learn More * [Getting Started](/en/guide/events/getting-started) -- step-by-step setup tutorial * [Custom Topics](/en/guide/events/custom-topics) -- proto options for topic naming * [Middleware](/en/guide/events/middleware) -- retry, DLQ, custom middleware * [Adapters](/en/guide/events/adapters) -- detailed adapter comparison and configuration * [@connectum/events](/en/packages/events) -- Package Guide * [@connectum/events API](/en/api/@connectum/events/) -- Full API Reference --- --- 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/events-amqp/functions/AmqpAdapter.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpAdapter # Function: AmqpAdapter() > **AmqpAdapter**(`options`): `EventAdapter` Defined in: [packages/events-amqp/src/AmqpAdapter.ts:462](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/AmqpAdapter.ts#L462) Create an AMQP/RabbitMQ adapter for @connectum/events. ## Parameters ### options [`AmqpAdapterOptions`](../types/interfaces/AmqpAdapterOptions.md) AMQP adapter configuration ## Returns `EventAdapter` EventAdapter instance ## Examples ```typescript import { AmqpAdapter } from "@connectum/events-amqp"; import { createEventBus } from "@connectum/events"; const bus = createEventBus({ adapter: AmqpAdapter({ url: "amqp://guest:guest@localhost:5672" }), routes: [myRoutes], }); await bus.start(); ``` **External AMQP contract (AsyncAPI-style)** ```typescript const adapter = AmqpAdapter({ url: "amqp://broker:5672", exchange: "partner.direct", exchangeType: "direct", serialization: { contentType: "application/json" }, topology: { queues: [{ name: "partner.inbound.v1", durable: true, arguments: { "x-dead-letter-exchange": "partner.dlx", "x-dead-letter-routing-key": "inbound.dead", }, }], bindings: [{ queue: "partner.inbound.v1", source: "partner.direct", routingKey: "inbound" }], }, queueOverrides: { partner: { queue: "partner.inbound.v1" } }, // externalContract: emit only contract-specified properties (no envelope). publisherOptions: { persistent: true, mandatory: true, externalContract: true }, }); ``` --- --- 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:198](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L198) 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/test-fixtures/index/functions/assertConnectError.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / assertConnectError # Function: assertConnectError() > **assertConnectError**(`error`, `expectedCode`, `messagePattern?`): `asserts error is ConnectError` Defined in: [assertions.ts:44](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/assertions.ts#L44) Assert that a thrown value is a ConnectError with the expected gRPC status code and, optionally, a message matching a pattern. This is a TypeScript [assertion function](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates): after a successful call the compiler narrows `error` to `ConnectError`. **Note on message format**: ConnectError messages include a code prefix, e.g. `[not_found] user not found`. The `messagePattern` is matched against the full message string. Use a `RegExp` for flexible matching. ## Parameters ### error `unknown` The value to check (typically from a `catch` block). ### expectedCode `Code` Expected gRPC/Connect status code. ### messagePattern? `string` | `RegExp` Optional substring or RegExp to match against `error.message`. ## Returns `asserts error is ConnectError` ## Throws When any of the checks fail. ## Example ```ts import { Code, ConnectError } from "@connectrpc/connect"; import { assertConnectError } from "@connectum/testing"; try { await client.getUser({ id: "missing" }); } catch (err) { assertConnectError(err, Code.NotFound, "user not found"); // err is now typed as ConnectError console.log(err.code); // Code.NotFound } ``` --- --- url: /en/api/@connectum/testing/index/functions/assertConnectError.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / assertConnectError # Function: assertConnectError() > **assertConnectError**(`error`, `expectedCode`, `messagePattern?`): `asserts error is ConnectError` Defined in: test-fixtures/dist/index.d.ts:44 Assert that a thrown value is a ConnectError with the expected gRPC status code and, optionally, a message matching a pattern. This is a TypeScript [assertion function](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates): after a successful call the compiler narrows `error` to `ConnectError`. **Note on message format**: ConnectError messages include a code prefix, e.g. `[not_found] user not found`. The `messagePattern` is matched against the full message string. Use a `RegExp` for flexible matching. ## Parameters ### error `unknown` The value to check (typically from a `catch` block). ### expectedCode `Code` Expected gRPC/Connect status code. ### messagePattern? `string` | `RegExp` Optional substring or RegExp to match against `error.message`. ## Returns `asserts error is ConnectError` ## Throws When any of the checks fail. ## Example ```ts import { Code, ConnectError } from "@connectrpc/connect"; import { assertConnectError } from "@connectum/testing"; try { await client.getUser({ id: "missing" }); } catch (err) { assertConnectError(err, Code.NotFound, "user not found"); // err is now typed as ConnectError console.log(err.code); // Code.NotFound } ``` --- --- 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:160](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L160) 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:131](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L131) 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/otel/provider/functions/buildResourceAttributes.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [provider](../index.md) / buildResourceAttributes # Function: buildResourceAttributes() > **buildResourceAttributes**(`inputs`): `Record`<`string`, `string` | `number` | `boolean`> Defined in: [packages/otel/src/provider.ts:98](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L98) Build the flat resource-attribute record shared by traces, metrics, and logs. Precedence (lowest to highest): `service.name`/`service.version` → env (`OTEL_RESOURCE_ATTRIBUTES`, `OTEL_SERVICE_INSTANCE_ID`) → explicit `resourceAttributes` → explicit `instanceId`. ## Parameters ### inputs [`ResourceAttributeInputs`](../interfaces/ResourceAttributeInputs.md) ## Returns `Record`<`string`, `string` | `number` | `boolean`> --- --- 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/main/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/core/functions/collectStreamingMethods.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / collectStreamingMethods # Function: collectStreamingMethods() > **collectStreamingMethods**(`registry`): [`StreamingMethodInfo`](../interfaces/StreamingMethodInfo.md)\[] Defined in: [packages/core/src/TransportValidation.ts:106](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L106) Collect bidi-streaming methods from a DescFile registry (built during route registration). Client-streaming is NOT collected — the Connect protocol supports it over HTTP/1.1. ## Parameters ### registry readonly `DescFile`\[] ## Returns [`StreamingMethodInfo`](../interfaces/StreamingMethodInfo.md)\[] --- --- url: /en/api/@connectum/events/functions/composeMiddleware.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / composeMiddleware # Function: composeMiddleware() > **composeMiddleware**(`middlewares`, `handler`): (`event`, `ctx`) => `Promise`<`void`> Defined in: [packages/events/src/middleware.ts:24](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/middleware.ts#L24) Compose an array of middleware into a single handler. Middleware is applied from left to right (outer to inner). The innermost function is the actual event handler. Uses a dispatch pattern that guards against double next() invocation. ## Parameters ### middlewares [`EventMiddleware`](../types/type-aliases/EventMiddleware.md)\[] Middleware functions to compose ### handler (`event`, `ctx`) => `Promise`<`void`> The final handler (innermost) ## Returns Composed handler function (`event`, `ctx`) => `Promise`<`void`> --- --- 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/main/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 **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', }; }, }); ``` **Bearer token with default extractor** ```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/main/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 **RBAC with declarative 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' }, ], }); ``` --- --- url: /en/api/@connectum/events/functions/createBroadcastSubscribers.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / createBroadcastSubscribers # Function: createBroadcastSubscribers() > **createBroadcastSubscribers**(`options`): [`EventBus`](../types/interfaces/EventBus.md) & `EventBusLike`\[] Defined in: [packages/events/src/broadcast.ts:76](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L76) Build one `EventBus` per reactor (each with its own consumer group) so a single published event fans out to ALL reactors independently. The returned buses are NOT started — start them yourself (e.g. `await Promise.all(buses.map((b) => b.start()))`) and stop them on shutdown. Throws if two reactors share a consumer group (that would load-balance / steal instead of fanning out). ## Parameters ### options [`BroadcastSubscribersOptions`](../interfaces/BroadcastSubscribersOptions.md) ## Returns [`EventBus`](../types/interfaces/EventBus.md) & `EventBusLike`\[] ## Example ```typescript const buses = createBroadcastSubscribers({ adapter: () => NatsAdapter({ servers, stream: 'orders' }), reactors: [ { group: 'pricing', routes: [pricingRoutes] }, { group: 'audit', routes: [auditRoutes] }, { group: 'notify', routes: [notifyRoutes] }, ], }); await Promise.all(buses.map((bus) => bus.start())); ``` --- --- 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/main/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 **Server-side usage with createServer** ```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(); ``` **Client-side usage with transport** ```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/core/functions/createCatalogClient.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / createCatalogClient # Function: createCatalogClient() > **createCatalogClient**(`options`): [`CatalogClient`](../interfaces/CatalogClient.md) Defined in: [packages/core/src/catalogClient.ts:108](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogClient.ts#L108) Build a standalone [CatalogClient](../interfaces/CatalogClient.md) from a [ServiceCatalog](../type-aliases/ServiceCatalog.md) and a [RemoteResolver](../type-aliases/RemoteResolver.md). ## Parameters ### options [`CreateCatalogClientOptions`](../interfaces/CreateCatalogClientOptions.md) ## Returns [`CatalogClient`](../interfaces/CatalogClient.md) ## Example ```ts import { createCatalogClient, mapResolver } from "@connectum/core"; import { serviceCatalog } from "./gen/catalog.ts"; // @connectum/protoc-gen-catalog const client = createCatalogClient({ catalog: serviceCatalog, resolver: mapResolver({ "fleet.v1.FleetService": createGrpcTransport({ baseUrl: process.env.FLEET_ADDR }), }), }); // Fully typed off the generated catalog — same surface as ctx.call: const trip = await client.call("trip.v1.TripService/StartTrip", { vehicleId }); ``` --- --- 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:94](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/circuit-breaker.ts#L94) 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 By default only infrastructure errors trip the breaker (see [defaultFailurePredicate](defaultFailurePredicate.md)); business codes like invalid\_argument or not\_found never do. Customize via [CircuitBreakerOptions.failurePredicate](../../interfaces/CircuitBreakerOptions.md#failurepredicate). The circuit breaker is an outbound/client-side pattern: it protects the caller from a sick upstream and gives that upstream room to recover. On a server's inbound stack it degenerates into error-rate load shedding — prefer timeout + bulkhead for inbound protection. ## Parameters ### options? [`CircuitBreakerOptions`](../../interfaces/CircuitBreakerOptions.md) = `{}` Circuit breaker options ## Returns `Interceptor` ConnectRPC interceptor ## Examples **Client-side usage with transport (recommended placement)** ```typescript import { createConnectTransport } from '@connectrpc/connect-node'; import { createCircuitBreakerInterceptor } from '@connectum/interceptors'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [ createCircuitBreakerInterceptor({ threshold: 5, // Open after 5 consecutive failures halfOpenAfter: 30000, // Try again after 30 seconds }), ], }); ``` **Custom failure classification (compose with the default)** ```typescript import { Code, ConnectError } from '@connectrpc/connect'; createCircuitBreakerInterceptor({ // Never trip on upstream per-client rate limits failurePredicate: (err, def) => def(err) && !(err instanceof ConnectError && err.code === Code.ResourceExhausted), }); ``` --- --- url: /en/api/@connectum/auth/functions/createClientBearerInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createClientBearerInterceptor # Function: createClientBearerInterceptor() > **createClientBearerInterceptor**(`options`): `Interceptor` Defined in: [packages/auth/src/client-bearer-interceptor.ts:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/client-bearer-interceptor.ts#L51) Create a client interceptor that attaches a Bearer token to outgoing requests. The interceptor sets the `Authorization: Bearer ` header on every outgoing request. If a token factory function is provided instead of a static string, it is called before each request to support token refresh. ## Parameters ### options [`ClientBearerInterceptorOptions`](../interfaces/ClientBearerInterceptorOptions.md) Configuration with a static token or async token factory ## Returns `Interceptor` A ConnectRPC client Interceptor ## Examples **Static token** ```typescript import { createClientBearerInterceptor } from '@connectum/auth'; import { createConnectTransport } from '@connectrpc/connect-node'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [createClientBearerInterceptor({ token: 'my-static-jwt-token', })], }); ``` **Async token factory (refresh)** ```typescript import { createClientBearerInterceptor } from '@connectum/auth'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', interceptors: [createClientBearerInterceptor({ token: async () => { const { accessToken } = await refreshTokenIfNeeded(); return accessToken; }, })], }); ``` --- --- url: /en/api/@connectum/auth/functions/createClientGatewayInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createClientGatewayInterceptor # Function: createClientGatewayInterceptor() > **createClientGatewayInterceptor**(`options`): `Interceptor` Defined in: [packages/auth/src/client-gateway-interceptor.ts:52](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/client-gateway-interceptor.ts#L52) Create a client interceptor that attaches gateway auth headers to outgoing requests. Sets the following headers for service-to-service communication: * `x-gateway-secret` — shared secret for trust verification * `x-auth-subject` — authenticated subject identifier * `x-auth-roles` — JSON-encoded roles array (optional) These headers are consumed by the server-side [createGatewayAuthInterceptor](createGatewayAuthInterceptor.md) to reconstruct the auth context without re-authentication. ## Parameters ### options [`ClientGatewayInterceptorOptions`](../interfaces/ClientGatewayInterceptorOptions.md) Gateway auth configuration ## Returns `Interceptor` A ConnectRPC client Interceptor ## Example **Service-to-service call** ```typescript import { createClientGatewayInterceptor } from '@connectum/auth'; import { createConnectTransport } from '@connectrpc/connect-node'; const transport = createConnectTransport({ baseUrl: 'http://internal-service:5000', interceptors: [createClientGatewayInterceptor({ secret: process.env.GATEWAY_SECRET!, subject: 'order-service', roles: ['service', 'order-writer'], })], }); ``` --- --- 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:156](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L156) ## Parameters ### options? [`DefaultInterceptorOptions`](../interfaces/DefaultInterceptorOptions.md) = `{}` ## Returns `Interceptor`\[] --- --- 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/main/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 **Server-side usage with createServer** ```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/events/functions/createEventBus.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / createEventBus # Function: createEventBus() > **createEventBus**(`options`): [`EventBus`](../types/interfaces/EventBus.md) & `EventBusLike` Defined in: [packages/events/src/EventBus.ts:72](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/EventBus.ts#L72) Create an EventBus instance. ## Parameters ### options [`EventBusOptions`](../types/interfaces/EventBusOptions.md) EventBus configuration ## Returns [`EventBus`](../types/interfaces/EventBus.md) & `EventBusLike` EventBus instance implementing EventBusLike for server integration ## Example ```typescript import { createEventBus, MemoryAdapter } from '@connectum/events'; const eventBus = createEventBus({ adapter: MemoryAdapter(), routes: [myEventRoutes], middleware: { retry: { maxRetries: 3, backoff: 'exponential' }, dlq: { topic: 'my-service.dlq' }, }, }); await eventBus.start(); await eventBus.publish(UserCreatedSchema, { id: '1', email: 'a@b.c', name: 'Test' }); await eventBus.stop(); ``` --- --- url: /en/api/@connectum/events/functions/createEventContext.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / createEventContext # Function: createEventContext() > **createEventContext**(`init`): [`EventContext`](../types/interfaces/EventContext.md) Defined in: [packages/events/src/EventContext.ts:18](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/EventContext.ts#L18) Create an EventContext from raw event data. The ack/nack operations are idempotent -- calling either multiple times has no effect after the first call. ## Parameters ### init [`EventContextInit`](../types/interfaces/EventContextInit.md) ## Returns [`EventContext`](../types/interfaces/EventContext.md) --- --- url: /en/api/@connectum/test-fixtures/index/functions/createFakeMethod.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createFakeMethod # Function: createFakeMethod() > **createFakeMethod**(`service`, `name`, `options?`): `DescMethod` Defined in: [fake-service.ts:72](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/fake-service.ts#L72) Create a fake DescMethod descriptor attached to a service. When `options.register` is `true`, the method is pushed into `service.methods` and added to `service.method` (keyed by `localName`). This is required for tests that iterate over service methods (e.g., `getPublicMethods()`). ## Parameters ### service `DescService` The parent `DescService` (typically from [createFakeService](createFakeService.md)). ### name `string` The RPC method name (PascalCase, e.g. `"GetUser"`). ### options? [`FakeMethodOptions`](../../types/interfaces/FakeMethodOptions.md) Optional configuration for method kind and registration. ## Returns `DescMethod` A fake `DescMethod` suitable for unit/integration tests. ## Example ```ts import { createFakeService, createFakeMethod } from "@connectum/testing"; const svc = createFakeService(); const method = createFakeMethod(svc, "GetUser", { register: true }); // method.localName === "getUser" // svc.methods.length === 1 ``` --- --- url: /en/api/@connectum/testing/index/functions/createFakeMethod.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createFakeMethod # Function: createFakeMethod() > **createFakeMethod**(`service`, `name`, `options?`): `DescMethod` Defined in: test-fixtures/dist/index.d.ts:100 Create a fake DescMethod descriptor attached to a service. When `options.register` is `true`, the method is pushed into `service.methods` and added to `service.method` (keyed by `localName`). This is required for tests that iterate over service methods (e.g., `getPublicMethods()`). ## Parameters ### service `DescService` The parent `DescService` (typically from [createFakeService](createFakeService.md)). ### name `string` The RPC method name (PascalCase, e.g. `"GetUser"`). ### options? [`FakeMethodOptions`](../interfaces/FakeMethodOptions.md) Optional configuration for method kind and registration. ## Returns `DescMethod` A fake `DescMethod` suitable for unit/integration tests. ## Example ```ts import { createFakeService, createFakeMethod } from "@connectum/testing"; const svc = createFakeService(); const method = createFakeMethod(svc, "GetUser", { register: true }); // method.localName === "getUser" // svc.methods.length === 1 ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createFakeService.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createFakeService # Function: createFakeService() > **createFakeService**(`options?`): `DescService` Defined in: [fake-service.ts:34](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/fake-service.ts#L34) Create a fake DescService descriptor for testing. The returned object has the same shape as a real `DescService` produced by the protobuf compiler, but contains only the fields commonly accessed in interceptor and utility code. The `methods` array and `method` lookup map start empty; use [createFakeMethod](createFakeMethod.md) with `register: true` to populate them. ## Parameters ### options? [`FakeServiceOptions`](../../types/interfaces/FakeServiceOptions.md) Optional overrides for service name and typeName. ## Returns `DescService` A fake `DescService` suitable for unit/integration tests. ## Example ```ts import { createFakeService } from "@connectum/testing"; const svc = createFakeService({ typeName: "acme.v1.UserService" }); // svc.typeName === "acme.v1.UserService" // svc.name === "UserService" ``` --- --- url: /en/api/@connectum/testing/index/functions/createFakeService.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createFakeService # Function: createFakeService() > **createFakeService**(`options?`): `DescService` Defined in: test-fixtures/dist/index.d.ts:76 Create a fake DescService descriptor for testing. The returned object has the same shape as a real `DescService` produced by the protobuf compiler, but contains only the fields commonly accessed in interceptor and utility code. The `methods` array and `method` lookup map start empty; use [createFakeMethod](createFakeMethod.md) with `register: true` to populate them. ## Parameters ### options? [`FakeServiceOptions`](../interfaces/FakeServiceOptions.md) Optional overrides for service name and typeName. ## Returns `DescService` A fake `DescService` suitable for unit/integration tests. ## Example ```ts import { createFakeService } from "@connectum/testing"; const svc = createFakeService({ typeName: "acme.v1.UserService" }); // svc.typeName === "acme.v1.UserService" // svc.name === "UserService" ``` --- --- 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/main/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 **Server-side usage with createServer** ```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(); ``` **Client-side usage with transport** ```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/main/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 **Kong/Envoy gateway with shared secret** ```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/@connectum/healthcheck/functions/createHealthcheckManager.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/healthcheck](../../../index.md) / [@connectum/healthcheck](../index.md) / createHealthcheckManager # Function: createHealthcheckManager() > **createHealthcheckManager**(): [`HealthcheckManager`](../classes/HealthcheckManager.md) Defined in: [HealthcheckManager.ts:264](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/HealthcheckManager.ts#L264) 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/@connectum/healthcheck/functions/createHttpHealthHandler.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/healthcheck](../../../index.md) / [@connectum/healthcheck](../index.md) / createHttpHealthHandler # Function: createHttpHealthHandler() > **createHttpHealthHandler**(`manager`, `healthPaths?`): `HttpHandler` Defined in: [httpHandler.ts:58](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/httpHandler.ts#L58) 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/createInternalAuthInterceptor.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / createInternalAuthInterceptor # Function: createInternalAuthInterceptor() > **createInternalAuthInterceptor**(`options`): `Interceptor` Defined in: [packages/auth/src/internal-auth-interceptor.ts:89](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/internal-auth-interceptor.ts#L89) Create an internal (service-to-service) authentication interceptor. For methods matched by `internalMethods`, the configured `trustSource` authorizes the call and sets an `AuthContext`. A trust source returning `null` (or throwing) is rejected as `Code.Unauthenticated`. Non-internal methods are a no-op pass-through. MUST run BEFORE `createProtoAuthzInterceptor`: the internal interceptor populates the `AuthContext` that proto-authz's `internal` rule consumes. Each trust-source factory strips its own trust header after extraction on the internal path (accept and reject), to prevent a spoofed marker from being propagated downstream. NOTE: for NON-internal methods this interceptor is a pure pass-through and does NOT strip any trust headers — a request to a `public`/gated method carrying a forged identity header passes through untouched. In the supported deployments the mesh sidecar (or an upstream gateway) terminates the trust boundary and scrubs these headers on every route; do not rely on this interceptor to sanitize non-internal routes. ## Parameters ### options [`InternalAuthInterceptorOptions`](../interfaces/InternalAuthInterceptorOptions.md) Internal auth configuration. ## Returns `Interceptor` ConnectRPC interceptor. ## Examples **Mesh deployment (production default)** ```typescript import { createInternalAuthInterceptor, meshIdentityTrust, getInternalMethods } from '@connectum/auth'; const internalAuth = createInternalAuthInterceptor({ internalMethods: getInternalMethods(services), trustSource: meshIdentityTrust({ allowlist: [ { principal: 'cluster.local/ns/default/sa/trips', roles: ['worker'] }, ], }), }); ``` **Non-mesh, per-service signed tokens** ```typescript import { createInternalAuthInterceptor, signedTokenTrust, getInternalMethods } from '@connectum/auth'; const internalAuth = createInternalAuthInterceptor({ internalMethods: getInternalMethods(services), trustSource: signedTokenTrust({ issuers: { 'trips-service': { jwksUri: 'https://trips/.well-known/jwks.json', claimsMapping: { roles: 'roles' } }, 'billing-service': { jwksUri: 'https://billing/.well-known/jwks.json' }, }, }), }); ``` --- --- 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/main/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 **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', }, }); ``` **HMAC secret (testing / simple setups)** ```typescript const jwtAuth = createJwtAuthInterceptor({ secret: process.env.JWT_SECRET, issuer: 'my-service', }); ``` --- --- url: /en/api/@connectum/testing/index/functions/createLocalClient.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createLocalClient # Function: createLocalClient() > **createLocalClient**<`T`>(`server`, `service`): `Client`<`T`> Defined in: [testing/src/createLocalClient.ts:38](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/createLocalClient.ts#L38) Create an in-process ConnectRPC client for a service registered on the given Server. ## Type Parameters ### T `T` *extends* `DescService` ## Parameters ### server `Server` A server created via `createServer({...})`. Does not need to be started. ### service `T` The proto service descriptor (e.g. `GreeterService`). ## Returns `Client`<`T`> A typed ConnectRPC `Client` that invokes handlers via the in-memory pipe. ## Example ```typescript import { createServer } from "@connectum/core"; import { createLocalClient } from "@connectum/testing"; const server = createServer({ services: [greeterRoutes] }); const client = createLocalClient(server, GreeterService); const res = await client.sayHello({ name: "world" }); ``` --- --- url: /en/api/@connectum/core/functions/createLocalTransport.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / createLocalTransport # Function: createLocalTransport() > **createLocalTransport**(`server`, `options?`): `Transport` Defined in: [packages/core/src/localTransport.ts:104](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/localTransport.ts#L104) Create an in-process ConnectRPC `Transport` over the services already registered on the given Connectum `Server`. The transport is safe to use before `server.start()` — it never opens a TCP/UDP port or HTTP/2 session. Server-side interceptors configured via `createServer({ interceptors })` are applied inside the handler chain; `options.interceptors` are applied on the client side of the call. Headers are propagated via `Headers` objects through the in-memory pipe; the wrapped `createRouterTransport` already clones headers at the call boundary, providing mutation isolation between client and server. The synthetic origin observed by interceptors reading `req.url` is `https://in-memory//` (set by the underlying ConnectRPC router transport — see `@connectrpc/connect`'s `router-transport.ts`). ## Parameters ### server [`Server`](../types/interfaces/Server.md) A server created via `createServer({...})`. ### options? [`CreateLocalTransportOptions`](../interfaces/CreateLocalTransportOptions.md) Optional client-side interceptors. ## Returns `Transport` A ConnectRPC `Transport` suitable for `createClient(service, transport)`. --- --- 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/main/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 **Server-side usage with createServer** ```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(); ``` **Client-side usage with transport** ```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/main/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 **Auth per service** ```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], }); ``` **Resilience per method** ```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/main/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/testing/index/functions/createMockContext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockContext # Function: createMockContext() > **createMockContext**(`options`): `Context` Defined in: [testing/src/mockContext.ts:68](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockContext.ts#L68) Create a Context whose `ctx.call` / `ctx.stream` resolve against the given mocks. Pass it as the second argument to a handler under test. ## Parameters ### options [`CreateMockContextOptions`](../interfaces/CreateMockContextOptions.md) ## Returns `Context` ## Example ```ts const ctx = createMockContext({ catalog: defineCatalog({ [InventoryService.typeName]: InventoryService }), mocks: [mockService(InventoryService, { getStock: () => create(StockSchema, { units: 7 }) })], }); const res = await orderHandler(create(CreateOrderSchema, { sku: "x" }), ctx); ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockDescField.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockDescField # Function: createMockDescField() > **createMockDescField**(`localName`, `options?`): `DescField` Defined in: [mock-desc.ts:62](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-desc.ts#L62) Create a mock DescField descriptor. Produces a minimal object that satisfies the `DescField` shape expected by ConnectRPC interceptors and protobuf utilities. ## Parameters ### localName `string` The field's local (camelCase) name. ### options? [`MockDescFieldOptions`](../../types/interfaces/MockDescFieldOptions.md) Optional overrides for field number, scalar type, and sensitivity. ## Returns `DescField` A mock `DescField` object. ## Example ```ts import { createMockDescField } from "@connectum/testing"; const field = createMockDescField("userId", { type: "int32", fieldNumber: 1 }); // field.localName === "userId" // field.scalar === 5 (INT32) ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockDescField.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockDescField # Function: createMockDescField() > **createMockDescField**(`localName`, `options?`): `DescField` Defined in: test-fixtures/dist/index.d.ts:182 Create a mock DescField descriptor. Produces a minimal object that satisfies the `DescField` shape expected by ConnectRPC interceptors and protobuf utilities. ## Parameters ### localName `string` The field's local (camelCase) name. ### options? [`MockDescFieldOptions`](../interfaces/MockDescFieldOptions.md) Optional overrides for field number, scalar type, and sensitivity. ## Returns `DescField` A mock `DescField` object. ## Example ```ts import { createMockDescField } from "@connectum/testing"; const field = createMockDescField("userId", { type: "int32", fieldNumber: 1 }); // field.localName === "userId" // field.scalar === 5 (INT32) ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockDescMessage.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockDescMessage # Function: createMockDescMessage() > **createMockDescMessage**(`typeName`, `options?`): `DescMessage` Defined in: [mock-desc.ts:109](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-desc.ts#L109) Create a mock DescMessage descriptor with all required structural properties. **Important**: the returned object always includes `members: []` which is required by `create()` from `@bufbuild/protobuf` — without it the runtime crashes. ## Parameters ### typeName `string` Fully-qualified protobuf type name (e.g. `"acme.v1.User"`). ### options? [`MockDescMessageOptions`](../../types/interfaces/MockDescMessageOptions.md) Optional field and oneof definitions. ## Returns `DescMessage` A mock `DescMessage` object. ## Example ```ts import { createMockDescMessage } from "@connectum/testing"; const msg = createMockDescMessage("acme.v1.User", { fields: [ { name: "id", type: "int32" }, { name: "email", type: "string" }, ], }); // msg.typeName === "acme.v1.User" // msg.name === "User" // msg.fields === [DescField, DescField] ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockDescMessage.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockDescMessage # Function: createMockDescMessage() > **createMockDescMessage**(`typeName`, `options?`): `DescMessage` Defined in: test-fixtures/dist/index.d.ts:210 Create a mock DescMessage descriptor with all required structural properties. **Important**: the returned object always includes `members: []` which is required by `create()` from `@bufbuild/protobuf` — without it the runtime crashes. ## Parameters ### typeName `string` Fully-qualified protobuf type name (e.g. `"acme.v1.User"`). ### options? [`MockDescMessageOptions`](../interfaces/MockDescMessageOptions.md) Optional field and oneof definitions. ## Returns `DescMessage` A mock `DescMessage` object. ## Example ```ts import { createMockDescMessage } from "@connectum/testing"; const msg = createMockDescMessage("acme.v1.User", { fields: [ { name: "id", type: "int32" }, { name: "email", type: "string" }, ], }); // msg.typeName === "acme.v1.User" // msg.name === "User" // msg.fields === [DescField, DescField] ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockDescMethod.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockDescMethod # Function: createMockDescMethod() > **createMockDescMethod**(`name`, `options?`): `DescMethod` Defined in: [mock-desc.ts:172](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-desc.ts#L172) Create a mock DescMethod descriptor. When `input` or `output` are not provided, default mock messages are created automatically based on the method name (e.g. `test.GetUserRequest` / `test.GetUserResponse`). ## Parameters ### name `string` The RPC method name (PascalCase by convention). ### options? [`MockDescMethodOptions`](../../types/interfaces/MockDescMethodOptions.md) Optional overrides for kind, input/output, and redaction. ## Returns `DescMethod` A mock `DescMethod` object. ## Example ```ts import { createMockDescMethod, createMockDescMessage } from "@connectum/testing"; const method = createMockDescMethod("GetUser"); // method.name === "GetUser" // method.localName === "getUser" // method.methodKind === "unary" const streaming = createMockDescMethod("ListUsers", { kind: "server_streaming", }); ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockDescMethod.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockDescMethod # Function: createMockDescMethod() > **createMockDescMethod**(`name`, `options?`): `DescMethod` Defined in: test-fixtures/dist/index.d.ts:236 Create a mock DescMethod descriptor. When `input` or `output` are not provided, default mock messages are created automatically based on the method name (e.g. `test.GetUserRequest` / `test.GetUserResponse`). ## Parameters ### name `string` The RPC method name (PascalCase by convention). ### options? [`MockDescMethodOptions`](../interfaces/MockDescMethodOptions.md) Optional overrides for kind, input/output, and redaction. ## Returns `DescMethod` A mock `DescMethod` object. ## Example ```ts import { createMockDescMethod, createMockDescMessage } from "@connectum/testing"; const method = createMockDescMethod("GetUser"); // method.name === "GetUser" // method.localName === "getUser" // method.methodKind === "unary" const streaming = createMockDescMethod("ListUsers", { kind: "server_streaming", }); ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockFn.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockFn # Function: createMockFn() > **createMockFn**<`F`>(`impl`): [`MockFn`](../interfaces/MockFn.md)<`F`> Defined in: [mock-compat.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-compat.ts#L54) Create a portable mock function that wraps `impl` and records every call. ## Type Parameters ### F `F` *extends* (...`args`) => `any` ## Parameters ### impl `F` The underlying implementation to delegate to. ## Returns [`MockFn`](../interfaces/MockFn.md)<`F`> A spy-enabled wrapper whose `.mock` property exposes call metadata. ## Example ```ts const add = createMockFn((a: number, b: number) => a + b); add(1, 2); add(3, 4); add.mock.callCount(); // 2 add.mock.calls[0].arguments; // [1, 2] ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockFn.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockFn # Function: createMockFn() > **createMockFn**<`F`>(`impl`): [`MockFn`](../interfaces/MockFn.md)<`F`> Defined in: test-fixtures/dist/index.d.ts:150 Create a portable mock function that wraps `impl` and records every call. ## Type Parameters ### F `F` *extends* (...`args`) => `any` ## Parameters ### impl `F` The underlying implementation to delegate to. ## Returns [`MockFn`](../interfaces/MockFn.md)<`F`> A spy-enabled wrapper whose `.mock` property exposes call metadata. ## Example ```ts const add = createMockFn((a: number, b: number) => a + b); add(1, 2); add(3, 4); add.mock.callCount(); // 2 add.mock.calls[0].arguments; // [1, 2] ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockNext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockNext # Function: createMockNext() > **createMockNext**(`options?`): `any` Defined in: [mock-next.ts:36](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-next.ts#L36) Create a mock `next` handler that resolves with a successful response. The returned function is a spy (via [createMockFn](createMockFn.md)), so callers can inspect `next.mock.calls` and `next.mock.callCount()` after the test. ## Parameters ### options? [`MockNextOptions`](../../types/interfaces/MockNextOptions.md) Optional overrides for the response payload and stream flag. ## Returns `any` A spy-enabled async function matching the ConnectRPC `next` signature. ## Example ```ts import { createMockNext } from "@connectum/testing"; const next = createMockNext({ message: { id: 1 } }); const res = await next({}); // res.message => { id: 1 } // next.mock.callCount() => 1 ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockNext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockNext # Function: createMockNext() > **createMockNext**(`options?`): `any` Defined in: test-fixtures/dist/index.d.ts:267 Create a mock `next` handler that resolves with a successful response. The returned function is a spy (via [createMockFn](createMockFn.md)), so callers can inspect `next.mock.calls` and `next.mock.callCount()` after the test. ## Parameters ### options? [`MockNextOptions`](../interfaces/MockNextOptions.md) Optional overrides for the response payload and stream flag. ## Returns `any` A spy-enabled async function matching the ConnectRPC `next` signature. ## Example ```ts import { createMockNext } from "@connectum/testing"; const next = createMockNext({ message: { id: 1 } }); const res = await next({}); // res.message => { id: 1 } // next.mock.callCount() => 1 ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockNextError.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockNextError # Function: createMockNextError() > **createMockNextError**(`code`, `message?`): `any` Defined in: [mock-next.ts:68](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-next.ts#L68) Create a mock `next` handler that always throws a ConnectError. Useful for testing how interceptors handle downstream failures. ## Parameters ### code `Code` The gRPC status code for the error. ### message? `string` Human-readable error message. Defaults to `"Mock error"`. ## Returns `any` A spy-enabled async function that throws on every call. ## Example ```ts import { Code } from "@connectrpc/connect"; import { createMockNextError } from "@connectum/testing"; const next = createMockNextError(Code.NotFound, "user not found"); await next({}).catch((err) => { // err instanceof ConnectError => true // err.code => Code.NotFound }); ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockNextError.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockNextError # Function: createMockNextError() > **createMockNextError**(`code`, `message?`): `any` Defined in: test-fixtures/dist/index.d.ts:289 Create a mock `next` handler that always throws a ConnectError. Useful for testing how interceptors handle downstream failures. ## Parameters ### code `Code` The gRPC status code for the error. ### message? `string` Human-readable error message. Defaults to `"Mock error"`. ## Returns `any` A spy-enabled async function that throws on every call. ## Example ```ts import { Code } from "@connectrpc/connect"; import { createMockNextError } from "@connectum/testing"; const next = createMockNextError(Code.NotFound, "user not found"); await next({}).catch((err) => { // err instanceof ConnectError => true // err.code => Code.NotFound }); ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockNextSlow.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockNextSlow # Function: createMockNextSlow() > **createMockNextSlow**(`delay`, `options?`): `any` Defined in: [mock-next.ts:93](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-next.ts#L93) Create a mock `next` handler that resolves after a configurable delay. Useful for testing timeout interceptors and other time-sensitive logic. ## Parameters ### delay `number` Time to wait in milliseconds before resolving. ### options? [`MockNextOptions`](../../types/interfaces/MockNextOptions.md) Optional overrides for the response payload and stream flag. ## Returns `any` A spy-enabled async function that sleeps before returning a response. ## Example ```ts import { createMockNextSlow } from "@connectum/testing"; const next = createMockNextSlow(500); const res = await next({}); // resolves after ~500 ms // res.message => { result: "success" } ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockNextSlow.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockNextSlow # Function: createMockNextSlow() > **createMockNextSlow**(`delay`, `options?`): `any` Defined in: test-fixtures/dist/index.d.ts:308 Create a mock `next` handler that resolves after a configurable delay. Useful for testing timeout interceptors and other time-sensitive logic. ## Parameters ### delay `number` Time to wait in milliseconds before resolving. ### options? [`MockNextOptions`](../interfaces/MockNextOptions.md) Optional overrides for the response payload and stream flag. ## Returns `any` A spy-enabled async function that sleeps before returning a response. ## Example ```ts import { createMockNextSlow } from "@connectum/testing"; const next = createMockNextSlow(500); const res = await next({}); // resolves after ~500 ms // res.message => { result: "success" } ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockRequest.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockRequest # Function: createMockRequest() > **createMockRequest**(`options?`): `any` Defined in: [mock-request.ts:34](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-request.ts#L34) Create a mock ConnectRPC [UnaryRequest](https://connectrpc.com/docs/node/interceptors) object suitable for testing interceptors. All fields have sensible defaults, so calling `createMockRequest()` with no arguments returns a fully valid request that can be passed straight into an interceptor under test. ## Parameters ### options? [`MockRequestOptions`](../../types/interfaces/MockRequestOptions.md) Optional overrides for request fields. ## Returns `any` A plain object matching the ConnectRPC `UnaryRequest` shape. ## Example ```ts import { createMockRequest } from "@connectum/testing"; const req = createMockRequest({ service: "acme.UserService", method: "GetUser" }); // req.service.typeName === "acme.UserService" // req.method.name === "GetUser" // req.url === "http://localhost/acme.UserService/GetUser" ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockRequest.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockRequest # Function: createMockRequest() > **createMockRequest**(`options?`): `any` Defined in: test-fixtures/dist/index.d.ts:337 Create a mock ConnectRPC [UnaryRequest](https://connectrpc.com/docs/node/interceptors) object suitable for testing interceptors. All fields have sensible defaults, so calling `createMockRequest()` with no arguments returns a fully valid request that can be passed straight into an interceptor under test. ## Parameters ### options? [`MockRequestOptions`](../interfaces/MockRequestOptions.md) Optional overrides for request fields. ## Returns `any` A plain object matching the ConnectRPC `UnaryRequest` shape. ## Example ```ts import { createMockRequest } from "@connectum/testing"; const req = createMockRequest({ service: "acme.UserService", method: "GetUser" }); // req.service.typeName === "acme.UserService" // req.method.name === "GetUser" // req.url === "http://localhost/acme.UserService/GetUser" ``` --- --- url: /en/api/@connectum/test-fixtures/index/functions/createMockStream.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / createMockStream # Function: createMockStream() > **createMockStream**<`T`>(`items`, `options?`): `AsyncIterable`<`T`> Defined in: [mock-stream.ts:35](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-stream.ts#L35) Create an AsyncIterable that yields `items` sequentially. Useful for testing ConnectRPC server-streaming or client-streaming interceptors and handlers without a real gRPC connection. The returned iterable is **reusable** — each call to `Symbol.asyncIterator` starts a fresh iteration over the same items. ## Type Parameters ### T `T` Type of items yielded by the stream. ## Parameters ### items `T`\[] Array of items to yield in order. ### options? [`MockStreamOptions`](../../types/interfaces/MockStreamOptions.md) Optional stream behavior configuration. ## Returns `AsyncIterable`<`T`> An async iterable that yields each item from `items`. ## Example ```ts import { createMockStream } from "@connectum/testing"; const stream = createMockStream([1, 2, 3], { delayMs: 10 }); for await (const value of stream) { console.log(value); // 1, 2, 3 } ``` --- --- url: /en/api/@connectum/testing/index/functions/createMockStream.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createMockStream # Function: createMockStream() > **createMockStream**<`T`>(`items`, `options?`): `AsyncIterable`<`T`> Defined in: test-fixtures/dist/index.d.ts:370 Create an AsyncIterable that yields `items` sequentially. Useful for testing ConnectRPC server-streaming or client-streaming interceptors and handlers without a real gRPC connection. The returned iterable is **reusable** — each call to `Symbol.asyncIterator` starts a fresh iteration over the same items. ## Type Parameters ### T `T` Type of items yielded by the stream. ## Parameters ### items `T`\[] Array of items to yield in order. ### options? [`MockStreamOptions`](../interfaces/MockStreamOptions.md) Optional stream behavior configuration. ## Returns `AsyncIterable`<`T`> An async iterable that yields each item from `items`. ## Example ```ts import { createMockStream } from "@connectum/testing"; const stream = createMockStream([1, 2, 3], { delayMs: 10 }); for await (const value of stream) { console.log(value); // 1, 2, 3 } ``` --- --- 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:59](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/client-interceptor.ts#L59) 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:52](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/interceptor.ts#L52) 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:132](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/proto/proto-authz-interceptor.ts#L132) 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 3b. internal = true: -- service-to-service (ADR-029) no context --> throw Unauthenticated no requires --> allow (any trusted internal caller) has requires --> fall through to step 4 (inclusive roles) 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). For `internal` methods the upstream interceptor is [createInternalAuthInterceptor](createInternalAuthInterceptor.md); the chain order is `errorHandler -> (jwtAuth | internalAuth) -> protoAuthz` — the auth interceptors populate the `AuthContext` that this interceptor consumes. ## Parameters ### options? [`ProtoAuthzInterceptorOptions`](../interfaces/ProtoAuthzInterceptorOptions.md) = `{}` Proto authorization interceptor options ## Returns `Interceptor` ConnectRPC interceptor ## Examples **Basic usage with proto options only** ```typescript import { createProtoAuthzInterceptor } from '@connectum/auth'; const authz = createProtoAuthzInterceptor(); // Proto options in .proto files control authorization ``` **With fallback programmatic rules** ```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/main/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 **Server-side usage with createServer** ```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/main/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/main/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/main/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 **Server-side usage with createServer** ```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(); ``` **Client-side usage with transport** ```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:576](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/Server.ts#L576) 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/main/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 **better-auth integration** ```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/main/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 **Create a test token** ```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/auth/testing/functions/createTestJwtRS256.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / createTestJwtRS256 # Function: createTestJwtRS256() > **createTestJwtRS256**(`privateKey`, `payload`, `options`): `Promise`<`string`> Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:133](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L133) Mint an RS256 test JWT signed by the private key from [generateRsaTestKeypair](generateRsaTestKeypair.md), with a `kid` header matching the published JWK. NOT for production use. ## Parameters ### privateKey `CryptoKey` Private key from [generateRsaTestKeypair](generateRsaTestKeypair.md). ### payload `Record`<`string`, `unknown`> JWT claims (e.g. `sub`, `roles`, `scope`). ### options `kid` (required, must match the published JWK) plus optional `issuer`/`audience`/`expiresIn` (default `"1h"`). #### audience? `string` #### expiresIn? `string` #### issuer? `string` #### kid `string` ## Returns `Promise`<`string`> --- --- url: /en/api/@connectum/testing/index/functions/createTestServer.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / createTestServer # Function: createTestServer() > **createTestServer**(`options`): `Promise`<[`TestServer`](../../types/interfaces/TestServer.md)> Defined in: [testing/src/test-server.ts:33](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/test-server.ts#L33) Create and start a test server on a random (or specified) port. Returns a [TestServer](../../types/interfaces/TestServer.md) with a pre-configured gRPC transport ready for use with ConnectRPC clients. The caller is responsible for calling [TestServer.close](../../types/interfaces/TestServer.md#close) when done. ## Parameters ### options [`CreateTestServerOptions`](../../types/interfaces/CreateTestServerOptions.md) Server configuration (services, interceptors, protocols, port) ## Returns `Promise`<[`TestServer`](../../types/interfaces/TestServer.md)> Running test server with transport and cleanup function ## Example ```typescript const server = await createTestServer({ services: [myRoutes] }); const client = createClient(MyService, server.transport); const response = await client.myMethod({ id: "1" }); await server.close(); ``` --- --- 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/main/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 **Server-side usage with createServer** ```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(); ``` **Client-side usage with transport** ```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/interceptors/circuit-breaker/functions/defaultFailurePredicate.md --- [Connectum API Reference](../../../../index.md) / [@connectum/interceptors](../../index.md) / [circuit-breaker](../index.md) / defaultFailurePredicate # Function: defaultFailurePredicate() > **defaultFailurePredicate**(`error`): `boolean` Defined in: [circuit-breaker.ts:37](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/circuit-breaker.ts#L37) Default circuit-failure classification. A ConnectError counts as a failure only when its code is an infrastructure code (Unknown, DeadlineExceeded, Internal, Unavailable, DataLoss, ResourceExhausted). Any non-ConnectError thrown value counts as a failure: unknown transport or runtime faults must still protect the upstream. Exported so custom predicates can compose with it — it is also passed as the second argument to [CircuitBreakerOptions.failurePredicate](../../interfaces/CircuitBreakerOptions.md#failurepredicate). ## Parameters ### error `unknown` ## Returns `boolean` --- --- url: /en/api/@connectum/core/functions/defineCatalog.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / defineCatalog # Function: defineCatalog() > **defineCatalog**<`T`>(`record`): `Readonly`<`T`> Defined in: [packages/core/src/serviceCatalog.ts:61](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/serviceCatalog.ts#L61) Build a [ServiceCatalog](../type-aliases/ServiceCatalog.md) from a literal record, preserving the literal key type for downstream inference. Equivalent to writing the record inline, but freezes the result and documents intent. Throws [CatalogConfigError](../classes/CatalogConfigError.md) if any key does not equal its descriptor's `typeName` — a mis-keyed entry would bypass the duplicate-`typeName` intent and break resolution by canonical type name. ## Type Parameters ### T `T` *extends* `Record`<`string`, `DescService`> ## Parameters ### record `T` ## Returns `Readonly`<`T`> ## Example ```ts const catalog = defineCatalog({ [OrdersService.typeName]: OrdersService, [InventoryService.typeName]: InventoryService, }); ``` --- --- url: /en/api/@connectum/core/functions/defineLazyService.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / defineLazyService # Function: defineLazyService() > **defineLazyService**<`S`>(`descriptor`, `factory`, `options?`): [`ServiceDefinition`](../interfaces/ServiceDefinition.md) Defined in: [packages/core/src/defineService.ts:91](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/defineService.ts#L91) Define a service whose handlers (and their dependencies) are created lazily. `factory` runs only when the service is actually mounted locally — i.e. when it is in `enabledServices` (or `enabledServices` is `undefined`). A service routed to a remote process never instantiates its local dependencies. Useful for DI-heavy monoliths where wiring a service is expensive. ## Type Parameters ### S `S` *extends* `DescService` ## Parameters ### descriptor `S` ### factory () => [`ConnectumServiceImpl`](../type-aliases/ConnectumServiceImpl.md)<`S`> ### options? `Partial`<`UniversalHandlerOptions`> ## Returns [`ServiceDefinition`](../interfaces/ServiceDefinition.md) --- --- url: /en/api/@connectum/core/functions/defineService.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / defineService # Function: defineService() > **defineService**<`S`>(`descriptor`, `handlers`, `options?`): [`ServiceDefinition`](../interfaces/ServiceDefinition.md) Defined in: [packages/core/src/defineService.ts:74](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/defineService.ts#L74) Define a service from its descriptor and handler map. Pass [ServiceOptions](../type-aliases/ServiceOptions.md) to set per-service handler options, e.g. interceptors applied to every method of this service: ## Type Parameters ### S `S` *extends* `DescService` ## Parameters ### descriptor `S` ### handlers [`ConnectumServiceImpl`](../type-aliases/ConnectumServiceImpl.md)<`S`> ### options? `Partial`<`UniversalHandlerOptions`> ## Returns [`ServiceDefinition`](../interfaces/ServiceDefinition.md) ## Example ```ts const greeter = defineService(GreeterService, { async sayHello(req, ctx) { // ctx.call(...) is available for cross-service calls return { message: `Hello, ${req.name}!` }; }, }, { interceptors: [requireAuth, auditLog] }); createServer({ services: [greeter] }); ``` --- --- url: /en/api/@connectum/events/functions/deriveServiceName.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / deriveServiceName # Function: deriveServiceName() > **deriveServiceName**(`serviceNames`): `string` | `undefined` Defined in: [packages/events/src/EventBus.ts:42](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/EventBus.ts#L42) Derive a service identifier from registered proto service type names. Extracts unique package names and appends the hostname for replica disambiguation. ## Parameters ### serviceNames readonly `string`\[] ## Returns `string` | `undefined` Service name in format `"{packages}@{hostname}"`, or undefined if no services registered --- --- url: /en/api/@connectum/otel/shared/functions/detectConnectumTransport.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [shared](../index.md) / detectConnectumTransport # Function: detectConnectumTransport() > **detectConnectumTransport**(`headers`): `"http"` | `"in-process"` Defined in: [packages/otel/src/shared.ts:187](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L187) Connectum transport identifier observed from request headers. `@connectum/core`'s `createLocalTransport` sets a synthetic request header (`connectum-internal-transport: in-process`) on every outgoing call so that the OTel interceptors can tag spans and metrics with the originating transport without parsing the synthetic `https://in-memory/...` URL. ## Parameters ### headers `Headers` The request headers (Connect `req.header`) ## Returns `"http"` | `"in-process"` `"in-process"` if the marker is present, `"http"` otherwise. --- --- url: /en/api/@connectum/events/functions/dlqMiddleware.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / dlqMiddleware # Function: dlqMiddleware() > **dlqMiddleware**(`options`, `adapter`): [`EventMiddleware`](../types/type-aliases/EventMiddleware.md) Defined in: [packages/events/src/middleware/dlq.ts:29](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/middleware/dlq.ts#L29) Create a DLQ middleware that catches errors from inner middleware (retry), publishes to DLQ topic, and acks the original. ## Parameters ### options [`DlqOptions`](../types/interfaces/DlqOptions.md) ### adapter [`EventAdapter`](../types/interfaces/EventAdapter.md) ## Returns [`EventMiddleware`](../types/type-aliases/EventMiddleware.md) --- --- url: /en/api/@connectum/core/functions/dnsResolver.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / dnsResolver # Function: dnsResolver() > **dnsResolver**(`options`): [`RemoteResolver`](../type-aliases/RemoteResolver.md) Defined in: [packages/core/src/remoteResolver.ts:77](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L77) A resolver that derives a base URL per service from a DNS-style template and builds a transport for it. Mirrors typical container/k8s service-name routing. Always resolves (never `null`) — the template is assumed to cover every remote service; use [mapResolver](mapResolver.md) for an explicit allow-list. ## Parameters ### options [`DnsResolverOptions`](../interfaces/DnsResolverOptions.md) ## Returns [`RemoteResolver`](../type-aliases/RemoteResolver.md) --- --- 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:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L51) 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/main/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/events-amqp/testing/functions/FakeAmqpAdapter.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [testing](../index.md) / FakeAmqpAdapter # Function: FakeAmqpAdapter() > **FakeAmqpAdapter**(`options?`): [`FakeAmqpAdapterInstance`](../interfaces/FakeAmqpAdapterInstance.md) Defined in: [packages/events-amqp/src/testing.ts:192](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L192) Create a programmable AMQP adapter test double. ## Parameters ### options? [`FakeAmqpAdapterOptions`](../interfaces/FakeAmqpAdapterOptions.md) = `{}` ## Returns [`FakeAmqpAdapterInstance`](../interfaces/FakeAmqpAdapterInstance.md) ## Example ```typescript import { FakeAmqpAdapter } from '@connectum/events-amqp/testing'; import { AmqpPublishTimeoutError } from '@connectum/events-amqp'; const fake = FakeAmqpAdapter(); const bus = createEventBus({ adapter: fake, routes: [eventRoutes] }); await bus.start(); // The state-UNKNOWN outcome, untestable against a real broker: fake.control.nextPublish(new AmqpPublishTimeoutError('no outcome (UNKNOWN)')); await assert.rejects(() => bus.publish(OrderSchema, order), AmqpPublishTimeoutError); fake.control.dropConnection(); // disconnected → reconnecting fake.control.completeRecovery(); // connected { reconnected: true } ``` --- --- 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/main/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/main/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/testing/functions/generateRsaTestKeypair.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / generateRsaTestKeypair # Function: generateRsaTestKeypair() > **generateRsaTestKeypair**(`kid?`): `Promise`<[`RsaTestKeypair`](../interfaces/RsaTestKeypair.md)> Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:73](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L73) Generate an RSA (RS256) test keypair and the matching public JWK. The returned `publicJwk` carries the `kid`/`alg`/`use` a JWKS endpoint publishes, and the same `kid` must be set on every token minted for it (otherwise `createRemoteJWKSet` fails key selection). ## Parameters ### kid? `string` = `TEST_JWT_KID` Key id to stamp on the JWK; defaults to [TEST\_JWT\_KID](../variables/TEST_JWT_KID.md). ## Returns `Promise`<[`RsaTestKeypair`](../interfaces/RsaTestKeypair.md)> --- --- 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:111](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/context.ts#L111) 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 **Usage in a service handler** ```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/main/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/main/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/auth/functions/getInternalMethods.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / getInternalMethods # Function: getInternalMethods() > **getInternalMethods**(`services`): `string`\[] Defined in: [packages/auth/src/proto/reader.ts:222](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/proto/reader.ts#L222) Get the list of internal 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 `internal`. Mirrors [getPublicMethods](getPublicMethods.md). Internal methods skip end-user (JWT) authentication — feed these into the JWT auth interceptor's `skipMethods` exactly like public methods — but, unlike public methods, they still require an internal trust marker established by [createInternalAuthInterceptor](createInternalAuthInterceptor.md). 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 { getInternalMethods, getPublicMethods } from '@connectum/auth/proto'; // JWT auth skips both public and internal methods; // the internal interceptor then enforces the trust marker on internal ones. const jwtAuth = createJwtAuthInterceptor({ jwksUri: '...', skipMethods: [...getPublicMethods(services), ...getInternalMethods(services)], }); ``` --- --- 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/main/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/main/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/main/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:371](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L371) 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:178](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/proto/reader.ts#L178) 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/main/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/main/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/main/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/@connectum/healthcheck/functions/Healthcheck.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/healthcheck](../../../index.md) / [@connectum/healthcheck](../index.md) / Healthcheck # Function: Healthcheck() > **Healthcheck**(`options?`): `ProtocolRegistration` Defined in: [Healthcheck.ts:84](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/Healthcheck.ts#L84) 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`](../types/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:357](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L357) Initialize the OpenTelemetry provider with explicit options. Optional -- [getProvider](getProvider.md), [getMeter](../../meter/functions/getMeter.md), [getTracer](../../tracer/functions/getTracer.md), and [getLogger](../../logger/functions/getLogger.md) auto-initialize with environment-based defaults. Idempotent: subsequent calls are no-ops if provider is already active. Call [shutdownProvider](shutdownProvider.md) first to re-initialize with new options. ## Parameters ### options? [`ProviderOptions`](../interfaces/ProviderOptions.md) Optional provider configuration overrides ## Returns `void` --- --- url: /en/api/@connectum/events-amqp/functions/isAutoRetriablePublishError.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / isAutoRetriablePublishError # Function: isAutoRetriablePublishError() > **isAutoRetriablePublishError**(`err`, `options?`): `boolean` Defined in: [packages/events-amqp/src/AmqpAdapter.ts:180](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/AmqpAdapter.ts#L180) The publish AUTO-RETRY boundary (#195): which publish failures the opt-in `publishRetry` retries inline. Deliberately NARROWER than the at-least-once REPUBLISH matrix in the error taxonomy (`errors.ts`): a broker nack is republish-safe by policy but is an explicit refusal — hammering it in a tight loop is not a retry strategy. Connection-class outcomes (`AmqpConnectionError`: publish during recovery, in-flight confirm lost to a drop) are retriable; a timeout (`AmqpPublishTimeoutError`, state UNKNOWN) joins only via `retryOnTimeout: true`. Deterministic outcomes (unroutable, serialization, topology) never retry. ## Parameters ### err `unknown` ### options? #### retryOnTimeout? `boolean` ## Returns `boolean` --- --- 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/main/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/events-kafka/functions/KafkaAdapter.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-kafka](../index.md) / KafkaAdapter # Function: KafkaAdapter() > **KafkaAdapter**(`options`): `EventAdapter` Defined in: [KafkaAdapter.ts:122](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-kafka/src/KafkaAdapter.ts#L122) Create a Kafka/Redpanda adapter for @connectum/events. ## Parameters ### options [`KafkaAdapterOptions`](../types/interfaces/KafkaAdapterOptions.md) Kafka adapter configuration ## Returns `EventAdapter` EventAdapter instance ## Example ```typescript import { KafkaAdapter } from "@connectum/events-kafka"; const adapter = KafkaAdapter({ brokers: ["localhost:9092"], clientId: "my-service", }); await adapter.connect(); await adapter.publish("user.created", payload); await adapter.disconnect(); ``` --- --- url: /en/api/@connectum/core/functions/mapResolver.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / mapResolver # Function: mapResolver() > **mapResolver**(`map`): [`RemoteResolver`](../type-aliases/RemoteResolver.md) Defined in: [packages/core/src/remoteResolver.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L54) A resolver backed by an explicit `{ [typeName]: Transport }` map. Unknown typeNames resolve to `null` (→ `Code.Unavailable`). ## Parameters ### map `Readonly`<`Record`<`string`, `Transport`>> ## Returns [`RemoteResolver`](../type-aliases/RemoteResolver.md) --- --- 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/main/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/events/functions/matchPattern.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / matchPattern # Function: matchPattern() > **matchPattern**(`pattern`, `topic`): `boolean` Defined in: [packages/events/src/wildcard.ts:27](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/wildcard.ts#L27) Match a topic against a wildcard pattern. ## Parameters ### pattern `string` Pattern with optional `*` and `>` wildcards ### topic `string` Concrete topic name to match ## Returns `boolean` true if the topic matches the pattern ## Example ```typescript matchPattern("user.*", "user.created") // true matchPattern("user.*", "user.created.v2") // false matchPattern("user.>", "user.created") // true matchPattern("user.>", "user.created.v2") // true matchPattern("user.created", "user.created") // true ``` --- --- url: /en/api/@connectum/core/functions/matchServicesPattern.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / matchServicesPattern # Function: matchServicesPattern() > **matchServicesPattern**(`pattern`, `names`): `string`\[] Defined in: [packages/core/src/enabledServices.ts:31](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/enabledServices.ts#L31) Return the subset of `names` matching a glob `pattern`, where `*` matches any run of characters (including dots). E.g. `"acme.*"` matches `"acme.v1.UsersService"`. Matched without a constructed `RegExp` (segment scan). ## Parameters ### pattern `string` ### names readonly `string`\[] ## Returns `string`\[] --- --- url: /en/api/@connectum/events/functions/MemoryAdapter.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / MemoryAdapter # Function: MemoryAdapter() > **MemoryAdapter**(): [`EventAdapter`](../types/interfaces/EventAdapter.md) Defined in: [packages/events/src/MemoryAdapter.ts:23](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/MemoryAdapter.ts#L23) Create an in-memory adapter for testing event flows without an external message broker. ## Returns [`EventAdapter`](../types/interfaces/EventAdapter.md) --- --- url: /en/api/@connectum/core/functions/mergeCatalogs.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / mergeCatalogs # Function: mergeCatalogs() > **mergeCatalogs**(...`catalogs`): [`ServiceCatalog`](../type-aliases/ServiceCatalog.md) Defined in: [packages/core/src/serviceCatalog.ts:79](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/serviceCatalog.ts#L79) Merge several catalogs into one. Throws [CatalogConfigError](../classes/CatalogConfigError.md) on a duplicate `typeName`, or on a key that does not equal its descriptor's `typeName`. TypeScript cannot catch a duplicate whose two descriptors have an identical shape (polyrepo finding F3), so this runtime check is mandatory rather than optional — a silent collision would route calls to the wrong service. ## Parameters ### catalogs ...readonly `Readonly`<`Record`<`string`, `DescService`>>\[] ## Returns [`ServiceCatalog`](../type-aliases/ServiceCatalog.md) --- --- url: /en/api/@connectum/core/functions/mergeEnabledServices.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / mergeEnabledServices # Function: mergeEnabledServices() > **mergeEnabledServices**(...`lists`): `string`\[] Defined in: [packages/core/src/enabledServices.ts:58](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/enabledServices.ts#L58) Merge several `enabledServices` lists, de-duplicating while preserving first-seen order. ## Parameters ### lists ...readonly readonly `string`\[]\[] ## Returns `string`\[] --- --- url: /en/api/@connectum/auth/functions/meshIdentityTrust.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / meshIdentityTrust # Function: meshIdentityTrust() > **meshIdentityTrust**(`options`): [`InternalTrustSource`](../type-aliases/InternalTrustSource.md) Defined in: [packages/auth/src/internal-auth-interceptor.ts:144](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/internal-auth-interceptor.ts#L144) Trust source that verifies a mesh-forwarded peer identity against an allow-list (ADR-029 option (a) — production default, inherently per-service). In a service mesh the sidecar terminates mTLS and forwards the verified peer identity as a header (e.g. an Istio short-form ServiceAccount principal `cluster.local/ns//sa/`, or a SPIFFE id). The mesh issues each workload its OWN mTLS identity, so matching that forwarded principal against an allow-list is per-service by construction — compromising one workload cannot forge another's identity. The identity header is stripped after extraction to prevent downstream spoofing. ## Parameters ### options [`MeshIdentityTrustOptions`](../interfaces/MeshIdentityTrustOptions.md) Allow-list and the identity header name. ## Returns [`InternalTrustSource`](../type-aliases/InternalTrustSource.md) An [InternalTrustSource](../type-aliases/InternalTrustSource.md). --- --- url: /en/api/@connectum/testing/index/functions/mockResolver.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / mockResolver # Function: mockResolver() > **mockResolver**(`mocks`): `RemoteResolver` Defined in: [testing/src/mockResolver.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockResolver.ts#L54) Build a RemoteResolver that serves the given mocks in-process. Returns `null` for any service not in the mock set (so it composes with real resolvers via `mapResolver`-style fallbacks). ## Parameters ### mocks readonly [`MockService`](../interfaces/MockService.md)\[] ## Returns `RemoteResolver` --- --- url: /en/api/@connectum/testing/index/functions/mockService.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / mockService # Function: mockService() > **mockService**<`S`>(`service`, `impl`): [`MockService`](../interfaces/MockService.md) Defined in: [testing/src/mockResolver.ts:38](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockResolver.ts#L38) Type-safe constructor for a [MockService](../interfaces/MockService.md). Pairs a service descriptor with handlers typed against it. ## Type Parameters ### S `S` *extends* `DescService` ## Parameters ### service `S` ### impl `Partial`<`ServiceImpl`<`S`>> ## Returns [`MockService`](../interfaces/MockService.md) ## Example ```ts mockService(InventoryService, { getStock: () => create(StockSchema, { units: 7 }), }); ``` --- --- url: /en/api/@connectum/events-nats/functions/NatsAdapter.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-nats](../index.md) / NatsAdapter # Function: NatsAdapter() > **NatsAdapter**(`options`): `EventAdapter` Defined in: [NatsAdapter.ts:83](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/NatsAdapter.ts#L83) Create a NATS JetStream adapter. ## Parameters ### options [`NatsAdapterOptions`](../types/interfaces/NatsAdapterOptions.md) ## Returns `EventAdapter` ## Example ```typescript import { NatsAdapter } from "@connectum/events-nats"; import { createEventBus } from "@connectum/events"; const adapter = NatsAdapter({ servers: "nats://localhost:4222" }); const bus = createEventBus({ adapter, routes: [myRoutes] }); await bus.start(); ``` --- --- 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:92](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/headers.ts#L92) 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 **Trust upstream auth headers** ```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/main/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**: `"console"` | `"otel"` | `"pino"` = `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"` | `"warn"` | `"debug"` | `"info"` = `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/otel/provider/functions/parseOtelResourceAttributesEnv.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [provider](../index.md) / parseOtelResourceAttributesEnv # Function: parseOtelResourceAttributesEnv() > **parseOtelResourceAttributesEnv**(`raw`): `Record`<`string`, `string`> Defined in: [packages/otel/src/provider.ts:62](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L62) Parse the standard `OTEL_RESOURCE_ATTRIBUTES` env var (`key1=value1,key2=value2`) into an attribute record. Malformed pairs (no `=`, empty key) are skipped. Values are kept as strings; whitespace around keys and values is trimmed. ## Parameters ### raw `string` | `undefined` ## Returns `Record`<`string`, `string`> --- --- url: >- /en/api/@connectum/healthcheck/@connectum/healthcheck/functions/parseServiceFromUrl.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/healthcheck](../../../index.md) / [@connectum/healthcheck](../index.md) / parseServiceFromUrl # Function: parseServiceFromUrl() > **parseServiceFromUrl**(`url`, `host`): `string` | `undefined` Defined in: [httpHandler.ts:111](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/httpHandler.ts#L111) 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/parseServicesEnv.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / parseServicesEnv # Function: parseServicesEnv() > **parseServicesEnv**(`value`): `string`\[] Defined in: [packages/core/src/enabledServices.ts:18](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/enabledServices.ts#L18) Parse a comma-separated env value into a list of proto `typeName`s, trimming whitespace and dropping empty entries. Returns `[]` for an empty/undefined value. ## Parameters ### value `string` | `null` | `undefined` ## Returns `string`\[] ## Example ```ts `enabledServices: parseServicesEnv(process.env.CONNECTUM_SERVICES)` ``` --- --- url: /en/api/@connectum/core/functions/perServiceEnvResolver.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / perServiceEnvResolver # Function: perServiceEnvResolver() > **perServiceEnvResolver**(`map`, `options?`): [`RemoteResolver`](../type-aliases/RemoteResolver.md) Defined in: [packages/core/src/remoteResolver.ts:100](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L100) A resolver backed by per-service environment variables: `map` pairs each `typeName` with the name of the env var holding its base URL. A service with no mapping, or whose env var is unset/empty, resolves to `null` (→ `Code.Unavailable`). Replaces hand-rolled env registries in boot code. ## Parameters ### map `Readonly`<`Record`<`string`, `string`>> ### options? [`PerServiceEnvResolverOptions`](../interfaces/PerServiceEnvResolverOptions.md) ## Returns [`RemoteResolver`](../type-aliases/RemoteResolver.md) ## Example ```ts `perServiceEnvResolver({ "orders.v1.OrdersService": "ORDERS_URL" })` ``` --- --- 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/main/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/events-redis/functions/RedisAdapter.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-redis](../index.md) / RedisAdapter # Function: RedisAdapter() > **RedisAdapter**(`options?`): `EventAdapter` Defined in: [RedisAdapter.ts:68](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/RedisAdapter.ts#L68) Create a Redis Streams adapter for the Connectum event bus. The adapter uses Redis Streams with consumer groups for durable, load-balanced event consumption. Each subscription creates a dedicated blocking connection (via `redis.duplicate()`) to avoid blocking the main connection used for publishing. ## Parameters ### options? [`RedisAdapterOptions`](../types/interfaces/RedisAdapterOptions.md) = `{}` ## Returns `EventAdapter` ## Example ```typescript import { createEventBus } from "@connectum/events"; import { RedisAdapter } from "@connectum/events-redis"; const bus = createEventBus({ adapter: RedisAdapter({ url: "redis://localhost:6379" }), routes: [myEventRoutes], }); await bus.start(); ``` --- --- 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:43](https://github.com/Connectum-Framework/connectum/blob/main/packages/reflection/src/Reflection.ts#L43) 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:124](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/context.ts#L124) 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/core/functions/resolveEffectiveTransport.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / resolveEffectiveTransport # Function: resolveEffectiveTransport() > **resolveEffectiveTransport**(`options`): [`EffectiveTransport`](../type-aliases/EffectiveTransport.md) Defined in: [packages/core/src/TransportValidation.ts:67](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L67) Resolve the effective transport from the server's TLS and `allowHTTP1` configuration. `allowHTTP1` defaults to `true` (matching TransportManager). ## Parameters ### options #### allowHTTP1? `boolean` #### hasTls `boolean` ## Returns [`EffectiveTransport`](../type-aliases/EffectiveTransport.md) --- --- 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:73](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/proto/reader.ts#L73) 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.internal -> service.internal -> 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/events/functions/resolveTopicName.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / resolveTopicName # Function: resolveTopicName() > **resolveTopicName**(`method`): `string` Defined in: [packages/events/src/topic.ts:22](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/topic.ts#L22) Resolve the topic name for an event handler method. Priority: 1. Custom option: `option (connectum.events.v1.event).topic = "custom"` 2. Default: `method.input.typeName` (e.g., "mypackage.UserCreated") ## Parameters ### method `DescMethod` ## Returns `string` --- --- url: /en/api/@connectum/events/functions/retryMiddleware.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / retryMiddleware # Function: retryMiddleware() > **retryMiddleware**(`options?`): [`EventMiddleware`](../types/type-aliases/EventMiddleware.md) Defined in: [packages/events/src/middleware/retry.ts:48](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/middleware/retry.ts#L48) Create a retry middleware with configurable options. On handler failure, retries up to `maxRetries` times with the configured backoff strategy. If all retries exhaust, the error is re-thrown for the next middleware (e.g., DLQ). ## Parameters ### options? [`RetryOptions`](../types/interfaces/RetryOptions.md) ## Returns [`EventMiddleware`](../types/type-aliases/EventMiddleware.md) --- --- 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`: `"console"` | `"otel"` | `"pino"`; `LOG_FORMAT`: `"json"` | `"pretty"`; `LOG_LEVEL`: `"error"` | `"warn"` | `"debug"` | `"info"`; `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/main/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`: `"console"` | `"otel"` | `"pino"`; `LOG_FORMAT`: `"json"` | `"pretty"`; `LOG_LEVEL`: `"error"` | `"warn"` | `"debug"` | `"info"`; `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:36](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/headers.ts#L36) 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/auth/functions/sharedSecretTrust.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / sharedSecretTrust # Function: sharedSecretTrust() > **sharedSecretTrust**(`options`): [`InternalTrustSource`](../type-aliases/InternalTrustSource.md) Defined in: [packages/auth/src/internal-auth-interceptor.ts:384](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/internal-auth-interceptor.ts#L384) Trust source that constant-time compares a single shared secret (ADR-029 option (c)). **DEV-ONLY.** A single shared secret is NOT per-service: every legitimate caller holds the same secret, so one compromise forges ALL internal identities. Use [meshIdentityTrust](meshIdentityTrust.md) (mesh) or [signedTokenTrust](signedTokenTrust.md) (non-mesh per-service JWT) in production. This factory exists only for local development and single-tenant low-trust-boundary setups, and is labeled as such so it is never mistaken for a containment-providing mode. ## Parameters ### options [`SharedSecretTrustOptions`](../interfaces/SharedSecretTrustOptions.md) The shared secret, header name, and the granted identity. ## Returns [`InternalTrustSource`](../type-aliases/InternalTrustSource.md) An [InternalTrustSource](../type-aliases/InternalTrustSource.md). --- --- 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:384](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L384) 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/auth/functions/signedTokenTrust.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / signedTokenTrust # Function: signedTokenTrust() > **signedTokenTrust**(`options`): [`InternalTrustSource`](../type-aliases/InternalTrustSource.md) Defined in: [packages/auth/src/internal-auth-interceptor.ts:266](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/internal-auth-interceptor.ts#L266) Trust source that verifies a per-service signed JWT via issuer-bound JWKS (ADR-029 option (b) — non-mesh per-service containment, NOT a shared secret). Each caller signs a short-lived JWT with its OWN private key; this trust source verifies it against that service's published public key (JWKS). Compromising service A's key forges only A. **Hard security requirement — issuer-bound key selection (verified empirically with `jose`).** The keyset is selected by the token's claimed `iss` (`issuers[iss].jwksUri`), and `jose.jwtVerify` is pinned to that same `issuer`. Each issuer gets its OWN `createRemoteJWKSet` — no `jwtVerify` call ever receives a keyset containing more than one issuer's keys. A single shared JWKS holding multiple services' keys does NOT contain compromise: `jose` resolves the signing key by `kid` independently of the `iss` claim, so a token claiming `iss: "B"` signed with A's key (header `kid: kid_A`) would be accepted against a shared keyset. This per-issuer binding prevents that forge. The framework ships only the verification primitive; key issuance/rotation/ JWKS publication belong to the deployment (SPIRE / the IdP / the mesh). ## Parameters ### options [`SignedTokenTrustOptions`](../interfaces/SignedTokenTrustOptions.md) Per-issuer JWKS configuration and the token header name. ## Returns [`InternalTrustSource`](../type-aliases/InternalTrustSource.md) An [InternalTrustSource](../type-aliases/InternalTrustSource.md). --- --- url: /en/api/@connectum/core/functions/singleTransportResolver.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / singleTransportResolver # Function: singleTransportResolver() > **singleTransportResolver**(`transport`): [`RemoteResolver`](../type-aliases/RemoteResolver.md) Defined in: [packages/core/src/remoteResolver.ts:46](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L46) A resolver that routes every remote service to the same `Transport`. Useful for a single upstream (sidecar, gateway) that fronts all remote services. ## Parameters ### transport `Transport` ## Returns [`RemoteResolver`](../type-aliases/RemoteResolver.md) --- --- url: /en/api/@connectum/auth/testing/functions/startTestJwksServer.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / startTestJwksServer # Function: startTestJwksServer() > **startTestJwksServer**(`jwks`): `Promise`<[`TestJwksServer`](../interfaces/TestJwksServer.md)> Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:95](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L95) Start an ephemeral in-process JWKS server publishing the given public JWK(s) at `/.well-known/jwks.json` on a random loopback port. ## Parameters ### jwks `JWK` | readonly `JWK`\[] One public JWK or an array (from [generateRsaTestKeypair](generateRsaTestKeypair.md)). ## Returns `Promise`<[`TestJwksServer`](../interfaces/TestJwksServer.md)> --- --- url: /en/api/@connectum/events-amqp/functions/toAmqpPattern.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / toAmqpPattern # Function: toAmqpPattern() > **toAmqpPattern**(`pattern`): `string` Defined in: [packages/events-amqp/src/AmqpAdapter.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/AmqpAdapter.ts#L54) Convert an EventBus wildcard pattern to an AMQP routing key pattern. EventBus uses NATS-style wildcards: * `*` matches a single token (same in AMQP topic exchange) * `>` matches one or more tokens (AMQP uses `#`) ## Parameters ### pattern `string` EventBus wildcard pattern ## Returns `string` AMQP routing key pattern --- --- 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/main/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/main/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/main/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 **Test a handler that reads auth context** ```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/testing/index/functions/withTestServer.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / withTestServer # Function: withTestServer() > **withTestServer**<`T`>(`options`, `testFn`): `Promise`<`T`> Defined in: [testing/src/test-server.ts:93](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/test-server.ts#L93) Run a test function with an auto-managed test server. Creates a test server, passes it to [testFn](#withtestserver), and guarantees cleanup via `finally` — even if the test throws. ## Type Parameters ### T `T` ## Parameters ### options [`CreateTestServerOptions`](../../types/interfaces/CreateTestServerOptions.md) Server configuration (services, interceptors, protocols, port) ### testFn (`server`) => `Promise`<`T`> Async function that receives the running test server ## Returns `Promise`<`T`> The value returned by testFn ## Example ```typescript const result = await withTestServer({ services: [myRoutes] }, async (server) => { const client = createClient(MyService, server.transport); return client.myMethod({ id: "1" }); }); ``` --- --- 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:83](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L83) 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"` | `"RECEIVED"` 'SENT' for outgoing, 'RECEIVED' for incoming messages ### 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!], }, }); ``` `headerMapping` describes how trusted gateway headers become an `AuthContext`. `trustSource` proves that the caller is the gateway rather than a client spoofing identity headers. See [`GatewayAuthInterceptorOptions`](/en/api/@connectum/auth/interfaces/GatewayAuthInterceptorOptions) for the exact nested fields. ### 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` | Space-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/openapi.md description: >- Generate an OpenAPI contract from proto and overlay the same Connectum authorization rules used at runtime. --- # OpenAPI Connectum services speak gRPC/Connect, but their contract often has to reach audiences that do not: REST/HTTP clients, API gateways, Swagger UI, SDK generators, and API catalogs. The common denominator for those is an **OpenAPI** document. Connectum's authorization lives in `.proto` options ([Proto-Based Authz](/en/guide/auth/proto-authz)). The pattern on this page generates an OpenAPI v3.1 contract that **reflects that authz** -- the same options the `createProtoAuthzInterceptor` enforces at runtime also drive the published spec, so the two cannot drift. **Outcome:** a reproducible OpenAPI artifact whose operation security is resolved through [`resolveMethodAuth`](/en/api/@connectum/auth/functions/resolveMethodAuth), not a second hand-maintained policy table. ::: tip Reference implementation The [`car-sharing`](https://github.com/Connectum-Framework/examples/tree/main/car-sharing) example ships this end-to-end (`buf.gen.openapi.yaml`, `scripts/openapi-authz.ts`, committed `openapi/*.yaml`). The rationale is recorded in [ADR-030](/en/contributing/adr/030-openapi-authz-generation). ::: ## How it works Generation is **two decoupled steps**, run together via one script: 1. **Base spec** -- the [`protoc-gen-connect-openapi`](https://github.com/sudorandom/protoc-gen-connect-openapi) buf remote plugin emits a faithful OpenAPI v3.1 description of the Connect API (paths, schemas, framing). It is accurate about the *shape* but blind to Connectum authz. 2. **Authz overlay** -- a small post-processor reads the `connectum.auth.v1` options via **`resolveMethodAuth`** from `@connectum/auth/proto` (the *same* reader the runtime interceptor uses) and patches each operation with `security` and `x-connectum-*` extensions. Keep the OpenAPI generation in its **own** buf template, separate from the one that emits your TypeScript. The remote plugin needs network access; isolating it means your normal `buf:generate` and tests stay offline and deterministic. ### 1. Base spec template ```yaml # buf.gen.openapi.yaml — separate from buf.gen.yaml (offline TS codegen) version: v2 clean: true inputs: - directory: proto plugins: - remote: buf.build/community/sudorandom-connect-openapi:v0.25.7 out: openapi opt: - format=yaml - features=connectrpc ``` ### 2. Authz overlay The overlay walks each service's methods, resolves the proto authz, and patches the corresponding operation. `resolveMethodAuth(method)` returns `{ public, internal?, policy?, requires? }`. ```typescript import { readFileSync, writeFileSync } from 'node:fs'; import { parse, stringify } from 'yaml'; import { resolveMethodAuth } from '@connectum/auth/proto'; import { OrderService } from '#gen/order/v1/order_pb.ts'; // One JWT bearer scheme, matching createJwtAuthInterceptor at the edge. const bearerAuth = { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }; const path = 'openapi/order/v1/order.openapi.yaml'; const doc: any = parse(readFileSync(path, 'utf8')); doc.components ??= {}; doc.components.securitySchemes ??= {}; doc.components.securitySchemes.bearerAuth = bearerAuth; for (const method of OrderService.methods) { const op = doc.paths?.[`/${OrderService.typeName}/${method.name}`]?.post; if (op === undefined) continue; // e.g. streaming RPCs are not emitted by default const auth = resolveMethodAuth(method); if (auth.public) { op.security = []; // explicitly open — overrides any global requirement op['x-connectum-public'] = true; continue; } op.security = [{ bearerAuth: [] }]; if (auth.requires?.roles.length) op['x-connectum-required-roles'] = [...auth.requires.roles]; if (auth.requires?.scopes.length) op['x-connectum-required-scopes'] = [...auth.requires.scopes]; } writeFileSync(path, stringify(doc)); ``` Wire both steps into one command: ```json { "scripts": { "openapi": "buf generate --template buf.gen.openapi.yaml && node scripts/openapi-authz.ts" } } ``` ## Authz → OpenAPI mapping | Connectum authz (proto) | `resolveMethodAuth` | OpenAPI patch on the operation | |---|---|---| | `public: true` | `auth.public === true` | `security: []` + `x-connectum-public: true` | | gated (default / `requires` / `policy`) | `auth.public === false` | `security: [{ bearerAuth: [] }]` | | `requires { roles: [...] }` | `auth.requires.roles` | `x-connectum-required-roles: [...]` | | `requires { scopes: [...] }` | `auth.requires.scopes` | `x-connectum-required-scopes: [...]` | | `internal: true` | `auth.internal === true` | `x-internal: true` | `security` and the `bearerAuth` scheme are standard OpenAPI that off-the-shelf tooling already understands. The `x-connectum-*` entries are **vendor extensions** -- advisory metadata for humans, gateways, and catalogs. They document intent; the wire enforcement remains the interceptor's job ([Proto-Based Authz](/en/guide/auth/proto-authz)). ## Notes & limitations * **Streaming RPCs** (server-, client-, or bidi-streaming) get no operation in the base spec unless the plugin's `with-streaming` opt is set -- OpenAPI's request/response model does not fit streaming. The plugin leaves an empty path entry, and the overlay skips any method that has no generated operation. (In `car-sharing`, `FleetService.ListVehicles` is server-streaming and is therefore skipped.) * **Network dependency.** `pnpm openapi` invokes a buf *remote* plugin, so generation is not fully offline. Commit the generated `openapi/*.yaml` so consumers and CI have the spec without regenerating. * **The `internal` marker** (`x-internal: true`) requires `@connectum/auth` >= 1.1.0 (see [ADR-029](/en/contributing/adr/029-internal-service-to-service-auth)). On 1.0.0, `internal` methods resolve as gated. * **Reference pattern, not a CLI (yet).** This is example code plus codegen config; it does not modify any published package. A first-class `connectum openapi` command is a [planned follow-up](/en/contributing/adr/030-openapi-authz-generation). ## Related * [Proto-Based Authz](/en/guide/auth/proto-authz) -- the authz options this overlay reads * [@connectum/auth](/en/packages/auth) -- Package Guide (`resolveMethodAuth`, `getPublicMethods`) * [ADR-030: OpenAPI generation with proto-authz overlay](/en/contributing/adr/030-openapi-authz-generation) -- design rationale * [`protoc-gen-connect-openapi`](https://github.com/sudorandom/protoc-gen-connect-openapi) -- the base generator --- --- 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 follows the dependency edges: ```mermaid flowchart LR Database["1. database"] --> Cache["2. cache"] Database --> Queue["3. message-queue"] Cache --> Queue ``` ::: 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 ```mermaid flowchart TD Signal["0s · SIGTERM received"] --> NotServing["0s · stopping → NOT_SERVING"] NotServing --> Endpoints["0–5s · Pod removed from service endpoints"] Endpoints --> Drain["5–25s · In-flight requests drain"] Drain --> Timeout["25s · Shutdown timeout boundary"] Timeout --> Hooks["25s · Shutdown hooks execute"] Hooks --> Stop["25s · stop event"] Stop --> Grace["30s · Kubernetes hard-kill boundary"] ``` ::: 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.md description: Model service readiness once and expose it through gRPC or HTTP probes. --- # Health Checks `@connectum/healthcheck` implements the gRPC Health Checking Protocol and can expose HTTP endpoints for platform probes. The application owns status transitions; the protocol only reports them. ```typescript const server = createServer({ services: [routes], protocols: [Healthcheck({ httpEnabled: true })], }); server.on('ready', () => { healthcheckManager.update(ServingStatus.SERVING); }); ``` ## Choose the owning guide | Need | Canonical destination | |---|---| | Configure protocol options, HTTP paths, status semantics, components, or dependency state | [Health protocol](/en/guide/health-checks/protocol) | | Configure Kubernetes HTTP/gRPC probes and termination timing | [Kubernetes integration](/en/guide/health-checks/kubernetes) | | Coordinate drain hooks and the global shutdown deadline | [Graceful shutdown](/en/guide/server/graceful-shutdown) | | Find an exact manager or protocol symbol | [`@connectum/healthcheck` API](/en/api/@connectum/healthcheck/) | Readiness means the process can accept useful work; liveness means it should not be restarted. During shutdown, mark the service not serving before draining traffic. Worker-only processes with `services: []` should register an application component in the manager so `/healthz` has an explicit readiness source; the protocol guide owns that pattern. Use the [`@connectum/healthcheck` module hub](/en/packages/healthcheck) for installation and navigation. --- --- url: /en/guide/production/in-process-transport.md description: >- Call locally registered Connectum services as plain function invocations — no HTTP/2, TLS, or wire serialization — with full behavioural parity to the HTTP transport. --- # In-Process Transport The **in-process transport** lets you invoke services that are registered on the same `Server` instance as direct function calls — without HTTP/2, TLS, sockets, or wire serialization — while preserving 1-to-1 behavioural parity with the HTTP/Connect/gRPC transport (interceptors, validation, authorization, error mapping, streaming semantics, OpenTelemetry spans and metrics). ::: tip Full API Reference TypeScript API documentation: [@connectum/core API Reference](/en/api/@connectum/core/). ::: ## Overview A typical Connectum service-to-service call goes over HTTP/2 loopback even when both endpoints live in the same Node.js process. That adds TLS handshakes, h2 framing, JSON/protobuf wire encoding, and a port binding — overhead that is pure waste for co-located services. The in-process transport reuses the `ConnectRouter` that `createServer()` has already built and dispatches client calls directly to the registered handlers. The client API is identical to a remote ConnectRPC client (`createClient(Service, transport)`), so the same caller code works whether the callee is local or remote. **When to use:** * **Modular monolith** — multiple bounded contexts hosted in a single process call each other over typed RPC contracts without the network overhead. * **Backend-for-Frontend (BFF)** — a BFF process embeds upstream services for low-latency composition. * **Tests** — exercise the full server-side interceptor chain (validation, auth, OTEL) without binding ports. * **Polyglot deployment** — a single client codebase that automatically routes to a local registered service or falls back to a remote HTTP transport based on the runtime topology. **When NOT to use:** * **Cross-process calls** — use `createGrpcTransport` / `createConnectTransport` over HTTP/2. The in-process transport is, by design, single-process only. * **HTTP-level middleware** — CORS, compression, and similar wire-level concerns do not apply because no data leaves the process. ## Quick Start ```typescript import { createServer } from '@connectum/core'; import { GreeterService } from './gen/greeter_pb.js'; import { greeterRoutes } from './greeter.js'; const server = createServer({ services: [greeterRoutes], }); // Auto-routing: resolved via the in-process service registry, // no server.start() required. const greeter = server.client(GreeterService); const { message } = await greeter.sayHello({ name: 'world' }); console.log(message); // "Hello, world!" ``` The call above executes the full server-side interceptor chain (including validation and authorization) and emits the same OpenTelemetry CLIENT and SERVER spans as an equivalent HTTP call — only the `connectum.transport` attribute differs. ## API Reference ### `server.client(service, options?)` Auto-routing client factory. Resolves the transport via the server's internal **service registry**: * If the service is registered on this `Server` (`server.hasService(service)` returns `true`) — returns an in-process client that dispatches directly to the registered handler. * Otherwise, if a `remoteResolver` is configured on the server — returns a standard ConnectRPC client over the `Transport` the resolver maps the service to. A resolver that returns `null` for the service yields `ConnectError(Code.Unavailable)` at client construction — the resolver runs inside `server.client()`, before any RPC is invoked. * Otherwise (not local and no `remoteResolver` configured) — throws `CatalogConfigError` immediately at client construction (fail-fast), naming the service `typeName`. ```typescript function client( service: T, options?: ServerClientOptions, // { endpoint?: string } ): Client; ``` This is the recommended entry point: the same call site works for both in-process and remote deployments without modification. Remote routing is configured once on the server via `remoteResolver`, not per call site — see [Remote Resolvers](/en/guide/service-communication/resolvers). ```typescript import { createServer, singleTransportResolver } from '@connectum/core'; import { createGrpcTransport } from '@connectrpc/connect-node'; const server = createServer({ services: [inventoryService], // Services not mounted locally are reached through this resolver. remoteResolver: singleTransportResolver( createGrpcTransport({ baseUrl: process.env.UPSTREAM_URL!, httpVersion: '2' }), ), }); // Local if registered on `server`, remote via the resolver otherwise. const inventory = server.client(InventoryService); ``` ### `server.localClient(service)` Low-level helper that always returns an in-process client. Requires the service to be registered on the server — otherwise the first call throws `ConnectError(unimplemented)`. ```typescript function localClient(service: T): Client; ``` ### `createLocalTransport(server, options?)` Lowest-level primitive. Returns a ConnectRPC `Transport` bound to the server's router. Use this directly when you need multiple clients with different client-side interceptor stacks over the same server. ```typescript function createLocalTransport( server: Server, options?: { interceptors?: Interceptor[] }, ): Transport; ``` The returned transport behaves like `createRouterTransport` from `@connectrpc/connect` but is wired into the Connectum server lifecycle and the same router that `server.start()` would expose over HTTP. ### `server.hasService(desc)` Synchronous registry lookup by `desc.typeName`. ```typescript function hasService(desc: DescService): boolean; ``` Useful for conditional routing in user code (e.g. when you build a custom transport selector). ## Behavioural Parity Guarantees The in-process transport is validated by a cross-transport contract test suite (`transportParityTest` driver in `@connectum/testing/parity`). For every covered scenario, the observed result over `createLocalTransport(server)` is structurally identical to the result over `createGrpcTransport({ baseUrl })` — modulo a single allow-listed attribute / label (`connectum.transport` / `transport`). Guaranteed identical between in-process and HTTP: * **Server-side interceptor chain** — same interceptors, same order. There is no API to bypass interceptors on the local path. * **Validation** — proto-declared `buf.validate` / `protovalidate` rules reject invalid requests with `ConnectError(invalid_argument)` and identical violation details on both transports. * **Authorization** — proto-declared authz rules and `@connectum/auth` interceptors apply uniformly. Missing/invalid tokens produce `ConnectError(unauthenticated)`; insufficient scope produces `ConnectError(permission_denied)` with identical metadata. * **Error mapping** — `ConnectError` (`code`, `message`, `metadata`, `details`) round-trips identically. Plain `Error` becomes `code === internal` on both paths. * **Streaming** — unary, server-stream, client-stream, and bidi RPCs preserve message order and respect `AbortSignal` cancellation on both paths. * **Headers / metadata** — `Headers` objects (including `authorization` and `@connectum/auth` serialized auth headers) round-trip in both directions. Headers are cloned at the boundary to prevent cross-side mutation. * **OpenTelemetry tracing and metrics** — see [Observability](#observability) below. ## Observability `@connectum/otel` instruments the in-process path through the same hooks as HTTP: * **Client span**: `SpanKind.CLIENT`, name `${rpc.service}/${rpc.method}`, attributes `rpc.system`, `rpc.service`, `rpc.method`, `rpc.connect_rpc.status_code`, plus `connectum.transport="in-process"`. * **Server span**: `SpanKind.SERVER` with the same attribute set. On the in-process path the server handler runs in the **same async context** as the client call, so the server span is established as a **child of the client span**: the parent comes from the active context (propagated directly in memory, no header round-trip), while a **Link** to the client span is also recorded from the extracted remote context. This is the in-process behaviour under the default `trustRemote: false`. On the HTTP path no async context is shared, so the same default yields a **root span with only the Link** (no parent) — pass `trustRemote: true` to `createOtelInterceptor` to make the server span adopt the extracted context as its parent on both paths, aligning them. * **Stream events**: `message.sent` and `message.received` are recorded on streaming spans identically to HTTP. * **Metrics**: `rpc.client.call.duration`, `rpc.server.call.duration`, `rpc.client.request.size`, `rpc.client.response.size`, `rpc.server.request.size`, `rpc.server.response.size`, and error counters are emitted with the same instrument names and label keys. Payload sizes are computed on the serialized protobuf form so they are directly comparable with HTTP. The only difference is an extra label `transport=in-process` (vs `transport=http`). Dashboards, alerts, and SLOs built over HTTP metrics continue to work after a service migrates to in-process invocation. ## Limitations By design, the in-process transport bypasses HTTP-wire concerns: * **No HTTP-level middleware** — CORS, compression, HTTP/2 flow control, request body size limits, and similar features do not apply because no bytes leave the process. * **No cross-process / IPC** — for cross-process communication (Unix sockets, separate hosts, worker\_threads) use HTTP transports. * **Streaming back-pressure** is provided by `AsyncIterable` semantics and is best-effort rather than HTTP/2 flow control. For very high-throughput streaming, prefer HTTP/2. * **Payload objects are shared by reference** inside the same process (as in any function call). Do not mutate request/response payloads after handing them off. `Headers` are explicitly cloned at the boundary. ## Coexistence with HTTP A single `Server` instance can simultaneously serve HTTP clients (after `server.start()`) and in-process clients (available immediately after `createServer()`). Both paths go through the same router and the same interceptor chain, so an interceptor observes both kinds of calls uniformly. ```typescript const server = createServer({ services: [routes] }); // In-process client works immediately, no socket bound. const local = server.client(MyService); await local.doWork({ /* ... */ }); // Bind HTTP/2 socket for external clients. await server.start(); // HTTP and in-process clients can be used concurrently. ``` If you do not call `server.start()`, no port is bound and `server.address` remains `null` — useful for tests and embedded use cases. ## Polyglot Deployment Pattern The auto-routing `server.client()` enables a single caller codebase that works in both monolithic and distributed deployments. Remote routing is decided once, when the server is created, by the `remoteResolver`. The call sites never change between topologies. ```typescript // shared/server.ts — same call sites in every deployment topology import { createServer, singleTransportResolver } from '@connectum/core'; import { createGrpcTransport } from '@connectrpc/connect-node'; export function buildServer(env: { upstreamUrl?: string }) { return createServer({ // Register only the services this process owns; the rest are remote. services: ownedServices, // Services not mounted locally are routed through the resolver. // Omit `remoteResolver` for a pure monolith that hosts everything. remoteResolver: env.upstreamUrl ? singleTransportResolver( createGrpcTransport({ baseUrl: env.upstreamUrl, httpVersion: '2' }), ) : undefined, }); } // shared/clients.ts — identical in every topology export function buildClients(server: Server) { return { inventory: server.client(InventoryService), pricing: server.client(PricingService), }; } ``` * **Monolith deployment**: register `InventoryService` and `PricingService` on the same `server` (no `remoteResolver` needed). Both clients route locally. * **Distributed deployment**: register only the services owned by this process and configure a `remoteResolver`. Others route remotely through the resolver. No change at the call site. * **Hybrid migration**: extract one service at a time. The client side never changes. If a service is not registered locally and no `remoteResolver` is configured, `server.client()` throws `CatalogConfigError` at construction — a fail-fast signal that deployment topology is misconfigured. A configured resolver that returns `null` for the service surfaces as `ConnectError(Code.Unavailable)` instead — also at construction, since the resolver runs inside `server.client()` before any RPC is invoked. For the full set of resolver factories (`singleTransportResolver`, `mapResolver`, `dnsResolver`, `perServiceEnvResolver`) see [Remote Resolvers](/en/guide/service-communication/resolvers). ## Testing `@connectum/testing` ships dedicated helpers for in-process testing: * **`createLocalClient(server, service)`** — concise client for unit and integration tests without binding ports. * **`transportParityTest(name, options)`** — driver that runs a single declarative scenario against both `createGrpcTransport({ baseUrl })` and `createLocalTransport(server)` and structurally diffs the observable outcome (response payload, headers, `ConnectError` fields, OTEL spans modulo `connectum.transport`, metrics modulo `transport` label). Any divergence fails the test. * **In-memory OTEL collectors** — `SpanExporter` and `MetricReader` helpers used by the parity driver for assertion on tracing and metrics output. Use the parity driver to guarantee that custom interceptors and proto-declared rules behave identically across transports: ```typescript import { ConnectError, createClient } from '@connectrpc/connect'; import { transportParityTest } from '@connectum/testing/parity'; transportParityTest('greeter.sayHello rejects empty name', { services: [greeterRoutes], scenario: async ({ transport }) => { const client = createClient(GreeterService, transport); try { return { response: await client.sayHello({ name: '' }) }; } catch (err) { const e = ConnectError.from(err); return { error: { code: e.code, message: e.message } }; } }, // `compare` is optional; omitted here, the default structural diff asserts // both transports produce an identical response/error/headers/spans/metrics // (modulo the `connectum.transport` attribute and `transport` metric label). }); ``` See [@connectum/testing](/en/packages/testing) for the full API. ## See Also * [@connectum/core](/en/packages/core) — module overview and generated API routes * [@connectum/otel](/en/packages/otel) — `connectum.transport` attribute and `transport` metric label * [@connectum/testing](/en/packages/testing) — `createLocalClient`, `transportParityTest` * [Connectum Runtime Architecture](/en/guide/production/architecture) * [API Reference: @connectum/core](/en/api/@connectum/core/) --- --- url: /en/api/@connectum/test-fixtures/index.md --- [Connectum API Reference](../../../index.md) / [@connectum/test-fixtures](../index.md) / index # index @connectum/test-fixtures — Mock factories and assertion helpers for Connectum tests. Transport-free (no `@connectum/core` dependency) so every Connectum package can depend on it without creating workspace build cycles. ## Interfaces * [MockCall](interfaces/MockCall.md) * [MockFn](interfaces/MockFn.md) ## Functions * [assertConnectError](functions/assertConnectError.md) * [createFakeMethod](functions/createFakeMethod.md) * [createFakeService](functions/createFakeService.md) * [createMockDescField](functions/createMockDescField.md) * [createMockDescMessage](functions/createMockDescMessage.md) * [createMockDescMethod](functions/createMockDescMethod.md) * [createMockFn](functions/createMockFn.md) * [createMockNext](functions/createMockNext.md) * [createMockNextError](functions/createMockNextError.md) * [createMockNextSlow](functions/createMockNextSlow.md) * [createMockRequest](functions/createMockRequest.md) * [createMockStream](functions/createMockStream.md) ## References ### FakeMethodOptions Re-exports [FakeMethodOptions](../types/interfaces/FakeMethodOptions.md) *** ### FakeServiceOptions Re-exports [FakeServiceOptions](../types/interfaces/FakeServiceOptions.md) *** ### MockDescFieldOptions Re-exports [MockDescFieldOptions](../types/interfaces/MockDescFieldOptions.md) *** ### MockDescMessageOptions Re-exports [MockDescMessageOptions](../types/interfaces/MockDescMessageOptions.md) *** ### MockDescMethodOptions Re-exports [MockDescMethodOptions](../types/interfaces/MockDescMethodOptions.md) *** ### MockNextOptions Re-exports [MockNextOptions](../types/interfaces/MockNextOptions.md) *** ### MockRequestOptions Re-exports [MockRequestOptions](../types/interfaces/MockRequestOptions.md) *** ### MockStreamOptions Re-exports [MockStreamOptions](../types/interfaces/MockStreamOptions.md) --- --- url: /en/api/@connectum/testing/index.md --- [Connectum API Reference](../../../index.md) / [@connectum/testing](../index.md) / index # index @connectum/testing — Testing utilities for the Connectum framework. Provides a test server utility, in-process transport helpers, OTel collectors, and a cross-transport parity driver to eliminate boilerplate in ConnectRPC service tests. Mock factories, assertion helpers and protobuf descriptor fixtures now live in `@connectum/test-fixtures`. They are re-exported from this entry for backwards compatibility — existing imports from `@connectum/testing` continue to work unchanged. ## Classes * [InMemoryMetricCollector](classes/InMemoryMetricCollector.md) * [InMemorySpanCollector](classes/InMemorySpanCollector.md) ## Interfaces * [CreateMockContextOptions](interfaces/CreateMockContextOptions.md) * [FakeMethodOptions](interfaces/FakeMethodOptions.md) * [FakeServiceOptions](interfaces/FakeServiceOptions.md) * [MockCall](interfaces/MockCall.md) * [MockDescFieldOptions](interfaces/MockDescFieldOptions.md) * [MockDescMessageOptions](interfaces/MockDescMessageOptions.md) * [MockDescMethodOptions](interfaces/MockDescMethodOptions.md) * [MockFn](interfaces/MockFn.md) * [MockNextOptions](interfaces/MockNextOptions.md) * [MockRequestOptions](interfaces/MockRequestOptions.md) * [MockService](interfaces/MockService.md) * [MockStreamOptions](interfaces/MockStreamOptions.md) * [NormalizedMetric](interfaces/NormalizedMetric.md) * [NormalizedSpan](interfaces/NormalizedSpan.md) ## Variables * [MOCK\_RESPONSE\_HEADER](variables/MOCK_RESPONSE_HEADER.md) * [TRANSPORT\_METRIC\_ATTRIBUTE](variables/TRANSPORT_METRIC_ATTRIBUTE.md) * [TRANSPORT\_SPAN\_ATTRIBUTE](variables/TRANSPORT_SPAN_ATTRIBUTE.md) ## Functions * [assertConnectError](functions/assertConnectError.md) * [createFakeMethod](functions/createFakeMethod.md) * [createFakeService](functions/createFakeService.md) * [createLocalClient](functions/createLocalClient.md) * [createMockContext](functions/createMockContext.md) * [createMockDescField](functions/createMockDescField.md) * [createMockDescMessage](functions/createMockDescMessage.md) * [createMockDescMethod](functions/createMockDescMethod.md) * [createMockFn](functions/createMockFn.md) * [createMockNext](functions/createMockNext.md) * [createMockNextError](functions/createMockNextError.md) * [createMockNextSlow](functions/createMockNextSlow.md) * [createMockRequest](functions/createMockRequest.md) * [createMockStream](functions/createMockStream.md) * [createTestServer](functions/createTestServer.md) * [mockResolver](functions/mockResolver.md) * [mockService](functions/mockService.md) * [withTestServer](functions/withTestServer.md) ## References ### CreateTestServerOptions Re-exports [CreateTestServerOptions](../types/interfaces/CreateTestServerOptions.md) *** ### TestServer Re-exports [TestServer](../types/interfaces/TestServer.md) --- --- 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 description: >- Choose and compose Connectum middleware without duplicating exact option reference. --- # Interceptors Interceptors wrap RPC execution with cross-cutting behavior while handlers stay focused on business logic. Their order is observable: a request moves from the first interceptor toward the handler and the response unwinds in reverse. ## Start with the default chain ```typescript import { createDefaultInterceptors } from '@connectum/interceptors'; const interceptors = createDefaultInterceptors({ timeout: { duration: 10_000 }, retry: { maxRetries: 2 }, }); ``` Error handling and validation are structural defaults. Timeout, bulkhead, circuit breaker, retry, fallback, and serializer behavior is opt-in because it changes request semantics. The canonical chain order, defaults, and standalone factories live in [Built-in interceptors](/en/guide/interceptors/built-in); exact fields live in [`DefaultInterceptorOptions`](/en/api/@connectum/interceptors/defaults/interfaces/DefaultInterceptorOptions). ## Choose the extension route | Need | Route | |---|---| | Configure the supported chain | [Built-in interceptors](/en/guide/interceptors/built-in) | | Add business-specific middleware | [Custom interceptors](/en/guide/interceptors/custom) | | Apply behavior to selected services or methods | [Method filtering](/en/guide/interceptors/method-filtering) | | Authenticate or authorize calls | [Auth and authz](/en/guide/auth) | | Trace incoming or outgoing calls | [Tracing](/en/guide/observability/tracing) | | Look up an exact factory or option | [`@connectum/interceptors` API](/en/api/@connectum/interceptors/) | Use native router scoping when an interceptor belongs to one service, a method-filter interceptor for declarative name patterns, and a custom interceptor only when selection depends on runtime request data. ## Module route The [`@connectum/interceptors` module hub](/en/packages/interceptors) owns installation, the minimal example, key entry points, and Learn / Configure / API navigation. --- --- url: /en/api/@connectum/events/types/interfaces/AdapterContext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / AdapterContext # Interface: AdapterContext Defined in: [packages/events/src/types.ts:93](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L93) Context provided to adapters by the EventBus before connect(). Contains service-level information derived from registered proto service descriptors. Adapters may use this for broker-level identification (e.g., Kafka clientId, NATS connection name, Redis connectionName). ## Properties ### serviceName? > `readonly` `optional` **serviceName?**: `string` Defined in: [packages/events/src/types.ts:103](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L103) Service identifier derived from proto service names. Format: `{packageNames}@{hostname}` Examples: * `"order.v1@pod-abc123"` (single service) * `"order.v1/payment.v1@pod-abc123"` (multiple services) --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpAdapterOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpAdapterOptions # Interface: AmqpAdapterOptions Defined in: [packages/events-amqp/src/types.ts:10](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L10) Options for creating an AMQP/RabbitMQ adapter. ## Properties ### consumerOptions? > `readonly` `optional` **consumerOptions?**: [`AmqpConsumerOptions`](AmqpConsumerOptions.md) Defined in: [packages/events-amqp/src/types.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L50) Consumer options. *** ### exchange? > `readonly` `optional` **exchange?**: `string` Defined in: [packages/events-amqp/src/types.ts:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L28) Exchange name for publishing and subscribing. #### Default ```ts "connectum.events" ``` *** ### exchangeOptions? > `readonly` `optional` **exchangeOptions?**: [`AmqpExchangeOptions`](AmqpExchangeOptions.md) Defined in: [packages/events-amqp/src/types.ts:40](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L40) Exchange assertion options. *** ### exchangeType? > `readonly` `optional` **exchangeType?**: `"headers"` | `"topic"` | `"direct"` | `"fanout"` Defined in: [packages/events-amqp/src/types.ts:35](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L35) Exchange type. #### Default ```ts "topic" ``` *** ### failFastOnInitialSetupError? > `readonly` `optional` **failFastOnInitialSetupError?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:152](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L152) Fail fast on a DETERMINISTIC setup/topology error on the FIRST connect, instead of entering amqplib's infinite recovery loop. amqplib's opt-in recovery resolves `connect()` only after its setup hook succeeds, and rejects only once `maxRetries` is exhausted (default `Infinity`). A permanent topology error on the first connect under the default recovery therefore HANGS `connect()` forever, with no thrown error and — because the lifecycle listeners attach only after that never-returning await — no callback. When this flag is `true` (and recovery is enabled), the adapter first validates topology against a throwaway non-recovering connection; a topology error rejects `connect()` with the typed `AmqpTopologyError` / `AmqpConnectionError`. Only deterministic setup/topology errors fail fast. A transient broker-unreachable at startup is NOT a fail-fast condition — it falls through to normal recovery (block-until-broker). SUBSEQUENT reconnects always keep infinite-recovery behavior. No-op with `recovery: false` (that path already fails fast on setup). Enabling this — or supplying [AmqpLifecycleCallbacks.onLifecycle](AmqpLifecycleCallbacks.md#onlifecycle) or [AmqpLifecycleCallbacks.onSetupFailed](AmqpLifecycleCallbacks.md#onsetupfailed) — adds one extra short-lived connection plus a topology validation pass at startup for the probe (recovery must be enabled; with `recovery: false` no probe runs and no `setup-failed` event is delivered). #### Default ```ts false ``` *** ### lifecycle? > `readonly` `optional` **lifecycle?**: [`AmqpLifecycleCallbacks`](AmqpLifecycleCallbacks.md) Defined in: [packages/events-amqp/src/types.ts:249](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L249) Connection lifecycle callbacks. Connection errors are surfaced here — not just logged. *** ### publisherOptions? > `readonly` `optional` **publisherOptions?**: [`AmqpPublisherOptions`](AmqpPublisherOptions.md) Defined in: [packages/events-amqp/src/types.ts:55](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L55) Publisher options. *** ### publishRetry? > `readonly` `optional` **publishRetry?**: `boolean` | [`AmqpPublishRetryOptions`](AmqpPublishRetryOptions.md) Defined in: [packages/events-amqp/src/types.ts:243](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L243) Opt-in bounded publish retry for CONNECTION-CLASS outcomes (since 1.3.0). When enabled, a `publish()` that fails with `AmqpConnectionError` — publishing during a recovery window, or an in-flight confirm lost to a connection drop — is retried in place (the caller's promise stays pending) instead of rejecting immediately: a short broker blip becomes a transparent delay. `true` = defaults; an object tunes the budget. The retry boundary is [isAutoRetriablePublishError](../../functions/isAutoRetriablePublishError.md) — deliberately NARROWER than the at-least-once republish matrix in the error taxonomy: a broker `nack` is republish-safe by policy but is NOT auto-retried inline (it is an explicit broker refusal, e.g. an over-capacity queue — hammering it in a tight loop helps nobody). `AmqpPublishTimeoutError` joins the boundary only with `retryOnTimeout: true`. Semantics — read before enabling: * **At-least-once, full stop.** A retried publish whose previous attempt was lost IN FLIGHT (confirm never arrived: state UNKNOWN) may duplicate on the broker. `x-event-id` / `messageId` stay STABLE across attempts (also with `externalContract` — a caller-supplied id is reused as-is), so consumer-side dedup keys on them. * **Worst-case latency**: each attempt is bounded by `publishTimeoutMs` (default 30s), so `maxRetries: 5` can hold a single `publish()` for several minutes worst-case — far beyond typical 30s RPC timeouts. There is deliberately no second overall-deadline knob: bound the budget via `maxRetries`/`publishTimeoutMs`. * **Shutdown-aware**: the loop aborts on `disconnect()` (throws the last connection error) and, living inside the `adapter.publish()` promise, is automatically covered by the bus-level `drainPublishTimeout`. * **Single-flight** (`mandatory: true` with `correlationHeader: false`; `externalContract` forces the latter but single-flight still requires `mandatory`): retries hold the chain — ordering is preserved at the cost of head-of-line blocking during backoff. In this headerless mode a late `basic.return` from an abandoned timed-out attempt may mark the current one (correlation is attempt-agnostic without the header) — prefer the default header correlation when combining `mandatory` with `retryOnTimeout`. * **Deterministic channel-close is not retried**: a broker reply with a `404`/`406` code that killed the publish CHANNEL (e.g. a publish to a missing exchange under `topologyMode: "skip"`) surfaces immediately with the broker reply as `cause` — the connection stays up, recovery never recreates the channel, so retrying cannot heal. Backoff mirrors the recovery formula (same knob names and semantics, incl. cap-before-jitter), but the DEFAULT budget differs: `maxRetries` here defaults to **5** (bounded), not `Infinity`. #### Default ```ts undefined (disabled — behavior unchanged) ``` *** ### publishTimeoutMs? > `readonly` `optional` **publishTimeoutMs?**: `number` Defined in: [packages/events-amqp/src/types.ts:259](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L259) Per-publish broker-outcome deadline in milliseconds. A publish whose ack/nack/return/connection-loss outcome does not arrive in time rejects with `AmqpPublishTimeoutError` (message state UNKNOWN — an at-least-once producer should republish). #### Default ```ts 30000 ``` *** ### queueOptions? > `readonly` `optional` **queueOptions?**: [`AmqpQueueOptions`](AmqpQueueOptions.md) Defined in: [packages/events-amqp/src/types.ts:45](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L45) Default queue assertion options. *** ### queueOverrides? > `readonly` `optional` **queueOverrides?**: `Record`<`string`, [`AmqpQueueOverride`](AmqpQueueOverride.md)> Defined in: [packages/events-amqp/src/types.ts:101](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L101) Map a consumer group name to an externally-named queue. By default a group consumes from `${exchange}.${group}`. An override lets a subscription attach to a queue from an external contract (with its own arguments) instead. *** ### recovery? > `readonly` `optional` **recovery?**: `boolean` | [`AmqpRecoveryOptions`](AmqpRecoveryOptions.md) Defined in: [packages/events-amqp/src/types.ts:122](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L122) Automatic connection recovery (delegated to amqplib's opt-in recovery). Enabled by default; pass `false` to restore no-reconnect behavior. On every (re)connect the adapter re-creates its channels, re-applies topology (per `topologyMode`), and replays active subscriptions. In-flight publishes at the moment of a connection loss reject with `AmqpConnectionError`. `maxRetries` governs BOTH the initial connect and steady-state recovery (counter reset on success); under the default `Infinity`, `connect()` blocks until the broker is reachable rather than failing fast (see [AmqpAdapterOptions.failFastOnInitialSetupError](#failfastoninitialsetuperror) to fail fast on a deterministic startup misconfiguration). See [AmqpRecoveryOptions](AmqpRecoveryOptions.md) for the retry-budget scope and jitter/`maxDelay` overshoot. #### Default ```ts true (amqplib defaults: 100ms initial, ×2, 30s cap, jitter 0.2, infinite retries) ``` *** ### serialization? > `readonly` `optional` **serialization?**: [`AmqpSerializationOptions`](AmqpSerializationOptions.md) Defined in: [packages/events-amqp/src/types.ts:66](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L66) Message serialization metadata and optional wire transcoding. The adapter receives payloads as bytes (the EventBus serializes protobuf upstream); this option controls the AMQP `contentType` property and lets an application transcode the wire body — e.g. when the application serializes JSON itself and publishes through the adapter directly against an external AsyncAPI contract. *** ### socketOptions? > `readonly` `optional` **socketOptions?**: `Record`<`string`, `unknown`> Defined in: [packages/events-amqp/src/types.ts:21](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L21) Socket options passed to `amqplib.connect()`. *** ### topology? > `readonly` `optional` **topology?**: [`AmqpTopology`](AmqpTopology.md) Defined in: [packages/events-amqp/src/types.ts:74](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L74) Explicit topology to declare on connect (and re-declare after recovery): exchanges, queues with arbitrary external names and raw arguments (e.g. `x-dead-letter-exchange`), and bindings — including exchange-to-exchange. *** ### topologyMode? > `readonly` `optional` **topologyMode?**: [`AmqpTopologyMode`](../type-aliases/AmqpTopologyMode.md) Defined in: [packages/events-amqp/src/types.ts:92](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L92) How topology is established: * `"assert"` (default) — declare idempotently (assertExchange/assertQueue/bind); * `"check"` — existence-only verification (checkExchange/checkQueue). A missing object raises AmqpTopologyError, which fails `connect()` fast ONLY with `recovery: false` or `failFastOnInitialSetupError: true`; under the default recovery a first-connect check failure otherwise enters the (infinite) recovery loop and is surfaced via `onSetupFailed` / `onReconnecting` rather than rejecting `connect()`. AMQP offers no passive introspection: argument equivalence and binding presence are NOT verifiable in this mode (a conflicting redeclare elsewhere is PRECONDITION\_FAILED 406); * `"skip"` — no topology operations at all; the application owns topology. #### Default ```ts "assert" ``` *** ### treatTopologyErrorAsFatal? > `readonly` `optional` **treatTopologyErrorAsFatal?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:191](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L191) Treat DETERMINISTIC topology drift during steady-state recovery as fatal: stop the reconnect cycle instead of retrying forever against a misconfigured broker. Under the default `maxRetries: Infinity`, a queue/exchange deleted or redeclared incompatibly while the adapter is reconnecting makes every recovery attempt fail deterministically — the adapter would retry forever, reporting `setup-failed` on each attempt but never giving up. With this flag the adapter stops the cycle on the first such failure and reports the terminal `reconnect-failed` lifecycle event (after the `setup-failed` event for the same attempt); subsequent publishes fail fast with `AmqpConnectionError`. The gate is the AMQP reply code of the failure cause — `404` (NOT\_FOUND) or `406` (PRECONDITION\_FAILED) — NOT the error class: transient causes wrapped into `AmqpTopologyError` during a setup pass (broker restarting `320`, internal error `541`, resource locked `405`, a mid-setup connection drop) stay in normal recovery. One known transient 404 is excluded explicitly: a RabbitMQ cluster classic queue whose home node is down ("... down or inaccessible") stays in recovery. After the fatal stop the adapter is fully torn down: consumers are dead (subscription records are cleared, mirroring `disconnect()`), publishes fail fast, and a later `connect()` starts from a clean slate — re-subscribe explicitly. Scope: steady-state recovery only. Boot-time drift is the startup probe's job — see [failFastOnInitialSetupError](#failfastoninitialsetuperror). Setting both covers boot and steady state; the remaining window — broker unreachable at `connect()` time with drift surfacing before the first successful connect — is closed by [AmqpRecoveryOptions.initialConnectMaxRetries](AmqpRecoveryOptions.md#initialconnectmaxretries) (since 1.3.0), whose bounded phase surfaces those failures and rejects on exhaustion. #### Default ```ts false ``` *** ### url > `readonly` **url**: `string` Defined in: [packages/events-amqp/src/types.ts:16](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L16) AMQP connection URL. #### Example ```ts "amqp://guest:guest@localhost:5672" ``` --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpBindingDeclaration.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpBindingDeclaration # Interface: AmqpBindingDeclaration Defined in: [packages/events-amqp/src/types.ts:319](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L319) ## Properties ### arguments? > `readonly` `optional` **arguments?**: `Record`<`string`, `unknown`> Defined in: [packages/events-amqp/src/types.ts:327](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L327) *** ### exchange? > `readonly` `optional` **exchange?**: `string` Defined in: [packages/events-amqp/src/types.ts:323](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L323) Destination exchange name (exchange-to-exchange binding). *** ### queue? > `readonly` `optional` **queue?**: `string` Defined in: [packages/events-amqp/src/types.ts:321](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L321) Destination queue name (queue binding) — mutually exclusive with `exchange`. *** ### routingKey > `readonly` **routingKey**: `string` Defined in: [packages/events-amqp/src/types.ts:326](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L326) *** ### source > `readonly` **source**: `string` Defined in: [packages/events-amqp/src/types.ts:325](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L325) Source exchange. --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpConsumerOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpConsumerOptions # Interface: AmqpConsumerOptions Defined in: [packages/events-amqp/src/types.ts:603](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L603) Consumer options. ## Properties ### exclusive? > `readonly` `optional` **exclusive?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:617](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L617) Whether the consumer is exclusive to this connection. #### Default ```ts false ``` *** ### prefetch? > `readonly` `optional` **prefetch?**: `number` Defined in: [packages/events-amqp/src/types.ts:610](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L610) Prefetch count (QoS) — how many unacknowledged messages a consumer can have at a time. #### Default ```ts 10 ``` --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpExchangeDeclaration.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpExchangeDeclaration # Interface: AmqpExchangeDeclaration Defined in: [packages/events-amqp/src/types.ts:301](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L301) ## Properties ### arguments? > `readonly` `optional` **arguments?**: `Record`<`string`, `unknown`> Defined in: [packages/events-amqp/src/types.ts:307](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L307) Raw AMQP arguments passthrough. *** ### autoDelete? > `readonly` `optional` **autoDelete?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:305](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L305) *** ### durable? > `readonly` `optional` **durable?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:304](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L304) *** ### name > `readonly` **name**: `string` Defined in: [packages/events-amqp/src/types.ts:302](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L302) *** ### type > `readonly` **type**: `"headers"` | `"topic"` | `"direct"` | `"fanout"` Defined in: [packages/events-amqp/src/types.ts:303](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L303) --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpExchangeOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpExchangeOptions # Interface: AmqpExchangeOptions Defined in: [packages/events-amqp/src/types.ts:552](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L552) Exchange assertion options. ## Properties ### autoDelete? > `readonly` `optional` **autoDelete?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:565](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L565) Whether the exchange is deleted when the last queue unbinds. #### Default ```ts false ``` *** ### durable? > `readonly` `optional` **durable?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:558](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L558) Whether the exchange should survive broker restarts. #### Default ```ts true ``` --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpLifecycleCallbacks.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpLifecycleCallbacks # Interface: AmqpLifecycleCallbacks Defined in: [packages/events-amqp/src/types.ts:495](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L495) Connection lifecycle callbacks. Prefer the single discriminated [onLifecycle](#onlifecycle) callback; the flat callbacks are a compatibility shim over the same event stream and are deprecated since 1.3.0 (removal not before 2.0). ## Properties ### ~~onConnected?~~ > `readonly` `optional` **onConnected?**: () => `void` Defined in: [packages/events-amqp/src/types.ts:515](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L515) #### Returns `void` #### Deprecated Since 1.3.0 — use [onLifecycle](#onlifecycle) (`type: "connected"`). Kept until at least 2.0. *** ### ~~onDisconnected?~~ > `readonly` `optional` **onDisconnected?**: (`cause`) => `void` Defined in: [packages/events-amqp/src/types.ts:517](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L517) #### Parameters ##### cause `Error` #### Returns `void` #### Deprecated Since 1.3.0 — use [onLifecycle](#onlifecycle) (`type: "disconnected"`). Kept until at least 2.0. *** ### onLifecycle? > `readonly` `optional` **onLifecycle?**: (`event`) => `void` Defined in: [packages/events-amqp/src/types.ts:513](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L513) Single discriminated-union lifecycle callback — the preferred surface. Receives every [AmqpLifecycleEvent](../type-aliases/AmqpLifecycleEvent.md), including `blocked`/`unblocked`, which have no flat-callback equivalent. Flat callbacks (if also set) are invoked after `onLifecycle` for the same underlying event. MUST NOT throw: dispatch runs inside the connection driver's event handlers, so exceptions are isolated (swallowed) to protect the connection — a throwing callback neither disturbs recovery nor starves the flat shim. Setting this (like `onSetupFailed` / `failFastOnInitialSetupError`) enables the startup validation probe: one extra short-lived connection plus a topology validation pass at `connect()` (requires recovery enabled), so `setup-failed { initial: true }` can be delivered for a deterministic misconfiguration at boot. #### Parameters ##### event [`AmqpLifecycleEvent`](../type-aliases/AmqpLifecycleEvent.md) #### Returns `void` *** ### ~~onReconnectFailed?~~ > `readonly` `optional` **onReconnectFailed?**: (`cause`) => `void` Defined in: [packages/events-amqp/src/types.ts:530](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L530) #### Parameters ##### cause `Error` #### Returns `void` #### Deprecated Since 1.3.0 — use [onLifecycle](#onlifecycle) (`type: "reconnect-failed"`). Kept until at least 2.0. *** ### ~~onReconnecting?~~ > `readonly` `optional` **onReconnecting?**: (`info`) => `void` Defined in: [packages/events-amqp/src/types.ts:528](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L528) A reconnect attempt has been scheduled. Fires exactly ONCE per scheduled retry (amqplib's `reconnect-scheduled`). A failed attempt that also emits `connect-failed` does NOT double-invoke this; the terminal case (retry budget exhausted, or a fatal topology stop under `treatTopologyErrorAsFatal`) is reported via [onReconnectFailed](#onreconnectfailed), not here. #### Parameters ##### info ###### attempt `number` ###### delay `number` ###### error `Error` #### Returns `void` #### Deprecated Since 1.3.0 — use [onLifecycle](#onlifecycle) (`type: "reconnecting"`). Kept until at least 2.0. *** ### ~~onSetupFailed?~~ > `readonly` `optional` **onSetupFailed?**: (`error`, `ctx`) => `void` Defined in: [packages/events-amqp/src/types.ts:546](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L546) A setup/topology failure occurred while (re)applying the declarative topology — during the startup window (`ctx.initial: true`; `ctx.attempt` is 0 on the probe, or the 0-based attempt index in the bounded initial phase) and/or on a reconnect whose topology re-assert fails (`ctx.initial: false`, `ctx.attempt` ≥ 1). This surfaces deterministic configuration drift (e.g. a missing queue in `check` mode, or a `PRECONDITION_FAILED` redeclare) distinctly from a mere broker outage, even when fail-fast is off. The initial-connect invocation requires a startup validation probe, which runs when either this callback, [onLifecycle](#onlifecycle), or [AmqpAdapterOptions.failFastOnInitialSetupError](AmqpAdapterOptions.md#failfastoninitialsetuperror) is set. #### Parameters ##### error `Error` ##### ctx ###### attempt `number` ###### initial `boolean` #### Returns `void` #### Deprecated Since 1.3.0 — use [onLifecycle](#onlifecycle) (`type: "setup-failed"`). Kept until at least 2.0. --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpPublisherOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpPublisherOptions # Interface: AmqpPublisherOptions Defined in: [packages/events-amqp/src/types.ts:623](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L623) Publisher options. ## Properties ### correlationHeader? > `readonly` `optional` **correlationHeader?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:652](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L652) How `basic.return` frames are correlated to publishes when `mandatory: true`. The return frame carries no deliveryTag, so: * `true` (default): stamp a private `x-connectum-publish-id` header on mandatory publishes and match returns by it. The header is visible on the wire to external consumers — document it in contracts. * `false`: no header; mandatory publishes are serialized (single-flight) so at most one is outstanding at a time — correlation is unambiguous at the cost of throughput. #### Default ```ts true ``` *** ### externalContract? > `readonly` `optional` **externalContract?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:681](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L681) Publish against an EXTERNAL (non-EventBus) message contract: suppress the EventBus envelope so the wire frame carries ONLY contract-specified properties. For an external AsyncAPI/AMQP contract the oracle is the published spec, not this serializer — a third-party consumer validates the exact header/property set, which must not include adapter-internal fields. When `true`, `publish()`: * does NOT stamp the `x-event-id` / `x-published-at` headers; * does NOT auto-populate the `messageId` or `timestamp` properties; * uses single-flight correlation for `mandatory` publishes (so no `x-connectum-publish-id` header reaches the wire) — `correlationHeader` is ignored in this mode. The frame then carries only `contentType`, `persistent`/deliveryMode, `mandatory`, and exactly the headers passed via `PublishOptions.metadata`. Per-message confirms, `mandatory` → `AmqpUnroutableError`, the typed error taxonomy, and connection recovery are unchanged. Leave unset (default) for normal EventBus use, where the envelope is stamped on publish and stripped on delivery. When the contract requires a specific `messageId` / `timestamp`, set them per-publish via `PublishOptions.messageId` / `PublishOptions.timestamp` (a caller-supplied value is used as-is; in external-contract mode nothing is auto-generated). #### Default ```ts false ``` *** ### mandatory? > `readonly` `optional` **mandatory?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:637](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L637) Whether the message should be returned if it cannot be routed. Unroutable messages reject the publish with `AmqpUnroutableError`. #### Default ```ts false ``` *** ### persistent? > `readonly` `optional` **persistent?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:629](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L629) Whether messages should be persisted to disk (deliveryMode=2). #### Default ```ts true ``` --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpPublishRetryOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpPublishRetryOptions # Interface: AmqpPublishRetryOptions Defined in: [packages/events-amqp/src/types.ts:461](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L461) Tuning for the opt-in bounded publish retry ([AmqpAdapterOptions.publishRetry](AmqpAdapterOptions.md#publishretry)). Backoff knobs mirror [AmqpRecoveryOptions](AmqpRecoveryOptions.md) (same names, same cap-before-jitter semantics) — but `maxRetries` defaults to a BOUNDED `5` here, not `Infinity`. ## Properties ### factor? > `readonly` `optional` **factor?**: `number` Defined in: [packages/events-amqp/src/types.ts:469](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L469) Exponential backoff factor. #### Default ```ts 2 ``` *** ### initialDelay? > `readonly` `optional` **initialDelay?**: `number` Defined in: [packages/events-amqp/src/types.ts:465](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L465) First retry delay in ms. #### Default ```ts 100 ``` *** ### jitter? > `readonly` `optional` **jitter?**: `number` Defined in: [packages/events-amqp/src/types.ts:471](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L471) Symmetric jitter factor (0..1). #### Default ```ts 0.2 ``` *** ### maxDelay? > `readonly` `optional` **maxDelay?**: `number` Defined in: [packages/events-amqp/src/types.ts:467](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L467) Base delay cap in ms; jitter applies on top of the capped base. #### Default ```ts 30000 ``` *** ### maxRetries? > `readonly` `optional` **maxRetries?**: `number` Defined in: [packages/events-amqp/src/types.ts:463](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L463) Retries after the first attempt (N retries = N+1 attempts). A negative value clamps to `0` (single attempt); `Infinity` is honored — retry until `disconnect()` aborts. #### Default ```ts 5 ``` *** ### onRetry? > `readonly` `optional` **onRetry?**: (`info`) => `void` Defined in: [packages/events-amqp/src/types.ts:485](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L485) Observability hook, invoked once per scheduled retry. MUST NOT throw (exceptions are isolated). Scoped here deliberately — publish retries are per-operation events, not connection lifecycle, so they do not join [AmqpLifecycleEvent](../type-aliases/AmqpLifecycleEvent.md). #### Parameters ##### info ###### attempt `number` ###### delay `number` ###### error `Error` ###### routingKey `string` #### Returns `void` *** ### retryOnTimeout? > `readonly` `optional` **retryOnTimeout?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:478](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L478) Also retry `AmqpPublishTimeoutError` (no broker outcome within `publishTimeoutMs`). The message state at a timeout is UNKNOWN, so this raises the duplicate likelihood — enable only with consumer-side dedup. #### Default ```ts false ``` --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpQueueDeclaration.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpQueueDeclaration # Interface: AmqpQueueDeclaration Defined in: [packages/events-amqp/src/types.ts:310](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L310) ## Properties ### arguments? > `readonly` `optional` **arguments?**: `Record`<`string`, `unknown`> Defined in: [packages/events-amqp/src/types.ts:316](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L316) Raw AMQP arguments passthrough (e.g. x-dead-letter-exchange). *** ### autoDelete? > `readonly` `optional` **autoDelete?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:313](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L313) *** ### durable? > `readonly` `optional` **durable?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:312](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L312) *** ### exclusive? > `readonly` `optional` **exclusive?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:314](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L314) *** ### name > `readonly` **name**: `string` Defined in: [packages/events-amqp/src/types.ts:311](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L311) --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpQueueOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpQueueOptions # Interface: AmqpQueueOptions Defined in: [packages/events-amqp/src/types.ts:571](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L571) Queue assertion options. ## Properties ### deadLetterExchange? > `readonly` `optional` **deadLetterExchange?**: `string` Defined in: [packages/events-amqp/src/types.ts:592](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L592) Dead letter exchange name for rejected messages. *** ### deadLetterRoutingKey? > `readonly` `optional` **deadLetterRoutingKey?**: `string` Defined in: [packages/events-amqp/src/types.ts:597](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L597) Dead letter routing key for rejected messages. *** ### durable? > `readonly` `optional` **durable?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:577](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L577) Whether the queue should survive broker restarts. #### Default ```ts true ``` *** ### maxLength? > `readonly` `optional` **maxLength?**: `number` Defined in: [packages/events-amqp/src/types.ts:587](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L587) Maximum number of messages in the queue. *** ### messageTtl? > `readonly` `optional` **messageTtl?**: `number` Defined in: [packages/events-amqp/src/types.ts:582](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L582) Per-message TTL in milliseconds. --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpQueueOverride.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpQueueOverride # Interface: AmqpQueueOverride Defined in: [packages/events-amqp/src/types.ts:331](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L331) External queue override for a consumer group. ## Properties ### arguments? > `readonly` `optional` **arguments?**: `Record`<`string`, `unknown`> Defined in: [packages/events-amqp/src/types.ts:335](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L335) Raw AMQP arguments used when asserting the queue (assert mode only). *** ### durable? > `readonly` `optional` **durable?**: `boolean` Defined in: [packages/events-amqp/src/types.ts:337](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L337) #### Default ```ts true ``` *** ### queue > `readonly` **queue**: `string` Defined in: [packages/events-amqp/src/types.ts:333](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L333) Externally-defined queue name to consume from. --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpRecoveryOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpRecoveryOptions # Interface: AmqpRecoveryOptions Defined in: [packages/events-amqp/src/types.ts:364](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L364) Recovery knobs (passed through to amqplib's opt-in recovery). `maxRetries` governs BOTH the initial connect and every subsequent recovery series, with the counter reset on each success — so a finite value chosen only to bound startup also caps steady-state recovery and makes the adapter brittle (N consecutive transient failures in any single series stop it permanently). The effective reconnect delay is symmetric jitter around the exponential base — uniform in `[base × (1 − jitter), base × (1 + jitter)]` with `base = min(maxDelay, initialDelay × factor^(attempt − 1))`. The cap applies BEFORE jitter, so the wait can overshoot `maxDelay` (~20% at the default jitter, up to ~2x at `jitter: 1`). Full jitter with a hard cap is expressible today: set `jitter: 1` and halve `initialDelay`/`maxDelay` — the delay becomes uniform in `[0, intended cap]` (verified against amqplib 2.0.1's internal formula; re-verify on upgrades). The initial connect CAN be bounded independently since 1.3.0 — see [AmqpRecoveryOptions.initialConnectMaxRetries](#initialconnectmaxretries) (#198; upstream native support tracked in ). A pluggable backoff hook remains tracked in (upstream: ). ## Properties ### factor? > `readonly` `optional` **factor?**: `number` Defined in: [packages/events-amqp/src/types.ts:370](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L370) #### Default ```ts 2 ``` *** ### initialConnectMaxRetries? > `readonly` `optional` **initialConnectMaxRetries?**: `number` Defined in: [packages/events-amqp/src/types.ts:406](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L406) Bound the retry budget of the INITIAL connect independently of steady-state recovery: N retries = N+1 attempts, mirroring `maxRetries` semantics. A single `maxRetries` cannot express "bounded startup, unbounded steady-state" — its counter resets on every success. When set to an explicit finite value (a negative value clamps to `0` — single attempt — mirroring amqplib's `maxRetries` normalization), the adapter owns the initial window with a bounded validate-connect loop (the startup probe folds into it — validation IS each attempt, no extra connects): every attempt surfaces per-attempt lifecycle events (`reconnecting` with the next delay, `setup-failed { initial: true, attempt }` for topology failures), and budget exhaustion rejects `connect()` with a typed `AmqpConnectionError` after a terminal `reconnect-failed` — never a silent block. Backoff matches amqplib's steady-state formula exactly (same knobs above, same cap-before-jitter semantics). `failFastOnInitialSetupError` still short-circuits a deterministic topology error on the first sight, budget notwithstanding. Handoff caveat: after a successful validation the real recovering connect runs — a broker dying inside that small window blocks per amqplib's own initial loop. Unset (default): behavior unchanged — amqplib's initial loop with the shared `maxRetries` governs startup, and initial-window per-retry events are not surfaced. Since 1.3.0; upstream native support tracked in . *** ### initialDelay? > `readonly` `optional` **initialDelay?**: `number` Defined in: [packages/events-amqp/src/types.ts:366](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L366) #### Default ```ts 100 ``` *** ### jitter? > `readonly` `optional` **jitter?**: `number` Defined in: [packages/events-amqp/src/types.ts:372](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L372) Symmetric jitter factor (0..1): the delay is uniform in `[base × (1 − jitter), base × (1 + jitter)]`. #### Default ```ts 0.2 ``` *** ### maxDelay? > `readonly` `optional` **maxDelay?**: `number` Defined in: [packages/events-amqp/src/types.ts:368](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L368) Base delay cap in ms; jitter is applied on top of the capped base, so the effective wait can exceed it. #### Default ```ts 30000 ``` *** ### maxRetries? > `readonly` `optional` **maxRetries?**: `number` Defined in: [packages/events-amqp/src/types.ts:374](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L374) Attempts per series (initial connect and each recovery series); resets on success. To bound ONLY startup, use [initialConnectMaxRetries](#initialconnectmaxretries). #### Default ```ts Infinity ``` --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpSerializationOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpSerializationOptions # Interface: AmqpSerializationOptions Defined in: [packages/events-amqp/src/types.ts:272](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L272) Serialization metadata and optional wire transcoding. ## Properties ### contentType? > `readonly` `optional` **contentType?**: `string` Defined in: [packages/events-amqp/src/types.ts:278](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L278) AMQP `contentType` message property. #### Default ```ts "application/protobuf" ``` *** ### decode? > `readonly` `optional` **decode?**: (`content`) => `Uint8Array` Defined in: [packages/events-amqp/src/types.ts:291](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L291) Transform the incoming wire body before it reaches the event handler. Failures nack the message (requeue per consumer policy). #### Parameters ##### content `Uint8Array` #### Returns `Uint8Array` *** ### encode? > `readonly` `optional` **encode?**: (`payload`) => `Uint8Array` Defined in: [packages/events-amqp/src/types.ts:285](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L285) Transform the outgoing wire body. Receives the payload bytes the EventBus (or the application) produced. Failures reject the publish with `AmqpSerializationError`. #### Parameters ##### payload `Uint8Array` #### Returns `Uint8Array` --- --- url: /en/api/@connectum/events-amqp/types/interfaces/AmqpTopology.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpTopology # Interface: AmqpTopology Defined in: [packages/events-amqp/src/types.ts:295](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L295) Declarative topology. ## Properties ### bindings? > `readonly` `optional` **bindings?**: readonly [`AmqpBindingDeclaration`](AmqpBindingDeclaration.md)\[] Defined in: [packages/events-amqp/src/types.ts:298](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L298) *** ### exchanges? > `readonly` `optional` **exchanges?**: readonly [`AmqpExchangeDeclaration`](AmqpExchangeDeclaration.md)\[] Defined in: [packages/events-amqp/src/types.ts:296](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L296) *** ### queues? > `readonly` `optional` **queues?**: readonly [`AmqpQueueDeclaration`](AmqpQueueDeclaration.md)\[] Defined in: [packages/events-amqp/src/types.ts:297](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L297) --- --- 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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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:147](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L147) Parameters for building base RPC attributes. ## Properties ### method > **method**: `string` Defined in: [packages/otel/src/shared.ts:149](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L149) *** ### serverAddress > **serverAddress**: `string` Defined in: [packages/otel/src/shared.ts:150](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L150) *** ### serverPort? > `optional` **serverPort?**: `number` Defined in: [packages/otel/src/shared.ts:151](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L151) *** ### service > **service**: `string` Defined in: [packages/otel/src/shared.ts:148](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/shared.ts#L148) --- --- 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/main/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/main/packages/otel/src/config.ts#L52) *** ### maxExportBatchSize > **maxExportBatchSize**: `number` Defined in: [packages/otel/src/config.ts:49](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/config.ts#L49) *** ### maxQueueSize > **maxQueueSize**: `number` Defined in: [packages/otel/src/config.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/config.ts#L50) *** ### scheduledDelayMillis > **scheduledDelayMillis**: `number` Defined in: [packages/otel/src/config.ts:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/config.ts#L51) --- --- url: /en/api/@connectum/core/interfaces/BidiStreamHandle.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / BidiStreamHandle # Interface: BidiStreamHandle\ Defined in: [packages/core/src/context.ts:84](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L84) Push handle for a bidi-streaming catalog call: `send()` requests while iterating `responses`; `close()` ends only the request (send) half — the response half keeps yielding until the server completes. ## Type Parameters ### Req `Req` ### Res `Res` ## Properties ### responses > `readonly` **responses**: `AsyncIterable`<`Res`> Defined in: [packages/core/src/context.ts:90](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L90) The server's response messages, in order. ## Methods ### close() > **close**(): `void` Defined in: [packages/core/src/context.ts:88](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L88) End the request (send) half; the response half is unaffected. #### Returns `void` *** ### send() > **send**(`request`): `void` Defined in: [packages/core/src/context.ts:86](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L86) Enqueue one request message. #### Parameters ##### request `Req` #### Returns `void` --- --- url: /en/api/@connectum/events/interfaces/BroadcastReactor.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / BroadcastReactor # Interface: BroadcastReactor Defined in: [packages/events/src/broadcast.ts:23](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L23) One independent broadcast reactor: its consumer group + routes. ## Properties ### group > `readonly` **group**: `string` Defined in: [packages/events/src/broadcast.ts:25](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L25) Consumer group — MUST be DISTINCT per reactor for true fan-out (a shared group load-balances). *** ### middleware? > `readonly` `optional` **middleware?**: [`MiddlewareConfig`](../types/interfaces/MiddlewareConfig.md) Defined in: [packages/events/src/broadcast.ts:29](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L29) Optional per-reactor middleware (retry/DLQ/custom). *** ### routes > `readonly` **routes**: [`EventRoute`](../types/type-aliases/EventRoute.md)\[] Defined in: [packages/events/src/broadcast.ts:27](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L27) The event routes (handlers) this reactor subscribes with. --- --- url: /en/api/@connectum/events/interfaces/BroadcastSubscribersOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / BroadcastSubscribersOptions # Interface: BroadcastSubscribersOptions Defined in: [packages/events/src/broadcast.ts:33](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L33) Options for [createBroadcastSubscribers](../functions/createBroadcastSubscribers.md). ## Properties ### adapter > `readonly` **adapter**: [`EventAdapter`](../types/interfaces/EventAdapter.md) | [`EventAdapterFactory`](../types/type-aliases/EventAdapterFactory.md) Defined in: [packages/events/src/broadcast.ts:40](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L40) The broker adapter. Pass ONE shared instance (fine for `MemoryAdapter` in tests, where all buses share the in-memory registry) OR a factory invoked once per reactor (use this for real brokers so each reactor bus gets its own connection / durable consumer). *** ### drainPublishTimeout? > `readonly` `optional` **drainPublishTimeout?**: `number` Defined in: [packages/events/src/broadcast.ts:48](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L48) Shared per-bus opt-in publish drain budget at `stop()` (ms). Since 1.3.0. *** ### drainTimeout? > `readonly` `optional` **drainTimeout?**: `number` Defined in: [packages/events/src/broadcast.ts:46](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L46) Shared per-bus drain timeout (ms). *** ### handlerTimeout? > `readonly` `optional` **handlerTimeout?**: `number` Defined in: [packages/events/src/broadcast.ts:44](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L44) Shared per-bus handler timeout (ms). *** ### reactors > `readonly` **reactors**: [`BroadcastReactor`](BroadcastReactor.md)\[] Defined in: [packages/events/src/broadcast.ts:42](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L42) The independent reactors — each becomes its own EventBus with its own group. *** ### signal? > `readonly` `optional` **signal?**: `AbortSignal` Defined in: [packages/events/src/broadcast.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/broadcast.ts#L50) Shared abort signal for graceful shutdown. --- --- url: /en/api/@connectum/interceptors/interfaces/BulkheadOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / BulkheadOptions # Interface: BulkheadOptions Defined in: [types.ts:196](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L196) Bulkhead interceptor options ## Properties ### capacity? > `optional` **capacity?**: `number` Defined in: [types.ts:201](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L201) Maximum number of concurrent requests #### Default ```ts 10 ``` *** ### queueSize? > `optional` **queueSize?**: `number` Defined in: [types.ts:207](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L207) Maximum queue size for pending requests #### Default ```ts 10 ``` *** ### skipStreaming? > `optional` **skipStreaming?**: `boolean` Defined in: [types.ts:213](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L213) 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/main/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/main/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/main/packages/auth/src/types.ts#L107) Cache entry time-to-live in milliseconds --- --- url: /en/api/@connectum/core/interfaces/CatalogClient.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / CatalogClient # Interface: CatalogClient Defined in: [packages/core/src/catalogClient.ts:67](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogClient.ts#L67) A standalone, catalog-typed client. Exposes the SAME `call` (unary) and `stream` (server/client/bidi) surface as the handler [Context](Context.md), keyed off [ConnectumCallMap](ConnectumCallMap.md)/[ConnectumStreamMap](ConnectumStreamMap.md), without constructing a `Server`. ## Properties ### call > **call**: [`CatalogCall`](../type-aliases/CatalogCall.md) Defined in: [packages/core/src/catalogClient.ts:78](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogClient.ts#L78) Invoke a unary service in the catalog over the resolver-supplied transport. With no augmentation of [ConnectumCallMap](ConnectumCallMap.md) this is statically uncallable — exactly as on the handler `ctx`. Errors mirror `ctx.call`: no catalog / unknown service / unknown method / wrong kind → `Code.FailedPrecondition`/`Code.Unimplemented`; resolver returns `null` → `Code.Unavailable`; resolver throws → `Code.Internal` (cause preserved). *** ### stream > **stream**: [`CatalogStream`](../type-aliases/CatalogStream.md) Defined in: [packages/core/src/catalogClient.ts:85](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogClient.ts#L85) Open a streaming call to a service in the catalog over the resolver-supplied transport. Returns the same kind-specific factory as `ctx.stream` (server-streaming → `AsyncIterable`; client-/bidi-streaming → push handles). --- --- 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/main/packages/interceptors/src/types.ts#L126) Circuit breaker interceptor options ## Properties ### failurePredicate? > `optional` **failurePredicate?**: (`error`, `defaultPredicate`) => `boolean` Defined in: [types.ts:173](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L173) Decides whether an error counts as a circuit failure. Receives the default predicate as the second argument so the default policy can be composed (extended or restricted) instead of reimplemented: ```typescript // Extend: also trip on NotFound failurePredicate: (err, def) => def(err) || (err instanceof ConnectError && err.code === Code.NotFound) // Restrict: never trip on ResourceExhausted (e.g. upstream rate limits) failurePredicate: (err, def) => def(err) && !(err instanceof ConnectError && err.code === Code.ResourceExhausted) // Legacy behavior: every error trips the breaker failurePredicate: () => true ``` Errors NOT classified as failures never open or re-arm the breaker and, in half-open state, close the circuit (treated as a successful probe). A predicate that throws is treated as if it returned `true` (fail-closed); the original upstream error is always the one propagated to the caller. #### Parameters ##### error `unknown` ##### defaultPredicate (`error`) => `boolean` #### Returns `boolean` #### Default ```ts defaultFailurePredicate (infrastructure codes only: Unknown, DeadlineExceeded, Internal, Unavailable, DataLoss, ResourceExhausted; non-ConnectError values count as failures) ``` *** ### halfOpenAfter? > `optional` **halfOpenAfter?**: `number` Defined in: [types.ts:137](https://github.com/Connectum-Framework/connectum/blob/main/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/main/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/main/packages/interceptors/src/types.ts#L131) Number of consecutive failures before opening circuit #### Default ```ts 5 ``` --- --- url: /en/api/@connectum/auth/interfaces/ClientBearerInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / ClientBearerInterceptorOptions # Interface: ClientBearerInterceptorOptions Defined in: [packages/auth/src/types.ts:524](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L524) Client-side Bearer token interceptor options. ## See [createClientBearerInterceptor](../functions/createClientBearerInterceptor.md) ## Properties ### token > `readonly` **token**: `string` | (() => `Promise`<`string`>) Defined in: [packages/auth/src/types.ts:532](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L532) Bearer token value or async factory function. When a string is provided, the same token is sent with every request. When a function is provided, it is called before each request to support token refresh flows. --- --- url: /en/api/@connectum/auth/interfaces/ClientGatewayInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / ClientGatewayInterceptorOptions # Interface: ClientGatewayInterceptorOptions Defined in: [packages/auth/src/types.ts:540](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L540) Client-side gateway service-to-service auth interceptor options. ## See [createClientGatewayInterceptor](../functions/createClientGatewayInterceptor.md) ## Properties ### roles? > `readonly` `optional` **roles?**: `string`\[] Defined in: [packages/auth/src/types.ts:546](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L546) Optional roles to propagate (JSON-encoded in header) *** ### secret > `readonly` **secret**: `string` Defined in: [packages/auth/src/types.ts:542](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L542) Shared secret for gateway trust verification *** ### subject > `readonly` **subject**: `string` Defined in: [packages/auth/src/types.ts:544](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L544) Authenticated subject identifier (e.g., service name) --- --- url: /en/api/@connectum/core/interfaces/ClientStreamHandle.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ClientStreamHandle # Interface: ClientStreamHandle\ Defined in: [packages/core/src/context.ts:72](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L72) Push handle for a client-streaming catalog call: send N requests, then `close()` to receive the single aggregated response. ## Type Parameters ### Req `Req` ### Res `Res` ## Methods ### close() > **close**(): `Promise`<`Res`> Defined in: [packages/core/src/context.ts:76](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L76) End the request stream and resolve with the server's single response. #### Returns `Promise`<`Res`> *** ### send() > **send**(`request`): `void` Defined in: [packages/core/src/context.ts:74](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L74) Enqueue one request message. #### Parameters ##### request `Req` #### Returns `void` --- --- 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/main/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/main/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/main/packages/otel/src/config.ts#L42) --- --- url: /en/api/@connectum/core/interfaces/ConnectumCallMap.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ConnectumCallMap # Interface: ConnectumCallMap Defined in: [packages/core/src/serviceCatalog.ts:31](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/serviceCatalog.ts#L31) Module-augmentation target for type-safe **unary** `ctx.call(method, request)`. `@connectum/protoc-gen-catalog` augments this with one entry per unary RPC, keyed `"/"` → `{ request; response }`. It starts empty so that a project with no generated catalog still type-checks (calls are then untyped rather than a hard error). ## Properties ### echo.v1.EchoService/Echo > **echo.v1.EchoService/Echo**: `object` Defined in: [packages/core/tests/integration/catalogClient.test.ts:37](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/catalogClient.test.ts#L37) #### request > **request**: `EchoRequest` #### response > **response**: `EchoResponse` *** ### echo.v1.EchoService/Nope > **echo.v1.EchoService/Nope**: `object` Defined in: [packages/core/tests/integration/ctxCall.test.ts:34](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/ctxCall.test.ts#L34) #### request > **request**: `EchoRequest` #### response > **response**: `EchoResponse` *** ### echo.v1.EchoService/RateLimitedEcho > **echo.v1.EchoService/RateLimitedEcho**: `object` Defined in: [packages/core/tests/integration/ctxCallErrorTrailers.test.ts:23](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/ctxCallErrorTrailers.test.ts#L23) #### request > **request**: `EchoRequest` #### response > **response**: `EchoResponse` *** ### ghost.v1.GhostService/Vanish > **ghost.v1.GhostService/Vanish**: `object` Defined in: [packages/core/tests/integration/ctxCall.test.ts:33](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/ctxCall.test.ts#L33) #### request > **request**: `EchoRequest` #### response > **response**: `EchoResponse` *** ### phantom.v1.PhantomService/Vanish > **phantom.v1.PhantomService/Vanish**: `object` Defined in: [packages/core/tests/integration/catalogClient.test.ts:38](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/catalogClient.test.ts#L38) #### request > **request**: `Item` #### response > **response**: `Item` *** ### streaming.v1.StreamingService/Absent > **streaming.v1.StreamingService/Absent**: `object` Defined in: [packages/core/tests/integration/catalogClient.test.ts:39](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/catalogClient.test.ts#L39) #### request > **request**: `Item` #### response > **response**: `Item` *** ### streaming.v1.StreamingService/Echo > **streaming.v1.StreamingService/Echo**: `object` Defined in: [packages/core/tests/integration/catalogClient.test.ts:36](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/catalogClient.test.ts#L36) #### request > **request**: `Item` #### response > **response**: `Item` --- --- url: /en/api/@connectum/core/interfaces/ConnectumStreamMap.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ConnectumStreamMap # Interface: ConnectumStreamMap Defined in: [packages/core/src/serviceCatalog.ts:42](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/serviceCatalog.ts#L42) Module-augmentation target for type-safe **streaming** `ctx.stream(method, ...)`. Augmented per streaming RPC, keyed `"/"` → `{ request; response; kind }` where `kind` is `"server-stream"`, `"client-stream"`, or `"bidi"`. Unary RPCs never appear here — they go to [ConnectumCallMap](ConnectumCallMap.md). ## Properties ### streaming.v1.StreamingService/Bidi > **streaming.v1.StreamingService/Bidi**: `object` Defined in: [packages/core/tests/integration/catalogClient.test.ts:44](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/catalogClient.test.ts#L44) #### kind > **kind**: `"bidi"` #### request > **request**: `Item` #### response > **response**: `Item` *** ### streaming.v1.StreamingService/Client > **streaming.v1.StreamingService/Client**: `object` Defined in: [packages/core/tests/integration/catalogClient.test.ts:43](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/catalogClient.test.ts#L43) #### kind > **kind**: `"client-stream"` #### request > **request**: `Item` #### response > **response**: `Count` *** ### streaming.v1.StreamingService/Server > **streaming.v1.StreamingService/Server**: `object` Defined in: [packages/core/tests/integration/catalogClient.test.ts:42](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/tests/integration/catalogClient.test.ts#L42) #### kind > **kind**: `"server-stream"` #### request > **request**: `Item` #### response > **response**: `Item` --- --- url: /en/api/@connectum/core/interfaces/Context.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / Context # Interface: Context Defined in: [packages/core/src/context.ts:131](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L131) The context object passed to every Connectum service handler. Extends ConnectRPC's `HandlerContext` (all of its fields remain available) and adds [Context.call](#call) (unary catalog calls) and [Context.stream](#stream) (streaming catalog calls). ## Extends * `HandlerContext` ## Properties ### call > **call**: [`CatalogCall`](../type-aliases/CatalogCall.md) Defined in: [packages/core/src/context.ts:142](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L142) Invoke a unary service in the catalog. The transport is chosen automatically: an in-process call when the target is mounted locally, otherwise the `remoteResolver`-supplied transport. `signal` and `timeoutMs` cascade from the incoming request unless overridden in `options` (see [CallOptions](../type-aliases/CallOptions.md)). #### Type Param **K** A `"${typeName}/${Method}"` key of [ConnectumCallMap](ConnectumCallMap.md). *** ### method > `readonly` **method**: `DescMethod` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:21 Metadata for the method being called. #### Inherited from `HandlerContext.method` *** ### protocolName > `readonly` **protocolName**: `string` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:62 Name of the RPC protocol in use; one of "connect", "grpc" or "grpc-web". #### Inherited from `HandlerContext.protocolName` *** ### requestHeader > `readonly` **requestHeader**: `Headers` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:47 Incoming request headers. #### Inherited from `HandlerContext.requestHeader` *** ### requestMethod > `readonly` **requestMethod**: `string` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:43 HTTP method of incoming request, usually "POST", but "GET" in the case of Connect Get. #### Inherited from `HandlerContext.requestMethod` *** ### responseHeader > `readonly` **responseHeader**: `Headers` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:54 Outgoing response headers. For methods that return a stream, response headers must be set before yielding the first response message. #### Inherited from `HandlerContext.responseHeader` *** ### responseTrailer > `readonly` **responseTrailer**: `Headers` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:58 Outgoing response trailers. #### Inherited from `HandlerContext.responseTrailer` *** ### service > `readonly` **service**: `DescService` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:25 Metadata for the service being called. #### Inherited from `HandlerContext.service` *** ### signal > `readonly` **signal**: `AbortSignal` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:33 An AbortSignal that triggers when the deadline is reached, or when an error occurs that aborts processing of the request, but also when the RPC is completed without error. The signal can be used to automatically cancel downstream calls. #### Inherited from `HandlerContext.signal` *** ### stream > **stream**: [`CatalogStream`](../type-aliases/CatalogStream.md) Defined in: [packages/core/src/context.ts:155](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L155) Open a streaming call to a service in the catalog. Returns a kind-specific factory: server-streaming yields an `AsyncIterable`; client- and bidi-streaming return push handles (see [ClientStreamHandle](ClientStreamHandle.md) / [BidiStreamHandle](BidiStreamHandle.md)). On a mid-stream transport failure the iterator delivers the messages received so far and then throws the terminal `ConnectError`. #### Type Param **K** A `"${typeName}/${Method}"` key of [ConnectumStreamMap](ConnectumStreamMap.md). *** ### timeoutMs > `readonly` **timeoutMs**: () => `number` | `undefined` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:38 If the current request has a timeout, this function returns the remaining time. #### Returns `number` | `undefined` #### Inherited from `HandlerContext.timeoutMs` *** ### url > `readonly` **url**: `string` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:70 The URL received by the server. #### Inherited from `HandlerContext.url` *** ### values > `readonly` **values**: `ContextValues` Defined in: node\_modules/.pnpm/@connectrpc+connect@2.1.2\_@bufbuild+protobuf@2.13.0/node\_modules/@connectrpc/connect/dist/esm/implementation.d.ts:66 Per RPC context values that can be used to pass data to handlers. #### Inherited from `HandlerContext.values` --- --- url: /en/api/@connectum/core/interfaces/CreateCatalogClientOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / CreateCatalogClientOptions # Interface: CreateCatalogClientOptions Defined in: [packages/core/src/catalogClient.ts:46](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogClient.ts#L46) Options for [createCatalogClient](../functions/createCatalogClient.md). ## Properties ### catalog > **catalog**: [`ServiceCatalog`](../type-aliases/ServiceCatalog.md) Defined in: [packages/core/src/catalogClient.ts:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogClient.ts#L51) The service catalog (a `Record`) that backs typed dispatch — the same object passed to `createServer({ catalog })`. *** ### resolver > **resolver**: [`RemoteResolver`](../type-aliases/RemoteResolver.md) Defined in: [packages/core/src/catalogClient.ts:58](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/catalogClient.ts#L58) Maps a target service `typeName` to a ConnectRPC `Transport`. Required: unlike a `Server`, a catalog client has no in-process/local path, so every call is routed through the resolver. A resolver that returns `null` for a target makes that call fail with `Code.Unavailable`. --- --- url: /en/api/@connectum/core/interfaces/CreateLocalTransportOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / CreateLocalTransportOptions # Interface: CreateLocalTransportOptions Defined in: [packages/core/src/localTransport.ts:44](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/localTransport.ts#L44) Options for [createLocalTransport](../functions/createLocalTransport.md). ## Properties ### interceptors? > `optional` **interceptors?**: `Interceptor`\[] Defined in: [packages/core/src/localTransport.ts:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/localTransport.ts#L51) Client-side interceptors applied to outbound calls before they reach the registered handlers. Server-side interceptors configured on the `Server` instance still run inside the handler chain — these are additive and run on the client side of the in-memory pipe. --- --- url: /en/api/@connectum/testing/index/interfaces/CreateMockContextOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / CreateMockContextOptions # Interface: CreateMockContextOptions Defined in: [testing/src/mockContext.ts:22](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockContext.ts#L22) Options for [createMockContext](../functions/createMockContext.md). ## Properties ### catalog > `readonly` **catalog**: `ServiceCatalog` Defined in: [testing/src/mockContext.ts:24](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockContext.ts#L24) The catalog the handler-under-test calls into. *** ### mocks > `readonly` **mocks**: readonly [`MockService`](MockService.md)\[] Defined in: [testing/src/mockContext.ts:26](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockContext.ts#L26) Mock implementations served via the catalog's resolver path. *** ### outgoingInterceptors? > `readonly` `optional` **outgoingInterceptors?**: readonly `Interceptor`\[] Defined in: [testing/src/mockContext.ts:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockContext.ts#L28) Optional outgoing interceptors (applied exactly as in production). *** ### propagateHeaders? > `readonly` `optional` **propagateHeaders?**: readonly `string`\[] Defined in: [testing/src/mockContext.ts:34](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockContext.ts#L34) Optional header names propagated onto outgoing calls (default none). *** ### requestHeader? > `readonly` `optional` **requestHeader?**: `HeadersInit` Defined in: [testing/src/mockContext.ts:30](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockContext.ts#L30) Optional inbound headers (seen by `ctx.requestHeader` + header propagation). *** ### timeoutMs? > `readonly` `optional` **timeoutMs?**: `number` Defined in: [testing/src/mockContext.ts:32](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockContext.ts#L32) Optional inbound deadline in ms (drives the `ctx.timeoutMs()` cascade). --- --- 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:215](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L215) Server configuration options for createServer() ## Properties ### allowHTTP1? > `optional` **allowHTTP1?**: `boolean` Defined in: [packages/core/src/types.ts:299](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L299) 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 ``` *** ### catalog? > `optional` **catalog?**: `Readonly`<`Record`<`string`, `DescService`>> Defined in: [packages/core/src/types.ts:368](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L368) The full set of services known to the system, `typeName → DescService` (typically the generated `serviceCatalog`). Drives startup validation and remote routing. Optional — a process that hosts everything locally and makes no cross-service calls needs no catalog. *** ### enabledServices? > `optional` **enabledServices?**: readonly `string`\[] Defined in: [packages/core/src/types.ts:376](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L376) Proto `typeName`s to mount **locally** from `services`. A service in `services` whose `typeName` is not listed is treated as remote (resolved via [CreateServerOptions.remoteResolver](#remoteresolver)). `undefined` mounts every provided service locally. *** ### eventBus? > `optional` **eventBus?**: [`EventBusLike`](EventBusLike.md) Defined in: [packages/core/src/types.ts:288](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L288) Event bus instance for pub/sub messaging. The event bus is started during `server.start()` (after route building, before transport listen) and stopped during graceful shutdown. #### Example ```typescript import { createEventBus } from '@connectum/events'; import { NatsAdapter } from '@connectum/events-nats'; const eventBus = createEventBus({ adapter: NatsAdapter({ servers: ['nats://localhost:4222'] }), router: eventRouter, }); const server = createServer({ services: [routes], eventBus, }); ``` *** ### handshakeTimeout? > `optional` **handshakeTimeout?**: `number` Defined in: [packages/core/src/types.ts:327](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L327) Handshake timeout in milliseconds #### Default ```ts 30000 ``` *** ### host? > `optional` **host?**: `string` Defined in: [packages/core/src/types.ts:231](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L231) 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:332](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L332) Additional HTTP/2 server options *** ### interceptors? > `optional` **interceptors?**: `Interceptor`\[] Defined in: [packages/core/src/types.ts:264](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L264) ConnectRPC interceptors. When omitted or `[]`, no interceptors are applied. Use `createDefaultInterceptors()` from `@connectum/interceptors` to get the default chain. *** ### jsonOptions? > `optional` **jsonOptions?**: `Partial`<`JsonReadOptions` & `JsonWriteOptions`> Defined in: [packages/core/src/types.ts:358](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L358) Connect JSON serialization options applied server-wide. Passed through to the underlying `connectNodeAdapter`, so it affects every registered service and protocol (e.g. healthcheck, reflection). The most common use is `alwaysEmitImplicit: true`, which includes fields with implicit presence (proto3 scalar `0`, empty string/list, enum default) in JSON responses instead of omitting them. For per-service control, pass the same option as the third argument of `router.service()` inside a [ServiceDefinition](../../interfaces/ServiceDefinition.md)'s `register` closure instead. Note: the relevant `JsonWriteOptions` field in `@bufbuild/protobuf` v2 is `alwaysEmitImplicit` (named `emitDefaultValues` in v1). #### Example ```typescript const server = createServer({ services: [routes], jsonOptions: { alwaysEmitImplicit: true }, }); ``` *** ### outgoingInterceptors? > `optional` **outgoingInterceptors?**: readonly `Interceptor`\[] Defined in: [packages/core/src/types.ts:390](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L390) Client-side interceptors applied to every outgoing `server.client()` / `ctx.call` call (cross-cutting concerns like auth or logging), so call sites stay free of boilerplate. *** ### port? > `optional` **port?**: `number` Defined in: [packages/core/src/types.ts:225](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L225) Server port #### Default ```ts 5000 ``` *** ### propagateHeaders? > `optional` **propagateHeaders?**: readonly `string`\[] Defined in: [packages/core/src/types.ts:400](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L400) Inbound header names to copy onto every outgoing `ctx.call` / `ctx.stream`. Empty by default — no header is propagated implicitly. Explicit `CallOptions.headers` always win over a propagated value. Use [defaultPropagateHeaders](../../variables/defaultPropagateHeaders.md) (W3C trace-context headers) as a base and add your own, e.g. `[...defaultPropagateHeaders, "x-tenant-id"]`. *** ### protocols? > `optional` **protocols?**: [`ProtocolRegistration`](ProtocolRegistration.md)\[] Defined in: [packages/core/src/types.ts:252](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L252) 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()], }); ``` *** ### remoteResolver? > `optional` **remoteResolver?**: [`RemoteResolver`](../../type-aliases/RemoteResolver.md) Defined in: [packages/core/src/types.ts:383](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L383) Resolves a service that is not mounted locally to a `Transport`. Consulted by `server.client()` (and `ctx.call`) for remote services. Synchronous and must not perform network I/O — see [RemoteResolver](../../type-aliases/RemoteResolver.md). *** ### services > **services**: readonly [`ServiceDefinition`](../../interfaces/ServiceDefinition.md)\[] Defined in: [packages/core/src/types.ts:219](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L219) Service routes to register *** ### shutdown? > `optional` **shutdown?**: [`ShutdownOptions`](ShutdownOptions.md) Defined in: [packages/core/src/types.ts:257](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L257) Graceful shutdown configuration *** ### tls? > `optional` **tls?**: [`TLSOptions`](TLSOptions.md) Defined in: [packages/core/src/types.ts:236](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L236) TLS configuration *** ### transportValidation? > `optional` **transportValidation?**: `"error"` | `"warn"` | `"off"` Defined in: [packages/core/src/types.ts:321](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L321) Startup validation of streaming method kinds vs the effective transport. Bidi-streaming methods require HTTP/2 (Connect protocol: "Bidirectional streaming requires HTTP/2, but the other RPC types also support HTTP/1.1"). On a plaintext HTTP/1.1 server (no TLS + `allowHTTP1: true`, the default) they fail silently at runtime — the first send hangs forever. With `"error"` (default) `start()` rejects with a `TransportValidationError` (code `CONNECTUM_UNSUPPORTED_STREAMING_TRANSPORT`) naming the affected methods and both fixes; `"warn"` logs once and starts anyway; `"off"` skips the check. On a TLS server that also allows HTTP/1.1 (`allowHTTP1: true`), bidi works for HTTP/2 clients but a client negotiating HTTP/1.1 over TLS hits the same hang — this residual risk is always a one-time warning (never a hard error), silenced only by `"off"`. Set `allowHTTP1: false` to remove the risk (the server refuses HTTP/1.1 at ALPN). #### Default ```ts "error" ``` --- --- url: /en/api/@connectum/testing/types/interfaces/CreateTestServerOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [types](../index.md) / CreateTestServerOptions # Interface: CreateTestServerOptions Defined in: [testing/src/types.ts:38](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L38) Options for createTestServer. ## Properties ### interceptors? > `optional` **interceptors?**: `unknown`\[] Defined in: [testing/src/types.ts:42](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L42) Interceptors to apply. Default: `[]` *** ### port? > `optional` **port?**: `number` Defined in: [testing/src/types.ts:46](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L46) Port number. Default: `0` (random available port) *** ### protocols? > `optional` **protocols?**: `unknown`\[] Defined in: [testing/src/types.ts:44](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L44) Protocol extensions (Healthcheck, Reflection). Default: `[]` *** ### services > **services**: `unknown`\[] Defined in: [testing/src/types.ts:40](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L40) ConnectRPC service route handlers. --- --- 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:39](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L39) 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 Only structural interceptors (errorHandler, validation) are enabled by default. Behavioral resilience interceptors (timeout, bulkhead, circuitBreaker, retry) are opt-in: implicitly enabled behavior-altering logic is hidden logic, and hidden logic caused a confirmed production incident (a server-side circuit breaker tripping on expected business errors). Enable each one explicitly where you need it. ## Properties ### bulkhead? > `optional` **bulkhead?**: `boolean` | [`BulkheadOptions`](../../interfaces/BulkheadOptions.md) Defined in: [defaults.ts:61](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L61) Bulkhead interceptor. Limits concurrent requests to prevent resource exhaustion. Opt-in: no hidden behavioral logic. #### Default ```ts false ``` *** ### circuitBreaker? > `optional` **circuitBreaker?**: `boolean` | [`CircuitBreakerOptions`](../../interfaces/CircuitBreakerOptions.md) Defined in: [defaults.ts:70](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L70) Circuit breaker interceptor. Prevents cascading failures by breaking circuit on consecutive errors. Opt-in: no hidden behavioral logic. Intended primarily for outbound client transports — see the README before enabling it server-side. #### Default ```ts false ``` *** ### errorHandler? > `optional` **errorHandler?**: `boolean` | [`ErrorHandlerOptions`](../../interfaces/ErrorHandlerOptions.md) Defined in: [defaults.ts:45](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L45) 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:86](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L86) 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:78](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L78) Retry interceptor. Retries transient failures with exponential backoff. Opt-in: no hidden behavioral logic. #### Default ```ts false ``` *** ### serializer? > `optional` **serializer?**: `boolean` | [`SerializerOptions`](../../interfaces/SerializerOptions.md) Defined in: [defaults.ts:101](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L101) Serializer interceptor (last in chain). Auto JSON serialization for ConnectRPC responses. Disabled by default — enable explicitly when JSON output is needed. #### Default ```ts false ``` *** ### timeout? > `optional` **timeout?**: `boolean` | [`TimeoutOptions`](../../interfaces/TimeoutOptions.md) Defined in: [defaults.ts:53](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L53) Timeout interceptor. Enforces request deadline before any processing. Opt-in: no hidden behavioral logic. #### Default ```ts false ``` *** ### validation? > `optional` **validation?**: `boolean` Defined in: [defaults.ts:93](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/defaults.ts#L93) Validation interceptor. Validates request messages using @connectrpc/validate. #### Default ```ts true ``` --- --- url: /en/api/@connectum/events/types/interfaces/DlqOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / DlqOptions # Interface: DlqOptions Defined in: [packages/events/src/types.ts:297](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L297) Dead letter queue middleware options ## Properties ### errorSerializer? > `optional` **errorSerializer?**: (`error`) => `string` Defined in: [packages/events/src/types.ts:306](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L306) Custom error serializer for DLQ metadata. Defaults to `error.name` only (e.g. "TypeError") to prevent credential leaks. For production, provide a custom serializer that redacts sensitive data (connection strings, tokens) before including error details. #### Parameters ##### error `unknown` #### Returns `string` *** ### topic > **topic**: `string` Defined in: [packages/events/src/types.ts:299](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L299) DLQ topic name --- --- url: /en/api/@connectum/core/interfaces/DnsResolverOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / DnsResolverOptions # Interface: DnsResolverOptions Defined in: [packages/core/src/remoteResolver.ts:59](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L59) Options for [dnsResolver](../functions/dnsResolver.md). ## Properties ### createTransport? > `readonly` `optional` **createTransport?**: (`baseUrl`) => `Transport` Defined in: [packages/core/src/remoteResolver.ts:68](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L68) Build a `Transport` from the resolved base URL. Defaults to a gRPC (HTTP/2) transport. #### Parameters ##### baseUrl `string` #### Returns `Transport` *** ### template > `readonly` **template**: `string` Defined in: [packages/core/src/remoteResolver.ts:66](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L66) URL template with `{shortName}` (alias `{name}`) placeholders. The short name is the last `typeName` segment, lower-cased, minus a trailing `Service` (e.g. `orders.v1.OrdersService` → `orders`). A k8s/DNS route is expressed directly, e.g. `"http://{shortName}.prod.svc.cluster.local:50051"`. --- --- 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/main/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/main/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/main/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/main/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/events/types/interfaces/EventAdapter.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventAdapter # Interface: EventAdapter Defined in: [packages/events/src/types.ts:112](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L112) Minimal adapter interface for message brokers. Inspired by Watermill (Go): minimal surface, broker-specific config in constructor, not in interface methods. ## Properties ### name > `readonly` **name**: `string` Defined in: [packages/events/src/types.ts:114](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L114) Adapter name for identification (e.g., "nats", "kafka", "redis", "memory") ## Methods ### connect() > **connect**(`context?`): `Promise`<`void`> Defined in: [packages/events/src/types.ts:123](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L123) Connect to the message broker. #### Parameters ##### context? [`AdapterContext`](AdapterContext.md) Optional adapter context with service-level information derived from proto service descriptors. Adapters may use `context.serviceName` for broker-level client identification. #### Returns `Promise`<`void`> *** ### disconnect() > **disconnect**(): `Promise`<`void`> Defined in: [packages/events/src/types.ts:126](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L126) Disconnect from the message broker #### Returns `Promise`<`void`> *** ### publish() > **publish**(`eventType`, `payload`, `options?`): `Promise`<`void`> Defined in: [packages/events/src/types.ts:129](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L129) Publish a serialized event to a topic #### Parameters ##### eventType `string` ##### payload `Uint8Array` ##### options? [`PublishOptions`](PublishOptions.md) #### Returns `Promise`<`void`> *** ### subscribe() > **subscribe**(`patterns`, `handler`, `options?`): `Promise`<[`EventSubscription`](EventSubscription.md)> Defined in: [packages/events/src/types.ts:132](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L132) Subscribe to event patterns with a raw handler #### Parameters ##### patterns `string`\[] ##### handler [`RawEventHandler`](../type-aliases/RawEventHandler.md) ##### options? [`RawSubscribeOptions`](RawSubscribeOptions.md) #### Returns `Promise`<[`EventSubscription`](EventSubscription.md)> --- --- url: /en/api/@connectum/events/types/interfaces/EventBus.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventBus # Interface: EventBus Defined in: [packages/events/src/types.ts:414](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L414) EventBus interface -- manages adapter, routes, and middleware ## Methods ### publish() > **publish**<`Desc`>(`schema`, `data`, `options?`): `Promise`<`void`> Defined in: [packages/events/src/types.ts:428](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L428) Publish a typed event #### Type Parameters ##### Desc `Desc` *extends* `DescMessage` #### Parameters ##### schema `Desc` ##### data `MessageShape`<`Desc`> ##### options? [`PublishOptions`](PublishOptions.md) #### Returns `Promise`<`void`> *** ### start() > **start**(`options?`): `Promise`<`void`> Defined in: [packages/events/src/types.ts:424](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L424) Start the event bus: connect adapter, set up subscriptions. An optional `signal` can be passed for graceful shutdown. If provided, it **overrides** the construction-time `EventBusOptions.signal`. The active signal is then composed with `AbortSignal.timeout(handlerTimeout)` via `AbortSignal.any()` for each event handler invocation, so either shutdown or per-event timeout will abort in-flight processing. #### Parameters ##### options? ###### signal? `AbortSignal` #### Returns `Promise`<`void`> *** ### stop() > **stop**(): `Promise`<`void`> Defined in: [packages/events/src/types.ts:426](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L426) Stop the event bus: drain subscriptions, disconnect adapter #### Returns `Promise`<`void`> --- --- url: /en/api/@connectum/core/types/interfaces/EventBusLike.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / EventBusLike # Interface: EventBusLike Defined in: [packages/core/src/types.ts:127](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L127) Minimal interface for event bus lifecycle integration with the server. Packages implementing event bus adapters (e.g., @connectum/events) must satisfy this interface to be used with `createServer({ eventBus })`. ## Methods ### start() > **start**(`options?`): `Promise`<`void`> Defined in: [packages/core/src/types.ts:134](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L134) Start the event bus (connect to broker, set up subscriptions). #### Parameters ##### options? Optional start parameters ###### signal? `AbortSignal` Abort signal from server for graceful shutdown #### Returns `Promise`<`void`> *** ### stop() > **stop**(): `Promise`<`void`> Defined in: [packages/core/src/types.ts:136](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L136) Stop the event bus (drain subscriptions, disconnect) #### Returns `Promise`<`void`> --- --- url: /en/api/@connectum/events/types/interfaces/EventBusOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventBusOptions # Interface: EventBusOptions Defined in: [packages/events/src/types.ts:324](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L324) EventBus configuration options for createEventBus() ## Properties ### adapter > **adapter**: [`EventAdapter`](EventAdapter.md) Defined in: [packages/events/src/types.ts:326](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L326) Adapter instance (e.g., NatsAdapter, KafkaAdapter, MemoryAdapter) *** ### drainPublishTimeout? > `optional` **drainPublishTimeout?**: `number` Defined in: [packages/events/src/types.ts:394](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L394) Opt-in symmetric publish drain during `stop()`: maximum time in milliseconds to wait for in-flight `publish()` promises (started BEFORE `stop()` was called) to settle, before the adapter is disconnected. Runs concurrently with the handler drain (`drainTimeout`) — shutdown waits for the slower of the two, not their sum. Tracked promises carry a no-op observer, so a publish that settles (even rejects) after the deadline never becomes an `unhandledRejection`; the caller's own `publish()` promise is unaffected (rejections still propagate to it). NOT covered: publishes issued from inside handlers (including the DLQ republish) — those are governed by the handler drain and its post-abort settle window; new `publish()` calls after `stop()` begins are rejected by the stopping gate (the relay-pattern design is tracked in https://github.com/Connectum-Framework/connectum/issues/212). Budget note: `createServer`'s `shutdown.timeout` bounds the transport phase, not the shutdown hooks that stop the bus — a large value here extends total process shutdown accordingly; size it below your orchestrator's kill grace period. Default: `undefined` — disabled: `stop()` behavior is unchanged and in-flight publishes race the adapter disconnect exactly as before. `0` (or negative) also disables waiting. Available since 1.3.0. *** ### drainTimeout? > `optional` **drainTimeout?**: `number` Defined in: [packages/events/src/types.ts:367](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L367) Maximum time in milliseconds to wait for in-flight event handlers to complete during shutdown. After this timeout, remaining handlers are force-aborted via AbortSignal. Default: 30000 (30 seconds). Set to 0 for immediate abort. *** ### group? > `optional` **group?**: `string` Defined in: [packages/events/src/types.ts:342](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L342) Consumer group name *** ### handlerTimeout? > `optional` **handlerTimeout?**: `number` Defined in: [packages/events/src/types.ts:359](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L359) Per-event handler timeout in milliseconds. Each event handler invocation gets an AbortSignal that fires after this duration. Default: 30000 (30 seconds). *** ### middleware? > `optional` **middleware?**: [`MiddlewareConfig`](MiddlewareConfig.md) Defined in: [packages/events/src/types.ts:344](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L344) Middleware configuration *** ### publishes? > `optional` **publishes?**: `DescService`\[] Defined in: [packages/events/src/types.ts:340](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L340) Event service descriptors this bus publishes to (publish-only, no subscription). A process that only PUBLISHES events has no `routes`, so its publish-topic lookup would be empty and `publish()` would fall back to the message `typeName` — silently emitting to the wrong topic whenever the event declares a custom `(connectum.events.v1.event).topic`. List the event service descriptors here to populate the publish-topic lookup from their proto options, so the declared topic is used end-to-end without hand-maintaining raw topic strings. Subscribers still register via `routes`. *** ### routes? > `optional` **routes?**: [`EventRoute`](../type-aliases/EventRoute.md)\[] Defined in: [packages/events/src/types.ts:328](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L328) Event routes to register *** ### signal? > `optional` **signal?**: `AbortSignal` Defined in: [packages/events/src/types.ts:352](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L352) Abort signal for graceful shutdown. When provided, per-event signals are composed via `AbortSignal.any()` so that server shutdown aborts in-flight event processing. Automatically set when used with `createServer({ eventBus })`. *** ### strictTopics? > `optional` **strictTopics?**: `boolean` Defined in: [packages/events/src/types.ts:408](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L408) Reject a `publish()` whose topic cannot be resolved instead of silently falling back to the message `typeName`. By default, when no explicit `publishOptions.topic` is given and the event type is covered by neither `routes` nor `publishes`, `publish()` emits to the raw `schema.typeName` — a silent misconfiguration (the event may never reach subscribers expecting the proto-declared `(event).topic`). With `strictTopics: true`, that case throws so the misconfiguration surfaces at the call site. Default: `false` (backward-compatible silent fallback). --- --- url: /en/api/@connectum/events/types/interfaces/EventContext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventContext # Interface: EventContext Defined in: [packages/events/src/types.ts:161](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L161) Per-event context with explicit ack/nack control. Passed to event handlers alongside the deserialized message. Supports explicit ack/nack control. If the handler completes without calling either, the event is automatically acknowledged. ## Properties ### attempt > `readonly` **attempt**: `number` Defined in: [packages/events/src/types.ts:171](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L171) Delivery attempt number (1-based) *** ### eventId > `readonly` **eventId**: `string` Defined in: [packages/events/src/types.ts:165](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L165) Unique event identifier *** ### eventType > `readonly` **eventType**: `string` Defined in: [packages/events/src/types.ts:167](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L167) Event type / topic name *** ### metadata > `readonly` **metadata**: `ReadonlyMap`<`string`, `string`> Defined in: [packages/events/src/types.ts:173](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L173) Event metadata (headers) *** ### publishedAt > `readonly` **publishedAt**: `Date` Defined in: [packages/events/src/types.ts:169](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L169) When the event was published *** ### signal > `readonly` **signal**: `AbortSignal` Defined in: [packages/events/src/types.ts:163](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L163) Abort signal (aborted when server is shutting down) ## Methods ### ack() > **ack**(): `Promise`<`void`> Defined in: [packages/events/src/types.ts:175](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L175) Acknowledge successful processing #### Returns `Promise`<`void`> *** ### nack() > **nack**(`requeue?`): `Promise`<`void`> Defined in: [packages/events/src/types.ts:177](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L177) Negative acknowledge -- request redelivery or send to DLQ #### Parameters ##### requeue? `boolean` #### Returns `Promise`<`void`> --- --- url: /en/api/@connectum/events/types/interfaces/EventContextInit.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventContextInit # Interface: EventContextInit Defined in: [packages/events/src/types.ts:183](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L183) Initialization data for creating an EventContext ## Properties ### onAck > `readonly` **onAck**: () => `Promise`<`void`> Defined in: [packages/events/src/types.ts:186](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L186) #### Returns `Promise`<`void`> *** ### onNack > `readonly` **onNack**: (`requeue`) => `Promise`<`void`> Defined in: [packages/events/src/types.ts:187](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L187) #### Parameters ##### requeue `boolean` #### Returns `Promise`<`void`> *** ### raw > `readonly` **raw**: [`RawEvent`](RawEvent.md) Defined in: [packages/events/src/types.ts:184](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L184) *** ### signal > `readonly` **signal**: `AbortSignal` Defined in: [packages/events/src/types.ts:185](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L185) --- --- url: /en/api/@connectum/events/types/interfaces/EventHandlerConfig.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventHandlerConfig # Interface: EventHandlerConfig\ Defined in: [packages/events/src/types.ts:206](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L206) Per-handler middleware configuration. Overrides global EventBus middleware for this specific handler. When present, the global middleware pipeline is bypassed entirely and only the per-handler middleware array is applied. ## Type Parameters ### I `I` ## Properties ### handler > `readonly` **handler**: [`TypedEventHandler`](../type-aliases/TypedEventHandler.md)<`I`> Defined in: [packages/events/src/types.ts:208](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L208) Event handler function *** ### middleware? > `readonly` `optional` **middleware?**: [`EventMiddleware`](../type-aliases/EventMiddleware.md)\[] Defined in: [packages/events/src/types.ts:210](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L210) Per-handler middleware array (overrides global middleware for this handler) --- --- url: /en/api/@connectum/events/types/interfaces/EventRouteEntry.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventRouteEntry # Interface: EventRouteEntry Defined in: [packages/events/src/types.ts:227](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L227) Registered event route (internal use) ## Properties ### handler > `readonly` **handler**: [`TypedEventHandler`](../type-aliases/TypedEventHandler.md)<`unknown`> Defined in: [packages/events/src/types.ts:233](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L233) Typed handler function *** ### method > `readonly` **method**: `DescMethod` Defined in: [packages/events/src/types.ts:231](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L231) Method descriptor for deserialization *** ### middleware? > `readonly` `optional` **middleware?**: [`EventMiddleware`](../type-aliases/EventMiddleware.md)\[] Defined in: [packages/events/src/types.ts:235](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L235) Per-handler middleware (overrides global when present) *** ### topic > `readonly` **topic**: `string` Defined in: [packages/events/src/types.ts:229](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L229) Topic pattern to subscribe to --- --- url: /en/api/@connectum/events/types/interfaces/EventRouter.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventRouter # Interface: EventRouter Defined in: [packages/events/src/types.ts:244](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L244) Event router for registering service event handlers. Mirrors ConnectRPC's ConnectRouter pattern: `events.service(UserEventHandlers, { ... })` mirrors `router.service(UserService, { ... })` ## Methods ### service() > **service**<`S`>(`serviceDesc`, `handlers`): `void` Defined in: [packages/events/src/types.ts:246](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L246) Register event handlers for a service #### Type Parameters ##### S `S` *extends* `DescService` #### Parameters ##### serviceDesc `S` ##### handlers [`ServiceEventHandlers`](../type-aliases/ServiceEventHandlers.md)<`S`> #### Returns `void` --- --- url: /en/api/@connectum/events/types/interfaces/EventSubscription.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventSubscription # Interface: EventSubscription Defined in: [packages/events/src/types.ts:43](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L43) Subscription handle returned by adapter.subscribe() ## Methods ### unsubscribe() > **unsubscribe**(): `Promise`<`void`> Defined in: [packages/events/src/types.ts:45](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L45) Unsubscribe and clean up #### Returns `Promise`<`void`> --- --- url: /en/api/@connectum/events-amqp/testing/interfaces/FakeAmqpAdapterInstance.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [testing](../index.md) / FakeAmqpAdapterInstance # Interface: FakeAmqpAdapterInstance Defined in: [packages/events-amqp/src/testing.ts:144](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L144) The fake adapter: a drop-in EventAdapter plus its [FakeAmqpControl](FakeAmqpControl.md). ## Extends * `EventAdapter` ## Properties ### control > `readonly` **control**: [`FakeAmqpControl`](FakeAmqpControl.md) Defined in: [packages/events-amqp/src/testing.ts:145](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L145) *** ### name > `readonly` **name**: `string` Defined in: packages/events/dist/index.d.ts:105 Adapter name for identification (e.g., "nats", "kafka", "redis", "memory") #### Inherited from `EventAdapter.name` ## Methods ### connect() > **connect**(`context?`): `Promise`<`void`> Defined in: packages/events/dist/index.d.ts:113 Connect to the message broker. #### Parameters ##### context? `AdapterContext` Optional adapter context with service-level information derived from proto service descriptors. Adapters may use `context.serviceName` for broker-level client identification. #### Returns `Promise`<`void`> #### Inherited from `EventAdapter.connect` *** ### disconnect() > **disconnect**(): `Promise`<`void`> Defined in: packages/events/dist/index.d.ts:115 Disconnect from the message broker #### Returns `Promise`<`void`> #### Inherited from `EventAdapter.disconnect` *** ### publish() > **publish**(`eventType`, `payload`, `options?`): `Promise`<`void`> Defined in: packages/events/dist/index.d.ts:117 Publish a serialized event to a topic #### Parameters ##### eventType `string` ##### payload `Uint8Array` ##### options? `PublishOptions` #### Returns `Promise`<`void`> #### Inherited from `EventAdapter.publish` *** ### subscribe() > **subscribe**(`patterns`, `handler`, `options?`): `Promise`<`EventSubscription`> Defined in: packages/events/dist/index.d.ts:119 Subscribe to event patterns with a raw handler #### Parameters ##### patterns `string`\[] ##### handler `RawEventHandler` ##### options? `RawSubscribeOptions` #### Returns `Promise`<`EventSubscription`> #### Inherited from `EventAdapter.subscribe` --- --- url: /en/api/@connectum/events-amqp/testing/interfaces/FakeAmqpAdapterOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [testing](../index.md) / FakeAmqpAdapterOptions # Interface: FakeAmqpAdapterOptions Defined in: [packages/events-amqp/src/testing.ts:53](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L53) Options for [FakeAmqpAdapter](../functions/FakeAmqpAdapter.md). ## Properties ### failFastOnInitialSetupError? > `readonly` `optional` **failFastOnInitialSetupError?**: `boolean` Defined in: [packages/events-amqp/src/testing.ts:60](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L60) Mirror of the real option: a topology `failSetup(...)` queued before `connect()` rejects it with the typed error instead of report-and-proceed. *** ### lifecycle? > `readonly` `optional` **lifecycle?**: [`AmqpLifecycleCallbacks`](../../types/interfaces/AmqpLifecycleCallbacks.md) Defined in: [packages/events-amqp/src/testing.ts:55](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L55) The same lifecycle surface as the real adapter (union + flat shim). --- --- url: /en/api/@connectum/events-amqp/testing/interfaces/FakeAmqpControl.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [testing](../index.md) / FakeAmqpControl # Interface: FakeAmqpControl Defined in: [packages/events-amqp/src/testing.ts:88](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L88) Deterministic control surface of the fake. ## Properties ### published > `readonly` **published**: readonly [`FakePublishedRecord`](FakePublishedRecord.md)\[] Defined in: [packages/events-amqp/src/testing.ts:130](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L130) Successfully acked publishes, in order. ## Methods ### block() > **block**(`reason?`): `void` Defined in: [packages/events-amqp/src/testing.ts:120](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L120) Broker flow control: `blocked { reason }` / `unblocked` (union-only events). #### Parameters ##### reason? `string` #### Returns `void` *** ### completeRecovery() > **completeRecovery**(): `void` Defined in: [packages/events-amqp/src/testing.ts:111](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L111) Advance a pending recovery: consumes a queued `failSetup` (reported per its gating, stays recovering) or, with nothing queued, completes with `connected { reconnected: true }` and settles parked subscribes. #### Returns `void` *** ### deliver() > **deliver**(`eventType`, `payload`, `options?`): `Promise`<[`FakeDeliveryResult`](FakeDeliveryResult.md)> Defined in: [packages/events-amqp/src/testing.ts:140](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L140) Deliver an event to matching subscriptions (NATS-style wildcard matching, one consumer per distinct group — competing-consumer parity; requires the connected state, like a real broker). Internal envelope keys (`x-event-id`, `x-published-at`, `x-connectum-publish-id`) are honored and stripped from handler-visible metadata, mirroring the real consumer. Resolves with the settlement summary once every handler settles; handler rejections are swallowed (counted in `failed`). #### Parameters ##### eventType `string` ##### payload `Uint8Array` ##### options? ###### attempt? `number` ###### metadata? `Record`<`string`, `string`> #### Returns `Promise`<[`FakeDeliveryResult`](FakeDeliveryResult.md)> *** ### dropConnection() > **dropConnection**(`error?`): `void` Defined in: [packages/events-amqp/src/testing.ts:105](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L105) Sever the connection: dispatches `disconnected { error }` then `reconnecting { attempt: 1, delay: 0 }` and parks in the recovering state — publishes fail fast with `AmqpConnectionError`, exactly like the real adapter's recovery window; new `subscribe()` calls PARK. #### Parameters ##### error? `Error` #### Returns `void` *** ### exhaustRecovery() > **exhaustRecovery**(`error?`): `void` Defined in: [packages/events-amqp/src/testing.ts:118](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L118) Terminal outcome: `reconnect-failed { error }`. The dead adapter fails publishes fast, rejects parked subscribes typed, and deactivates all subscriptions (the cycle died — so did its consumers). Reconnect requires `disconnect()` first, like the real retries-exhausted state. #### Parameters ##### error? `Error` #### Returns `void` *** ### failSetup() > **failSetup**(`error?`, `object?`): `void` Defined in: [packages/events-amqp/src/testing.ts:98](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L98) Queue a setup failure. An `AmqpTopologyError` (the default) follows the real gating: `setup-failed { initial: true, attempt: 0 }` at `connect()` (typed rejection under `failFastOnInitialSetupError`), or `setup-failed { initial: false, attempt }` at the next [completeRecovery](#completerecovery). A NON-topology error follows the real gating too: no `setup-failed` event — at `connect()` it is consumed silently, at `completeRecovery` it only schedules the next `reconnecting`. #### Parameters ##### error? `Error` ##### object? [`AmqpTopologyObject`](../../type-aliases/AmqpTopologyObject.md) #### Returns `void` *** ### nextPublish() > **nextPublish**(...`outcomes`): `void` Defined in: [packages/events-amqp/src/testing.ts:128](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L128) Queue FIFO outcomes for upcoming `publish()` calls. An empty queue means `"ack"`. Use the real error classes (`AmqpPublishNackError`, `AmqpConnectionError`, `AmqpPublishTimeoutError` — the state-UNKNOWN outcome only this fake reproduces deterministically, …). #### Parameters ##### outcomes ...[`FakePublishOutcome`](../type-aliases/FakePublishOutcome.md)\[] #### Returns `void` *** ### unblock() > **unblock**(): `void` Defined in: [packages/events-amqp/src/testing.ts:121](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L121) #### Returns `void` --- --- url: /en/api/@connectum/events-amqp/testing/interfaces/FakeDeliveryResult.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [testing](../index.md) / FakeDeliveryResult # Interface: FakeDeliveryResult Defined in: [packages/events-amqp/src/testing.ts:74](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L74) Settlement summary of one [FakeAmqpControl.deliver](FakeAmqpControl.md#deliver) call. ## Properties ### acked > `readonly` **acked**: `number` Defined in: [packages/events-amqp/src/testing.ts:78](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L78) Handlers that called `ack()`. *** ### delivered > `readonly` **delivered**: `number` Defined in: [packages/events-amqp/src/testing.ts:76](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L76) Handlers invoked (one per matching fan-out sub + one per distinct group). *** ### failed > `readonly` **failed**: `number` Defined in: [packages/events-amqp/src/testing.ts:84](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L84) Handlers that rejected (swallowed, like the real consumer's nack-on-error path). *** ### nacked > `readonly` **nacked**: `number` Defined in: [packages/events-amqp/src/testing.ts:80](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L80) Handlers that called `nack(false)`. *** ### requeued > `readonly` **requeued**: `number` Defined in: [packages/events-amqp/src/testing.ts:82](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L82) Handlers that called `nack(true)` — model redelivery by delivering again with `attempt + 1`. --- --- url: /en/api/@connectum/test-fixtures/types/interfaces/FakeMethodOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [types](../index.md) / FakeMethodOptions # Interface: FakeMethodOptions Defined in: [types.ts:102](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L102) Options for createFakeMethod. ## Properties ### methodKind? > `optional` **methodKind?**: `"unary"` | `"client_streaming"` | `"server_streaming"` | `"bidi_streaming"` Defined in: [types.ts:104](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L104) Method kind. Default: `'unary'` *** ### register? > `optional` **register?**: `boolean` Defined in: [types.ts:106](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L106) Whether to register the method in service.methods. Default: `false` --- --- url: /en/api/@connectum/testing/index/interfaces/FakeMethodOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / FakeMethodOptions # Interface: FakeMethodOptions Defined in: test-fixtures/dist/types.d.ts:75 Options for [createFakeMethod](../functions/createFakeMethod.md). ## Properties ### methodKind? > `optional` **methodKind?**: `"unary"` | `"client_streaming"` | `"server_streaming"` | `"bidi_streaming"` Defined in: test-fixtures/dist/types.d.ts:77 Method kind. Default: `'unary'` *** ### register? > `optional` **register?**: `boolean` Defined in: test-fixtures/dist/types.d.ts:79 Whether to register the method in service.methods. Default: `false` --- --- url: /en/api/@connectum/events-amqp/testing/interfaces/FakePublishedRecord.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [testing](../index.md) / FakePublishedRecord # Interface: FakePublishedRecord Defined in: [packages/events-amqp/src/testing.ts:67](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L67) A recorded successful publish, as the adapter received it from the bus. ## Properties ### eventType > `readonly` **eventType**: `string` Defined in: [packages/events-amqp/src/testing.ts:68](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L68) *** ### options? > `readonly` `optional` **options?**: `PublishOptions` Defined in: [packages/events-amqp/src/testing.ts:70](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L70) *** ### payload > `readonly` **payload**: `Uint8Array` Defined in: [packages/events-amqp/src/testing.ts:69](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L69) --- --- url: /en/api/@connectum/test-fixtures/types/interfaces/FakeServiceOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [types](../index.md) / FakeServiceOptions # Interface: FakeServiceOptions Defined in: [types.ts:94](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L94) Options for createFakeService. ## Properties ### name? > `optional` **name?**: `string` Defined in: [types.ts:98](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L98) Service name (short). Default: derived from typeName *** ### typeName? > `optional` **typeName?**: `string` Defined in: [types.ts:96](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L96) Service type name. Default: `'test.v1.TestService'` --- --- url: /en/api/@connectum/testing/index/interfaces/FakeServiceOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / FakeServiceOptions # Interface: FakeServiceOptions Defined in: test-fixtures/dist/types.d.ts:68 Options for [createFakeService](../functions/createFakeService.md). ## Properties ### name? > `optional` **name?**: `string` Defined in: test-fixtures/dist/types.d.ts:72 Service name (short). Default: derived from typeName *** ### typeName? > `optional` **typeName?**: `string` Defined in: test-fixtures/dist/types.d.ts:70 Service type name. Default: `'test.v1.TestService'` --- --- url: /en/api/@connectum/interceptors/interfaces/FallbackOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / FallbackOptions # Interface: FallbackOptions\ Defined in: [types.ts:219](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L219) Fallback interceptor options ## Type Parameters ### T `T` = `unknown` ## Properties ### handler > **handler**: (`error`) => `T` | `Promise`<`T`> Defined in: [types.ts:223](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L223) Fallback function to call on error #### Parameters ##### error `Error` #### Returns `T` | `Promise`<`T`> *** ### skipStreaming? > `optional` **skipStreaming?**: `boolean` Defined in: [types.ts:229](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L229) 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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/packages/auth/src/types.ts#L290) Header containing credential type --- --- url: >- /en/api/@connectum/healthcheck/@connectum/healthcheck/types/interfaces/HealthcheckOptions.md --- [Connectum API Reference](../../../../../../index.md) / [@connectum/healthcheck](../../../../index.md) / [@connectum/healthcheck/types](../index.md) / HealthcheckOptions # Interface: HealthcheckOptions Defined in: [types.ts:27](https://github.com/Connectum-Framework/connectum/blob/main/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/main/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/main/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/main/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/main/packages/healthcheck/src/types.ts#L44) Watch interval in milliseconds for streaming health updates #### Default ```ts 500 ``` --- --- url: /en/api/@connectum/auth/interfaces/InternalAuthInterceptorOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / InternalAuthInterceptorOptions # Interface: InternalAuthInterceptorOptions Defined in: [packages/auth/src/types.ts:341](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L341) Options for [createInternalAuthInterceptor](../functions/createInternalAuthInterceptor.md). ## Properties ### internalMethods > `readonly` **internalMethods**: readonly `string`\[] Defined in: [packages/auth/src/types.ts:359](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L359) Method patterns that are internal (service-to-service). Typically the output of `getInternalMethods(services)`. The interceptor enforces the trust marker only on these methods; all other methods pass through unchanged (no-op). Patterns: `"Service/Method"`, `"Service/*"`, or `"*"`. *** ### trustSource > `readonly` **trustSource**: [`InternalTrustSource`](../type-aliases/InternalTrustSource.md) Defined in: [packages/auth/src/types.ts:350](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L350) The trust source that authorizes an internal call. Use one of the provided factories — [meshIdentityTrust](../functions/meshIdentityTrust.md) (production default, per-service via the mesh), [signedTokenTrust](../functions/signedTokenTrust.md) (non-mesh, per-service JWT/JWKS with mandatory issuer-bound key selection), or [sharedSecretTrust](../functions/sharedSecretTrust.md) (dev-only fallback) — or supply a custom one. --- --- 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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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 **RSA public key** ```typescript const rsaKey = await crypto.subtle.importKey( "spki", pemToArrayBuffer(rsaPem), { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, true, ["verify"], ); ``` **EC public key** ```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/main/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/main/packages/auth/src/types.ts#L233) Methods to skip authentication for. #### Default ```ts [] ``` --- --- url: /en/api/@connectum/events-kafka/types/interfaces/KafkaAdapterOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-kafka](../../index.md) / [types](../index.md) / KafkaAdapterOptions # Interface: KafkaAdapterOptions Defined in: [types.ts:12](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-kafka/src/types.ts#L12) Options for creating a KafkaAdapter instance. ## Properties ### brokers > `readonly` **brokers**: `string`\[] Defined in: [types.ts:14](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-kafka/src/types.ts#L14) Kafka broker addresses (e.g., \["localhost:9092"]) *** ### clientId? > `readonly` `optional` **clientId?**: `string` Defined in: [types.ts:17](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-kafka/src/types.ts#L17) Client ID for this producer/consumer (default: "connectum") *** ### consumerOptions? > `readonly` `optional` **consumerOptions?**: `object` Defined in: [types.ts:32](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-kafka/src/types.ts#L32) Consumer-specific options #### allowAutoTopicCreation? > `readonly` `optional` **allowAutoTopicCreation?**: `boolean` Whether Kafka should auto-create topics on subscribe (default: false) #### fromBeginning? > `readonly` `optional` **fromBeginning?**: `boolean` Whether to start consuming from the beginning of topics (default: false) #### sessionTimeout? > `readonly` `optional` **sessionTimeout?**: `number` Session timeout in milliseconds (default: 30000) *** ### kafkaConfig? > `readonly` `optional` **kafkaConfig?**: `Omit`<`Partial`<`KafkaConfig`>, `"brokers"` | `"clientId"`> Defined in: [types.ts:23](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-kafka/src/types.ts#L23) Additional KafkaJS configuration overrides. Merged with brokers and clientId. *** ### producerOptions? > `readonly` `optional` **producerOptions?**: `object` Defined in: [types.ts:26](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-kafka/src/types.ts#L26) Producer-specific options #### compression? > `readonly` `optional` **compression?**: `CompressionTypes` Compression type for produced messages --- --- 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/main/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/main/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/main/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/main/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/main/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/main/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/main/packages/interceptors/src/types.ts#L45) Logger interceptor options ## Properties ### level? > `optional` **level?**: `"error"` | `"warn"` | `"debug"` | `"info"` Defined in: [types.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/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/main/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/main/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/main/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/main/packages/otel/src/logger.ts#L8) --- --- url: /en/api/@connectum/auth/interfaces/MeshIdentityEntry.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / MeshIdentityEntry # Interface: MeshIdentityEntry Defined in: [packages/auth/src/types.ts:366](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L366) An allow-list entry for [meshIdentityTrust](../functions/meshIdentityTrust.md), mapping a verified mesh identity (the forwarded peer principal) to its authorization context. ## Properties ### name? > `readonly` `optional` **name?**: `string` Defined in: [packages/auth/src/types.ts:377](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L377) Optional human-readable name for the calling service. *** ### principal > `readonly` **principal**: `string` Defined in: [packages/auth/src/types.ts:371](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L371) The mesh-forwarded peer identity to match, e.g. an Istio short-form ServiceAccount principal `cluster.local/ns//sa/` or a SPIFFE id. *** ### roles? > `readonly` `optional` **roles?**: readonly `string`\[] Defined in: [packages/auth/src/types.ts:373](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L373) Roles granted to this caller (compose via `requires {roles}`). *** ### scopes? > `readonly` `optional` **scopes?**: readonly `string`\[] Defined in: [packages/auth/src/types.ts:375](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L375) Scopes granted to this caller (compose via `requires {scopes}`). --- --- url: /en/api/@connectum/auth/interfaces/MeshIdentityTrustOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / MeshIdentityTrustOptions # Interface: MeshIdentityTrustOptions Defined in: [packages/auth/src/types.ts:383](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L383) Options for [meshIdentityTrust](../functions/meshIdentityTrust.md). ## Properties ### allowlist > `readonly` **allowlist**: readonly [`MeshIdentityEntry`](MeshIdentityEntry.md)\[] Defined in: [packages/auth/src/types.ts:389](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L389) Allow-list of permitted mesh identities. Each entry maps a forwarded peer principal to its roles/scopes. A request whose identity is not on the list is rejected. *** ### header? > `readonly` `optional` **header?**: `string` Defined in: [packages/auth/src/types.ts:394](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L394) Header carrying the mesh-forwarded peer identity. #### Default ```ts "x-forwarded-client-principal" ``` *** ### type? > `readonly` `optional` **type?**: `string` Defined in: [packages/auth/src/types.ts:396](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L396) Credential type set on the resulting AuthContext. #### Default ```ts "mesh" ``` --- --- 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:22 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. ## Since 1.3.0 ## Methods ### addBatchObservableCallback() > **addBatchObservableCallback**<`AttributesTypes`>(`callback`, `observables`): `void` Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:102 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:42 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:28 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:34 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:78 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:69 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:87 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:60 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.1/node\_modules/@opentelemetry/api/build/src/metrics/Meter.d.ts:112 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/events/types/interfaces/MiddlewareConfig.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / MiddlewareConfig # Interface: MiddlewareConfig Defined in: [packages/events/src/types.ts:312](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L312) Built-in middleware configuration ## Properties ### custom? > `optional` **custom?**: [`EventMiddleware`](../type-aliases/EventMiddleware.md)\[] Defined in: [packages/events/src/types.ts:318](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L318) Custom user middleware (executed outermost) *** ### dlq? > `optional` **dlq?**: [`DlqOptions`](DlqOptions.md) Defined in: [packages/events/src/types.ts:316](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L316) Dead letter queue configuration *** ### retry? > `optional` **retry?**: [`RetryOptions`](RetryOptions.md) Defined in: [packages/events/src/types.ts:314](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L314) Retry configuration --- --- url: /en/api/@connectum/test-fixtures/index/interfaces/MockCall.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / MockCall # Interface: MockCall\ Defined in: [mock-compat.ts:15](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-compat.ts#L15) A single recorded invocation of a [MockFn](MockFn.md). ## Type Parameters ### Args `Args` *extends* readonly `unknown`\[] = readonly `unknown`\[] ## Properties ### arguments > `readonly` **arguments**: `Args` Defined in: [mock-compat.ts:17](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-compat.ts#L17) The arguments passed to the mock function. --- --- url: /en/api/@connectum/testing/index/interfaces/MockCall.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockCall # Interface: MockCall\ Defined in: test-fixtures/dist/index.d.ts:115 A single recorded invocation of a [MockFn](MockFn.md). ## Type Parameters ### Args `Args` *extends* readonly `unknown`\[] = readonly `unknown`\[] ## Properties ### arguments > `readonly` **arguments**: `Args` Defined in: test-fixtures/dist/index.d.ts:117 The arguments passed to the mock function. --- --- url: /en/api/@connectum/test-fixtures/types/interfaces/MockDescFieldOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [types](../index.md) / MockDescFieldOptions # Interface: MockDescFieldOptions Defined in: [types.ts:58](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L58) Options for createMockDescField. ## Properties ### fieldNumber? > `optional` **fieldNumber?**: `number` Defined in: [types.ts:62](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L62) Proto field number. Default: auto-incremented *** ### isSensitive? > `optional` **isSensitive?**: `boolean` Defined in: [types.ts:60](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L60) Mark field as sensitive (for redact interceptor). Default: `false` *** ### type? > `optional` **type?**: `string` Defined in: [types.ts:64](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L64) Field scalar type. Default: `'string'` --- --- url: /en/api/@connectum/testing/index/interfaces/MockDescFieldOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockDescFieldOptions # Interface: MockDescFieldOptions Defined in: test-fixtures/dist/types.d.ts:43 Options for [createMockDescField](../functions/createMockDescField.md). ## Properties ### fieldNumber? > `optional` **fieldNumber?**: `number` Defined in: test-fixtures/dist/types.d.ts:47 Proto field number. Default: auto-incremented *** ### isSensitive? > `optional` **isSensitive?**: `boolean` Defined in: test-fixtures/dist/types.d.ts:45 Mark field as sensitive (for redact interceptor). Default: `false` *** ### type? > `optional` **type?**: `string` Defined in: test-fixtures/dist/types.d.ts:49 Field scalar type. Default: `'string'` --- --- url: /en/api/@connectum/test-fixtures/types/interfaces/MockDescMessageOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [types](../index.md) / MockDescMessageOptions # Interface: MockDescMessageOptions Defined in: [types.ts:46](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L46) Options for createMockDescMessage. ## Properties ### fields? > `optional` **fields?**: `object`\[] Defined in: [types.ts:48](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L48) Field definitions. Default: `[]` #### fieldNumber? > `optional` **fieldNumber?**: `number` #### name > **name**: `string` #### type? > `optional` **type?**: `string` *** ### oneofs? > `optional` **oneofs?**: `string`\[] Defined in: [types.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L54) Oneof group names. Default: `[]` --- --- url: /en/api/@connectum/testing/index/interfaces/MockDescMessageOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockDescMessageOptions # Interface: MockDescMessageOptions Defined in: test-fixtures/dist/types.d.ts:32 Options for [createMockDescMessage](../functions/createMockDescMessage.md). ## Properties ### fields? > `optional` **fields?**: `object`\[] Defined in: test-fixtures/dist/types.d.ts:34 Field definitions. Default: `[]` #### fieldNumber? > `optional` **fieldNumber?**: `number` #### name > **name**: `string` #### type? > `optional` **type?**: `string` *** ### oneofs? > `optional` **oneofs?**: `string`\[] Defined in: test-fixtures/dist/types.d.ts:40 Oneof group names. Default: `[]` --- --- url: /en/api/@connectum/test-fixtures/types/interfaces/MockDescMethodOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [types](../index.md) / MockDescMethodOptions # Interface: MockDescMethodOptions Defined in: [types.ts:68](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L68) Options for createMockDescMethod. ## Properties ### input? > `optional` **input?**: `DescMessage` Defined in: [types.ts:70](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L70) Input message descriptor. *** ### kind? > `optional` **kind?**: `"unary"` | `"client_streaming"` | `"server_streaming"` | `"bidi_streaming"` Defined in: [types.ts:74](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L74) Method kind. Default: `'unary'` *** ### output? > `optional` **output?**: `DescMessage` Defined in: [types.ts:72](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L72) Output message descriptor. *** ### useSensitiveRedaction? > `optional` **useSensitiveRedaction?**: `boolean` Defined in: [types.ts:76](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L76) Enable sensitive field redaction for this method. Default: `false` --- --- url: /en/api/@connectum/testing/index/interfaces/MockDescMethodOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockDescMethodOptions # Interface: MockDescMethodOptions Defined in: test-fixtures/dist/types.d.ts:52 Options for [createMockDescMethod](../functions/createMockDescMethod.md). ## Properties ### input? > `optional` **input?**: `DescMessage` Defined in: test-fixtures/dist/types.d.ts:54 Input message descriptor. *** ### kind? > `optional` **kind?**: `"unary"` | `"client_streaming"` | `"server_streaming"` | `"bidi_streaming"` Defined in: test-fixtures/dist/types.d.ts:58 Method kind. Default: `'unary'` *** ### output? > `optional` **output?**: `DescMessage` Defined in: test-fixtures/dist/types.d.ts:56 Output message descriptor. *** ### useSensitiveRedaction? > `optional` **useSensitiveRedaction?**: `boolean` Defined in: test-fixtures/dist/types.d.ts:60 Enable sensitive field redaction for this method. Default: `false` --- --- url: /en/api/@connectum/test-fixtures/index/interfaces/MockFn.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [index](../index.md) / MockFn # Interface: MockFn()\ Defined in: [mock-compat.ts:27](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-compat.ts#L27) A callable spy that records every invocation. The shape intentionally mirrors the subset of `node:test` `mock.fn()` that Connectum testing utilities rely on. ## Type Parameters ### F `F` *extends* (...`args`) => `any` > **MockFn**(...`args`): `ReturnType`<`F`> Defined in: [mock-compat.ts:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-compat.ts#L28) A callable spy that records every invocation. The shape intentionally mirrors the subset of `node:test` `mock.fn()` that Connectum testing utilities rely on. ## Parameters ### args ...`Parameters`<`F`> ## Returns `ReturnType`<`F`> ## Properties ### mock > `readonly` **mock**: `object` Defined in: [mock-compat.ts:30](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/mock-compat.ts#L30) Spy metadata. #### calls > `readonly` **calls**: readonly [`MockCall`](MockCall.md)<`Parameters`<`F`>>\[] Ordered list of recorded calls. #### callCount() > **callCount**(): `number` Returns the total number of recorded calls. ##### Returns `number` --- --- url: /en/api/@connectum/testing/index/interfaces/MockFn.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockFn # Interface: MockFn()\ Defined in: test-fixtures/dist/index.d.ts:125 A callable spy that records every invocation. The shape intentionally mirrors the subset of `node:test` `mock.fn()` that Connectum testing utilities rely on. ## Type Parameters ### F `F` *extends* (...`args`) => `any` > **MockFn**(...`args`): `ReturnType`<`F`> Defined in: test-fixtures/dist/index.d.ts:126 A callable spy that records every invocation. The shape intentionally mirrors the subset of `node:test` `mock.fn()` that Connectum testing utilities rely on. ## Parameters ### args ...`Parameters`<`F`> ## Returns `ReturnType`<`F`> ## Properties ### mock > `readonly` **mock**: `object` Defined in: test-fixtures/dist/index.d.ts:128 Spy metadata. #### calls > `readonly` **calls**: readonly [`MockCall`](MockCall.md)<`Parameters`<`F`>>\[] Ordered list of recorded calls. #### callCount() > **callCount**(): `number` Returns the total number of recorded calls. ##### Returns `number` --- --- url: /en/api/@connectum/test-fixtures/types/interfaces/MockNextOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [types](../index.md) / MockNextOptions # Interface: MockNextOptions Defined in: [types.ts:34](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L34) Options for createMockNext and createMockNextSlow. ## Properties ### message? > `optional` **message?**: `unknown` Defined in: [types.ts:36](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L36) Response message. Default: `{ result: 'success' }` *** ### stream? > `optional` **stream?**: `boolean` Defined in: [types.ts:38](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L38) Streaming response flag. Default: `false` --- --- url: /en/api/@connectum/testing/index/interfaces/MockNextOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockNextOptions # Interface: MockNextOptions Defined in: test-fixtures/dist/types.d.ts:25 Options for [createMockNext](../functions/createMockNext.md) and [createMockNextSlow](../functions/createMockNextSlow.md). ## Properties ### message? > `optional` **message?**: `unknown` Defined in: test-fixtures/dist/types.d.ts:27 Response message. Default: `{ result: 'success' }` *** ### stream? > `optional` **stream?**: `boolean` Defined in: test-fixtures/dist/types.d.ts:29 Streaming response flag. Default: `false` --- --- url: /en/api/@connectum/test-fixtures/types/interfaces/MockRequestOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [types](../index.md) / MockRequestOptions # Interface: MockRequestOptions Defined in: [types.ts:14](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L14) Options for createMockRequest. ## Properties ### headers? > `optional` **headers?**: `Headers` Defined in: [types.ts:26](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L26) Request headers. Default: `new Headers()` *** ### message? > `optional` **message?**: `unknown` Defined in: [types.ts:20](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L20) Request message payload. Default: `{}` *** ### method? > `optional` **method?**: `string` Defined in: [types.ts:18](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L18) Method name. Default: `'TestMethod'` *** ### service? > `optional` **service?**: `string` Defined in: [types.ts:16](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L16) Service type name. Default: `'test.TestService'` *** ### stream? > `optional` **stream?**: `boolean` Defined in: [types.ts:22](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L22) Streaming request flag. Default: `false` *** ### url? > `optional` **url?**: `string` Defined in: [types.ts:24](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L24) Request URL. Auto-generated from service/method if omitted. --- --- url: /en/api/@connectum/testing/index/interfaces/MockRequestOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockRequestOptions # Interface: MockRequestOptions Defined in: test-fixtures/dist/types.d.ts:10 Options for [createMockRequest](../functions/createMockRequest.md). ## Properties ### headers? > `optional` **headers?**: `Headers` Defined in: test-fixtures/dist/types.d.ts:22 Request headers. Default: `new Headers()` *** ### message? > `optional` **message?**: `unknown` Defined in: test-fixtures/dist/types.d.ts:16 Request message payload. Default: `{}` *** ### method? > `optional` **method?**: `string` Defined in: test-fixtures/dist/types.d.ts:14 Method name. Default: `'TestMethod'` *** ### service? > `optional` **service?**: `string` Defined in: test-fixtures/dist/types.d.ts:12 Service type name. Default: `'test.TestService'` *** ### stream? > `optional` **stream?**: `boolean` Defined in: test-fixtures/dist/types.d.ts:18 Streaming request flag. Default: `false` *** ### url? > `optional` **url?**: `string` Defined in: test-fixtures/dist/types.d.ts:20 Request URL. Auto-generated from service/method if omitted. --- --- url: /en/api/@connectum/testing/index/interfaces/MockService.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockService # Interface: MockService Defined in: [testing/src/mockResolver.ts:22](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockResolver.ts#L22) A mocked service: its proto descriptor paired with a (partial) implementation. ## Properties ### impl > `readonly` **impl**: `Partial`<`ServiceImpl`<`DescService`>> Defined in: [testing/src/mockResolver.ts:24](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockResolver.ts#L24) *** ### service > `readonly` **service**: `DescService` Defined in: [testing/src/mockResolver.ts:23](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockResolver.ts#L23) --- --- url: /en/api/@connectum/test-fixtures/types/interfaces/MockStreamOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/test-fixtures](../../index.md) / [types](../index.md) / MockStreamOptions # Interface: MockStreamOptions Defined in: [types.ts:84](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L84) Options for createMockStream. ## Properties ### delayMs? > `optional` **delayMs?**: `number` Defined in: [types.ts:86](https://github.com/Connectum-Framework/connectum/blob/main/packages/test-fixtures/src/types.ts#L86) Delay in milliseconds between yielded items. --- --- url: /en/api/@connectum/testing/index/interfaces/MockStreamOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MockStreamOptions # Interface: MockStreamOptions Defined in: test-fixtures/dist/types.d.ts:63 Options for [createMockStream](../functions/createMockStream.md). ## Properties ### delayMs? > `optional` **delayMs?**: `number` Defined in: test-fixtures/dist/types.d.ts:65 Delay in milliseconds between yielded items. --- --- url: /en/api/@connectum/events-nats/types/interfaces/NatsAdapterOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-nats](../../index.md) / [types](../index.md) / NatsAdapterOptions # Interface: NatsAdapterOptions Defined in: [types.ts:12](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L12) Options for creating a NATS JetStream adapter. ## Properties ### connectionOptions? > `readonly` `optional` **connectionOptions?**: `Partial`<`NodeConnectionOptions`> Defined in: [types.ts:32](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L32) NATS connection options (escape hatch for advanced config). The `servers` field from this object is overridden by the top-level `servers` option. *** ### consumerOptions? > `readonly` `optional` **consumerOptions?**: [`NatsConsumerOptions`](NatsConsumerOptions.md) Defined in: [types.ts:35](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L35) JetStream consumer tuning options. *** ### servers > `readonly` **servers**: `string` | `string`\[] Defined in: [types.ts:14](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L14) NATS server URL(s). Accepts a single string or an array. *** ### stream? > `readonly` `optional` **stream?**: `string` Defined in: [types.ts:24](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L24) JetStream stream name. When set, subjects are prefixed with `{stream}.` and the stream is auto-created on `connect()` if it does not exist. #### Default ```ts "events" ``` --- --- url: /en/api/@connectum/events-nats/types/interfaces/NatsConsumerOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-nats](../../index.md) / [types](../index.md) / NatsConsumerOptions # Interface: NatsConsumerOptions Defined in: [types.ts:41](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L41) Options for JetStream consumer behaviour. ## Properties ### ackWait? > `readonly` `optional` **ackWait?**: `number` Defined in: [types.ts:58](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L58) Ack wait timeout in milliseconds. After this period an unacknowledged message is redelivered. #### Default ```ts 30_000 ``` *** ### deliverPolicy? > `readonly` `optional` **deliverPolicy?**: `"new"` | `"all"` | `"last"` Defined in: [types.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L50) Deliver policy for new consumers. * `"new"` — only messages published after consumer creation * `"all"` — all available messages * `"last"` — last message per subject #### Default ```ts "new" ``` *** ### maxDeliver? > `readonly` `optional` **maxDeliver?**: `number` Defined in: [types.ts:66](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-nats/src/types.ts#L66) Maximum number of delivery attempts before the message is discarded by the server. #### Default ```ts 5 ``` --- --- url: /en/api/@connectum/testing/index/interfaces/NormalizedMetric.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / NormalizedMetric # Interface: NormalizedMetric Defined in: [testing/src/otel-collectors.ts:48](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L48) Structural representation of a single metric data point. ## Properties ### description > **description**: `string` Defined in: [testing/src/otel-collectors.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L50) *** ### name > **name**: `string` Defined in: [testing/src/otel-collectors.ts:49](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L49) *** ### points > **points**: `object`\[] Defined in: [testing/src/otel-collectors.ts:53](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L53) #### attributes > **attributes**: `Record`<`string`, `unknown`> #### value > **value**: `unknown` *** ### type > **type**: `string` Defined in: [testing/src/otel-collectors.ts:52](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L52) *** ### unit > **unit**: `string` Defined in: [testing/src/otel-collectors.ts:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L51) --- --- url: /en/api/@connectum/testing/index/interfaces/NormalizedSpan.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / NormalizedSpan # Interface: NormalizedSpan Defined in: [testing/src/otel-collectors.ts:34](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L34) Structural, transport-agnostic representation of a span suitable for `deepEqual`. ## Properties ### attributes > **attributes**: `Record`<`string`, `unknown`> Defined in: [testing/src/otel-collectors.ts:37](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L37) *** ### events > **events**: `object`\[] Defined in: [testing/src/otel-collectors.ts:38](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L38) #### attributes > **attributes**: `Record`<`string`, `unknown`> #### name > **name**: `string` *** ### kind > **kind**: `number` Defined in: [testing/src/otel-collectors.ts:36](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L36) *** ### name > **name**: `string` Defined in: [testing/src/otel-collectors.ts:35](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L35) *** ### parentSpanId > **parentSpanId**: `string` | `undefined` Defined in: [testing/src/otel-collectors.ts:42](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L42) *** ### spanId > **spanId**: `string` Defined in: [testing/src/otel-collectors.ts:41](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L41) *** ### status > **status**: `object` Defined in: [testing/src/otel-collectors.ts:39](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L39) #### code > **code**: `number` #### message > **message**: `string` | `undefined` *** ### traceId > **traceId**: `string` Defined in: [testing/src/otel-collectors.ts:40](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L40) --- --- 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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/packages/otel/src/config.ts#L32) --- --- url: /en/api/@connectum/core/interfaces/PerServiceEnvResolverOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / PerServiceEnvResolverOptions # Interface: PerServiceEnvResolverOptions Defined in: [packages/core/src/remoteResolver.ts:87](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L87) Options for [perServiceEnvResolver](../functions/perServiceEnvResolver.md). ## Properties ### createTransport? > `readonly` `optional` **createTransport?**: (`baseUrl`) => `Transport` Defined in: [packages/core/src/remoteResolver.ts:89](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L89) Build a `Transport` from the resolved base URL. Defaults to a gRPC (HTTP/2) transport. #### Parameters ##### baseUrl `string` #### Returns `Transport` --- --- 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:555](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L555) 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:574](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L574) 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:560](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L560) 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:565](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L565) 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:52](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L52) 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:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L54) 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:85](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L85) 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:93](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L93) Optional HTTP handler for fallback routing (e.g., /healthz endpoint) *** ### name > `readonly` **name**: `string` Defined in: [packages/core/src/types.ts:87](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L87) Protocol name for identification (e.g., "healthcheck", "reflection") ## Methods ### register() > **register**(`router`, `context`): `void` Defined in: [packages/core/src/types.ts:90](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L90) 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/main/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/main/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/main/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/main/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/main/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:35](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L35) Options for initializing the OpenTelemetry provider ## Properties ### instanceId? > `optional` **instanceId?**: `string` Defined in: [packages/otel/src/provider.ts:45](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L45) Sets `service.instance.id` on the resource (OTel semconv). Lets a fleet of same-role processes be told apart in telemetry. Takes precedence over the `OTEL_SERVICE_INSTANCE_ID` env var. *** ### resourceAttributes? > `optional` **resourceAttributes?**: `Record`<`string`, `string` | `number` | `boolean`> Defined in: [packages/otel/src/provider.ts:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L51) Extra resource attributes merged into the resource (e.g. `device.id`, `facility`). Applied to traces, metrics, and logs alike. Takes precedence over attributes parsed from the `OTEL_RESOURCE_ATTRIBUTES` env var. *** ### serviceName? > `optional` **serviceName?**: `string` Defined in: [packages/otel/src/provider.ts:37](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L37) Override service name (defaults to OTEL\_SERVICE\_NAME or npm\_package\_name) *** ### serviceVersion? > `optional` **serviceVersion?**: `string` Defined in: [packages/otel/src/provider.ts:39](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L39) Override service version (defaults to npm\_package\_version) *** ### settings? > `optional` **settings?**: `Partial`<[`OTLPSettings`](../../interfaces/OTLPSettings.md)> Defined in: [packages/otel/src/provider.ts:53](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L53) Override OTLP exporter settings (defaults to env-based config) --- --- url: /en/api/@connectum/events/types/interfaces/PublishOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / PublishOptions # Interface: PublishOptions Defined in: [packages/events/src/types.ts:59](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L59) Options for publishing events ## Properties ### group? > `optional` **group?**: `string` Defined in: [packages/events/src/types.ts:63](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L63) Named group tag for workflow grouping *** ### key? > `optional` **key?**: `string` Defined in: [packages/events/src/types.ts:67](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L67) Message key for partitioning (Kafka: partition key, others: ignored) *** ### messageId? > `optional` **messageId?**: `string` Defined in: [packages/events/src/types.ts:75](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L75) Caller-supplied message identifier the adapter sets on the wire where supported (AMQP: the `messageId` property; other adapters ignore it). Primarily for external-contract publishing — in `@connectum/events-amqp` `externalContract` mode the adapter does not auto-generate a `messageId`, so set this when the contract requires one. *** ### metadata? > `optional` **metadata?**: `Record`<`string`, `string`> Defined in: [packages/events/src/types.ts:65](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L65) Additional metadata / headers *** ### timestamp? > `optional` **timestamp?**: `number` Defined in: [packages/events/src/types.ts:82](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L82) Caller-supplied message timestamp in **Unix epoch seconds**, set on the wire where supported (AMQP: the `timestamp` property; other adapters ignore it). Like [messageId](#messageid), mainly for external-contract publishing where the adapter does not auto-populate it. *** ### topic? > `optional` **topic?**: `string` Defined in: [packages/events/src/types.ts:61](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L61) Override topic name (default: schema.typeName) --- --- url: /en/api/@connectum/events/types/interfaces/RawEvent.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / RawEvent # Interface: RawEvent Defined in: [packages/events/src/types.ts:16](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L16) Raw event data delivered by the adapter ## Properties ### attempt > `readonly` **attempt**: `number` Defined in: [packages/events/src/types.ts:26](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L26) Delivery attempt number (1-based) *** ### eventId > `readonly` **eventId**: `string` Defined in: [packages/events/src/types.ts:18](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L18) Unique event identifier *** ### eventType > `readonly` **eventType**: `string` Defined in: [packages/events/src/types.ts:20](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L20) Event type / topic name *** ### metadata > `readonly` **metadata**: `ReadonlyMap`<`string`, `string`> Defined in: [packages/events/src/types.ts:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L28) Event metadata (headers) *** ### payload > `readonly` **payload**: `Uint8Array` Defined in: [packages/events/src/types.ts:22](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L22) Serialized protobuf payload *** ### publishedAt > `readonly` **publishedAt**: `Date` Defined in: [packages/events/src/types.ts:24](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L24) When the event was published --- --- url: /en/api/@connectum/events/types/interfaces/RawSubscribeOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / RawSubscribeOptions # Interface: RawSubscribeOptions Defined in: [packages/events/src/types.ts:51](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L51) Options for raw subscribe ## Properties ### group? > `optional` **group?**: `string` Defined in: [packages/events/src/types.ts:53](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L53) Consumer group name for load-balanced consumption --- --- url: /en/api/@connectum/events-redis/types/interfaces/RedisAdapterOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-redis](../../index.md) / [types](../index.md) / RedisAdapterOptions # Interface: RedisAdapterOptions Defined in: [types.ts:12](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/types.ts#L12) Options for creating a Redis Streams adapter. ## Properties ### brokerOptions? > `readonly` `optional` **brokerOptions?**: [`RedisBrokerOptions`](RedisBrokerOptions.md) Defined in: [types.ts:32](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/types.ts#L32) Broker-specific tuning for Redis Streams consumption. *** ### redisOptions? > `readonly` `optional` **redisOptions?**: `RedisOptions` Defined in: [types.ts:27](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/types.ts#L27) Redis connection options (alternative to `url`). Passed directly to `new Redis(redisOptions)`. When `url` is also set, these options are merged as the second argument. *** ### url? > `readonly` `optional` **url?**: `string` Defined in: [types.ts:19](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/types.ts#L19) Redis connection URL (e.g., "redis://localhost:6379"). Takes precedence over `redisOptions.host` / `redisOptions.port` when both are provided. --- --- url: /en/api/@connectum/events-redis/types/interfaces/RedisBrokerOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-redis](../../index.md) / [types](../index.md) / RedisBrokerOptions # Interface: RedisBrokerOptions Defined in: [types.ts:38](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/types.ts#L38) Redis Streams broker tuning options. ## Properties ### blockMs? > `readonly` `optional` **blockMs?**: `number` Defined in: [types.ts:56](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/types.ts#L56) Block timeout in milliseconds for XREADGROUP. How long the consumer blocks waiting for new messages before retrying the loop. #### Default ```ts 5000 ``` *** ### count? > `readonly` `optional` **count?**: `number` Defined in: [types.ts:63](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/types.ts#L63) Number of messages to read per XREADGROUP call. #### Default ```ts 10 ``` *** ### maxLen? > `readonly` `optional` **maxLen?**: `number` Defined in: [types.ts:46](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-redis/src/types.ts#L46) Maximum stream length (MAXLEN approximate for XADD). When set, older entries are trimmed on publish. #### Default ```ts undefined (no limit) ``` --- --- 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/main/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/main/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/main/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/main/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/main/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 ### internal > `readonly` **internal**: `boolean` Defined in: [packages/auth/src/proto/reader.ts:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/proto/reader.ts#L28) Whether the method is internal (service-to-service): skip end-user (JWT) authentication, but require an internal trust marker established by [createInternalAuthInterceptor](../functions/createInternalAuthInterceptor.md). Distinct from `public`. See ADR-029. *** ### policy > `readonly` **policy**: `"allow"` | `"deny"` | `undefined` Defined in: [packages/auth/src/proto/reader.ts:30](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/proto/reader.ts#L30) 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/main/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:32](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/proto/reader.ts#L32) Required roles and scopes, or undefined if none specified. --- --- url: /en/api/@connectum/core/interfaces/ResolverContext.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ResolverContext # Interface: ResolverContext Defined in: [packages/core/src/remoteResolver.ts:29](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L29) Context handed to a [RemoteResolver](../type-aliases/RemoteResolver.md) for a single resolution. ## Properties ### endpoint? > `readonly` `optional` **endpoint?**: `string` Defined in: [packages/core/src/remoteResolver.ts:33](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L33) Opaque endpoint hint from `CallOptions.endpoint` (polymorphic deployments). *** ### typeName > `readonly` **typeName**: `string` Defined in: [packages/core/src/remoteResolver.ts:31](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L31) Proto service `typeName`, e.g. `"orders.v1.OrdersService"`. --- --- url: /en/api/@connectum/otel/provider/interfaces/ResourceAttributeInputs.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [provider](../index.md) / ResourceAttributeInputs # Interface: ResourceAttributeInputs Defined in: [packages/otel/src/provider.ts:82](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L82) Inputs for [buildResourceAttributes](../functions/buildResourceAttributes.md). ## Properties ### env? > `optional` **env?**: `object` Defined in: [packages/otel/src/provider.ts:88](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L88) Environment source (defaults to `process.env`). #### OTEL\_RESOURCE\_ATTRIBUTES? > `optional` **OTEL\_RESOURCE\_ATTRIBUTES?**: `string` #### OTEL\_SERVICE\_INSTANCE\_ID? > `optional` **OTEL\_SERVICE\_INSTANCE\_ID?**: `string` *** ### instanceId? > `optional` **instanceId?**: `string` Defined in: [packages/otel/src/provider.ts:85](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L85) *** ### resourceAttributes? > `optional` **resourceAttributes?**: `Record`<`string`, `string` | `number` | `boolean`> Defined in: [packages/otel/src/provider.ts:86](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L86) *** ### serviceName > **serviceName**: `string` Defined in: [packages/otel/src/provider.ts:83](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L83) *** ### serviceVersion > **serviceVersion**: `string` Defined in: [packages/otel/src/provider.ts:84](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/provider.ts#L84) --- --- url: /en/api/@connectum/events/types/interfaces/RetryOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / RetryOptions # Interface: RetryOptions Defined in: [packages/events/src/types.ts:279](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L279) Retry middleware options ## Properties ### backoff? > `optional` **backoff?**: `"fixed"` | `"exponential"` | `"linear"` Defined in: [packages/events/src/types.ts:283](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L283) Backoff strategy *** ### initialDelay? > `optional` **initialDelay?**: `number` Defined in: [packages/events/src/types.ts:285](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L285) Initial delay in ms (default: 1000) *** ### maxDelay? > `optional` **maxDelay?**: `number` Defined in: [packages/events/src/types.ts:287](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L287) Maximum delay in ms (default: 30000) *** ### maxRetries? > `optional` **maxRetries?**: `number` Defined in: [packages/events/src/types.ts:281](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L281) Maximum retry attempts (default: 3) *** ### multiplier? > `optional` **multiplier?**: `number` Defined in: [packages/events/src/types.ts:289](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L289) Multiplier for exponential backoff (default: 2) *** ### retryableErrors? > `optional` **retryableErrors?**: (`error`) => `boolean` Defined in: [packages/events/src/types.ts:291](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L291) Filter: only retry for these error types #### Parameters ##### error `unknown` #### Returns `boolean` --- --- 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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/packages/otel/src/metrics.ts#L25) Histogram measuring size of RPC server response messages (unit: bytes) --- --- url: /en/api/@connectum/auth/testing/interfaces/RsaTestKeypair.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / RsaTestKeypair # Interface: RsaTestKeypair Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:53](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L53) A generated RSA test keypair plus the public JWK to publish at a JWKS endpoint. ## Properties ### kid > `readonly` **kid**: `string` Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:61](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L61) Key id shared by `publicJwk` and the token header (load-bearing for JWKS key selection). *** ### privateKey > `readonly` **privateKey**: `CryptoKey` Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:55](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L55) Private signing key — pass to [createTestJwtRS256](../functions/createTestJwtRS256.md). *** ### publicJwk > `readonly` **publicJwk**: `JWK` Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:59](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L59) Public JWK (carries `kid`, `alg: "RS256"`, `use: "sig"`) — serve at the JWKS endpoint. *** ### publicKey > `readonly` **publicKey**: `CryptoKey` Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:57](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L57) Public verification key. --- --- 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/main/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/main/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/main/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/main/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/main/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/main/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/main/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:424](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L424) 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:452](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L452) Current server address Returns null until server is started *** ### eventBus > `readonly` **eventBus**: [`EventBusLike`](EventBusLike.md) | `null` Defined in: [packages/core/src/types.ts:592](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L592) Event bus instance, if configured Returns null if no event bus was provided to createServer(). *** ### interceptors > `readonly` **interceptors**: readonly `Interceptor`\[] Defined in: [packages/core/src/types.ts:580](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L580) Registered interceptors *** ### isRunning > `readonly` **isRunning**: `boolean` Defined in: [packages/core/src/types.ts:457](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L457) Whether server is currently running *** ### protocols > `readonly` **protocols**: readonly [`ProtocolRegistration`](ProtocolRegistration.md)\[] Defined in: [packages/core/src/types.ts:585](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L585) Registered protocols *** ### routes > `readonly` **routes**: readonly [`ServiceDefinition`](../../interfaces/ServiceDefinition.md)\[] Defined in: [packages/core/src/types.ts:575](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L575) Registered service routes *** ### shutdownSignal > `readonly` **shutdownSignal**: `AbortSignal` Defined in: [packages/core/src/types.ts:559](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L559) 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:462](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L462) Current server state *** ### transport > `readonly` **transport**: [`TransportServer`](../type-aliases/TransportServer.md) | `null` Defined in: [packages/core/src/types.ts:570](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L570) 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.9.3/node\_modules/@types/node/events.d.ts:87 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, event, ...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:511](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L511) 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.9.3/node\_modules/@types/node/events.d.ts:92 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:518](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L518) 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:504](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L504) Add a service route at runtime #### Parameters ##### service [`ServiceDefinition`](../../interfaces/ServiceDefinition.md) #### Returns `void` #### Throws Error if server is already running *** ### client() > **client**<`T`>(`service`, `options?`): `Client`<`T`> Defined in: [packages/core/src/types.ts:659](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L659) Unified client factory: auto-routes to the in-process transport if the service is registered on this `Server`, otherwise to the transport supplied by the configured `remoteResolver` (e.g. a `createGrpcTransport({ baseUrl })` to a remote peer). An optional `options.endpoint` hint is forwarded to the resolver. Fail-fast (split error model): a non-local service with no `remoteResolver` configured is a configuration mistake → throws [CatalogConfigError](../../classes/CatalogConfigError.md) at the `server.client(...)` call. A resolver that returns `null` is an operational miss → `ConnectError(Code.Unavailable)`. Enables polyglot deployments where the same call site (`server.client(S)`) routes locally in a modular monolith and remotely when the service is split into a separate process — without code changes. #### Type Parameters ##### T `T` *extends* `DescService` #### Parameters ##### service `T` ##### options? [`ServerClientOptions`](ServerClientOptions.md) #### Returns `Client`<`T`> #### Example ```typescript // Configure the resolver once; the same call works whether GreeterService // is co-located or remote: const server = createServer({ services: [...], remoteResolver }); const client = server.client(GreeterService); await client.sayHello({ name: 'world' }); ``` *** ### emit() > **emit**<`E`>(`eventName`, ...`args`): `boolean` Defined in: node\_modules/.pnpm/@types+node@25.9.3/node\_modules/@types/node/events.d.ts:134 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.9.3/node\_modules/@types/node/events.d.ts:154 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.9.3/node\_modules/@types/node/events.d.ts:161 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` *** ### hasService() > **hasService**(`desc`): `boolean` Defined in: [packages/core/src/types.ts:632](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L632) Synchronous registry lookup: returns whether the given proto service descriptor is served locally by this `Server`. Triggers route materialization on first call. Source of truth is the same `ConnectRouter.service(desc, impl)` chain used to build the HTTP handler — no separate registration step. #### Parameters ##### desc `DescService` #### Returns `boolean` #### Example ```typescript if (server.hasService(GreeterService)) { // routed in-process } ``` *** ### listenerCount() > **listenerCount**<`E`>(`eventName`, `listener?`): `number` Defined in: node\_modules/.pnpm/@types+node@25.9.3/node\_modules/@types/node/events.d.ts:170 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 `string` | `symbol` The name of the event being listened for ##### 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.9.3/node\_modules/@types/node/events.d.ts:186 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` *** ### localClient() > **localClient**<`T`>(`service`): `Client`<`T`> Defined in: [packages/core/src/types.ts:615](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L615) Create a fully-typed ConnectRPC client that dispatches calls directly to handlers registered on this server, without opening any TCP socket. Safe to call before `server.start()` — the routes are materialized lazily on first access. Once materialized, `addService` / `addInterceptor` / `addProtocol` will throw. #### Type Parameters ##### T `T` *extends* `DescService` #### Parameters ##### service `T` #### Returns `Client`<`T`> #### Example ```typescript import { GreeterService } from './gen/greeter_pb.js'; const server = createServer({ services: [routes] }); const client = server.localClient(GreeterService); const response = await client.sayHello({ name: 'world' }); ``` *** ### off() #### Call Signature > **off**(`event`, `listener`): `this` Defined in: [packages/core/src/types.ts:489](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L489) 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:490](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L490) 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:491](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L491) 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:492](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L492) 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:493](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L493) 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:471](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L471) 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:472](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L472) 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:473](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L473) 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:474](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L474) 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:475](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L475) 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:480](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L480) 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:481](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L481) 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:482](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L482) 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:483](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L483) 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:484](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L484) 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:530](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L530) 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:539](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L539) 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:551](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L551) 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.9.3/node\_modules/@types/node/events.d.ts:275 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 `string` | `symbol` The name of the event. ##### 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.9.3/node\_modules/@types/node/events.d.ts:292 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 `string` | `symbol` The name of the event. ##### 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.9.3/node\_modules/@types/node/events.d.ts:326 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.9.3/node\_modules/@types/node/events.d.ts:338 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.9.3/node\_modules/@types/node/events.d.ts:425 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.9.3/node\_modules/@types/node/events.d.ts:436 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:434](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L434) 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:441](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L441) Stop the server gracefully #### Returns `Promise`<`void`> #### Throws Error if server is not in RUNNING state --- --- url: /en/api/@connectum/core/types/interfaces/ServerClientOptions.md --- [Connectum API Reference](../../../../index.md) / [@connectum/core](../../index.md) / [types](../index.md) / ServerClientOptions # Interface: ServerClientOptions Defined in: [packages/core/src/types.ts:665](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L665) Options for [Server.client](Server.md#client). ## Properties ### endpoint? > `optional` **endpoint?**: `string` Defined in: [packages/core/src/types.ts:671](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L671) Opaque endpoint hint forwarded to the configured `remoteResolver` when the requested service is not mounted locally (polymorphic deployments — one proto served at several endpoints). Ignored for locally-mounted services. --- --- url: /en/api/@connectum/core/interfaces/ServiceDefinition.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ServiceDefinition # Interface: ServiceDefinition Defined in: [packages/core/src/defineService.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/defineService.ts#L50) A service ready to be mounted: its proto descriptor plus a `register` closure that wires the handlers onto a `ConnectRouter`. Produced by [defineService](../functions/defineService.md) and [defineLazyService](../functions/defineLazyService.md); consumed by `createServer({ services })`. ## Properties ### descriptor > `readonly` **descriptor**: `DescService` Defined in: [packages/core/src/defineService.ts:52](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/defineService.ts#L52) The proto service descriptor (carries `typeName` and `file`). *** ### register > `readonly` **register**: (`router`, `ctx`) => `void` Defined in: [packages/core/src/defineService.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/defineService.ts#L54) **`Internal`** Mounts the service's handlers on the given router. #### Parameters ##### router `ConnectRouter` ##### ctx `RegisterContext` #### Returns `void` --- --- url: >- /en/api/@connectum/healthcheck/@connectum/healthcheck/types/interfaces/ServiceStatus.md --- [Connectum API Reference](../../../../../../index.md) / [@connectum/healthcheck](../../../../index.md) / [@connectum/healthcheck/types](../index.md) / ServiceStatus # Interface: ServiceStatus Defined in: [types.ts:20](https://github.com/Connectum-Framework/connectum/blob/main/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/main/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:483](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L483) 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:506](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L506) LRU cache for session verification results *** ### extractToken? > `readonly` `optional` **extractToken?**: (`req`) => `string` | `Promise`<`string` | `null`> | `null` Defined in: [packages/auth/src/types.ts:504](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L504) 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:499](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L499) 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:516](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L516) 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:510](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L510) Propagate auth context as headers for downstream services *** ### skipMethods? > `readonly` `optional` **skipMethods?**: `string`\[] Defined in: [packages/auth/src/types.ts:508](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L508) Methods to skip authentication for *** ### verifySession > `readonly` **verifySession**: (`token`, `headers`) => `unknown` Defined in: [packages/auth/src/types.ts:492](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L492) 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/auth/interfaces/SharedSecretTrustOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / SharedSecretTrustOptions # Interface: SharedSecretTrustOptions Defined in: [packages/auth/src/types.ts:457](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L457) Options for [sharedSecretTrust](../functions/sharedSecretTrust.md). DEV-ONLY: a single shared secret is NOT per-service — one compromise forges all callers. Use [meshIdentityTrust](../functions/meshIdentityTrust.md) or [signedTokenTrust](../functions/signedTokenTrust.md) in production. See ADR-029. ## Properties ### header? > `readonly` `optional` **header?**: `string` Defined in: [packages/auth/src/types.ts:464](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L464) Header carrying the shared secret. #### Default ```ts "x-internal-secret" ``` *** ### roles? > `readonly` `optional` **roles?**: readonly `string`\[] Defined in: [packages/auth/src/types.ts:471](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L471) Roles granted to a trusted caller. *** ### scopes? > `readonly` `optional` **scopes?**: readonly `string`\[] Defined in: [packages/auth/src/types.ts:473](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L473) Scopes granted to a trusted caller. *** ### secret > `readonly` **secret**: `string` Defined in: [packages/auth/src/types.ts:459](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L459) The shared secret, constant-time compared against the header value. *** ### subject? > `readonly` `optional` **subject?**: `string` Defined in: [packages/auth/src/types.ts:469](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L469) Subject identity assigned to a trusted call. #### Default ```ts "internal" ``` *** ### type? > `readonly` `optional` **type?**: `string` Defined in: [packages/auth/src/types.ts:475](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L475) Credential type set on the resulting AuthContext. #### Default ```ts "internal" ``` --- --- 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:184](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L184) Graceful shutdown options ## Properties ### autoShutdown? > `optional` **autoShutdown?**: `boolean` Defined in: [packages/core/src/types.ts:201](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L201) Enable automatic graceful shutdown on signals #### Default ```ts false ``` *** ### forceCloseOnTimeout? > `optional` **forceCloseOnTimeout?**: `boolean` Defined in: [packages/core/src/types.ts:209](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L209) 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:195](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L195) Signals to listen for graceful shutdown #### Default ```ts ["SIGTERM", "SIGINT"] ``` *** ### timeout? > `optional` **timeout?**: `number` Defined in: [packages/core/src/types.ts:189](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L189) Timeout in milliseconds for graceful shutdown #### Default ```ts 30000 ``` --- --- url: /en/api/@connectum/auth/interfaces/SignedTokenIssuer.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / SignedTokenIssuer # Interface: SignedTokenIssuer Defined in: [packages/auth/src/types.ts:407](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L407) Per-issuer JWKS configuration for [signedTokenTrust](../functions/signedTokenTrust.md). The JWKS lookup is issuer-bound: the keyset is selected by the token's `iss` claim and verification is pinned to that same issuer. This is a hard security requirement — a single shared JWKS holding multiple services' keys does NOT contain compromise (jose resolves the key by `kid` independently of `iss`). ## Properties ### algorithms? > `readonly` `optional` **algorithms?**: `string`\[] Defined in: [packages/auth/src/types.ts:413](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L413) Allowed signing algorithms. #### Default ```ts ["RS256"] ``` *** ### audience? > `readonly` `optional` **audience?**: `string` | `string`\[] Defined in: [packages/auth/src/types.ts:411](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L411) Expected audience(s) for tokens from this issuer. *** ### claimsMapping? > `readonly` `optional` **claimsMapping?**: `object` Defined in: [packages/auth/src/types.ts:420](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L420) Mapping from token claims to AuthContext fields (dot-notation paths). `subject` defaults to `sub ?? iss`; `roles`/`scopes` to none unless mapped. #### name? > `readonly` `optional` **name?**: `string` #### roles? > `readonly` `optional` **roles?**: `string` #### scopes? > `readonly` `optional` **scopes?**: `string` #### subject? > `readonly` `optional` **subject?**: `string` *** ### jwksUri > `readonly` **jwksUri**: `string` Defined in: [packages/auth/src/types.ts:409](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L409) The issuer's JWKS endpoint URL (its own keyset only). *** ### maxTokenAge? > `readonly` `optional` **maxTokenAge?**: `string` | `number` Defined in: [packages/auth/src/types.ts:415](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L415) Maximum token age (seconds or string like "2h"). --- --- url: /en/api/@connectum/auth/interfaces/SignedTokenTrustOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / SignedTokenTrustOptions # Interface: SignedTokenTrustOptions Defined in: [packages/auth/src/types.ts:433](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L433) Options for [signedTokenTrust](../functions/signedTokenTrust.md). ## Properties ### header? > `readonly` `optional` **header?**: `string` Defined in: [packages/auth/src/types.ts:445](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L445) Header carrying the service token. The value may be a bare token or a `Bearer ` value. #### Default ```ts "x-internal-token" ``` *** ### issuers > `readonly` **issuers**: `Readonly`<`Record`<`string`, [`SignedTokenIssuer`](SignedTokenIssuer.md)>> Defined in: [packages/auth/src/types.ts:439](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L439) Per-issuer configuration keyed by the issuer (`iss`) value. The keyset is selected by the token's claimed issuer and verification is pinned to that same issuer — never a single shared keyset across issuers. *** ### type? > `readonly` `optional` **type?**: `string` Defined in: [packages/auth/src/types.ts:447](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L447) Credential type set on the resulting AuthContext. #### Default ```ts "service" ``` --- --- url: /en/api/@connectum/core/interfaces/StreamingMethodInfo.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / StreamingMethodInfo # Interface: StreamingMethodInfo Defined in: [packages/core/src/TransportValidation.ts:76](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L76) A streaming method that requires HTTP/2. ## Properties ### kind > `readonly` **kind**: `string` Defined in: [packages/core/src/TransportValidation.ts:82](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L82) Streaming kind: `bidi_streaming`. *** ### method > `readonly` **method**: `string` Defined in: [packages/core/src/TransportValidation.ts:80](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L80) Method name (e.g. `StreamCodes`). *** ### service > `readonly` **service**: `string` Defined in: [packages/core/src/TransportValidation.ts:78](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L78) Fully qualified service typeName (e.g. `acme.v1.ScannerService`). --- --- url: /en/api/@connectum/auth/testing/interfaces/TestJwksServer.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / TestJwksServer # Interface: TestJwksServer Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:80](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L80) A running in-process JWKS server. ## Properties ### origin > `readonly` **origin**: `string` Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:84](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L84) Origin (no path), e.g. `http://127.0.0.1:`. *** ### url > `readonly` **url**: `string` Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:82](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L82) The JWKS URL — pass as `jwksUri` to `createJwtAuthInterceptor`. ## Methods ### close() > **close**(): `Promise`<`void`> Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:86](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L86) Stop the server. Call after the test (e.g. in `after`). #### Returns `Promise`<`void`> --- --- url: /en/api/@connectum/testing/types/interfaces/TestServer.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [types](../index.md) / TestServer # Interface: TestServer Defined in: [testing/src/types.ts:26](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L26) A running test server with transport and cleanup. ## Properties ### baseUrl > **baseUrl**: `string` Defined in: [testing/src/types.ts:30](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L30) Server base URL (e.g. `http://localhost:54321`). *** ### port > **port**: `number` Defined in: [testing/src/types.ts:32](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L32) Assigned port number. *** ### transport > **transport**: `Transport` Defined in: [testing/src/types.ts:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L28) Pre-configured client transport connected to the test server. ## Methods ### close() > **close**(): `Promise`<`void`> Defined in: [testing/src/types.ts:34](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/types.ts#L34) Stop the server and close all connections. #### Returns `Promise`<`void`> --- --- url: /en/api/@connectum/interceptors/interfaces/TimeoutOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/interceptors](../index.md) / TimeoutOptions # Interface: TimeoutOptions Defined in: [types.ts:179](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L179) Timeout interceptor options ## Properties ### duration? > `optional` **duration?**: `number` Defined in: [types.ts:184](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L184) Request timeout in milliseconds #### Default ```ts 30000 (30 seconds) ``` *** ### skipStreaming? > `optional` **skipStreaming?**: `boolean` Defined in: [types.ts:190](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L190) 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:99](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L99) TLS configuration options ## Properties ### certPath? > `optional` **certPath?**: `string` Defined in: [packages/core/src/types.ts:108](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L108) Path to TLS certificate file *** ### dirPath? > `optional` **dirPath?**: `string` Defined in: [packages/core/src/types.ts:114](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L114) 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:103](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L103) 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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/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.1/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:9 Tracer provides an interface for creating [Span](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.Span.html)s. ## Since 1.0.0 ## Methods ### startActiveSpan() #### Call Signature > **startActiveSpan**<`F`>(`name`, `fn`): `ReturnType`<`F`> Defined in: node\_modules/.pnpm/@opentelemetry+api@1.9.1/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:69 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.1/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:70 ##### 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.1/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:71 ##### 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.1/node\_modules/@opentelemetry/api/build/src/trace/tracer.d.ts:24 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 Istio manifests for a multi-service deployment are available in the [car-sharing/istio](https://github.com/Connectum-Framework/examples/tree/main/car-sharing/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 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/car-sharing/k8s/namespace.yaml) for the full manifest. After applying, every new pod in the 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/car-sharing/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), override the namespace-wide policy with a second `PeerAuthentication` that sets `PERMISSIVE` mode and a `selector` targeting that specific service. The car-sharing example enforces namespace-wide `STRICT` mTLS in [peer-authentication.yaml](https://github.com/Connectum-Framework/examples/blob/main/car-sharing/istio/peer-authentication.yaml); add a `PERMISSIVE` override only for services that must accept plaintext. ## 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/car-sharing/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/car-sharing/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/car-sharing/istio/canary-virtual-service.yaml) for the full manifest. Deploy the canary with a different version label: create a single-replica canary pod labeled `version: canary` running the release candidate image, alongside the stable Deployment. The [car-sharing/istio](https://github.com/Connectum-Framework/examples/tree/main/car-sharing/istio) example wires the canary subsets in `canary-virtual-service.yaml` and `destination-rule.yaml`. ### Header-Based Routing Route specific users or test traffic to the canary by adding an HTTP `match` block to the VirtualService: match requests carrying an `x-canary: true` header and route them to the canary subset, while all other traffic goes to stable. See [virtual-service.yaml](https://github.com/Connectum-Framework/examples/blob/main/car-sharing/istio/virtual-service.yaml) in the car-sharing example for the VirtualService structure to extend. ## 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. An Istio `Telemetry` resource enables tracing (e.g. 100% sampling), Prometheus metrics, and OTel access logging for the namespace. See the [Istio Telemetry API](https://istio.io/latest/docs/reference/config/telemetry/) for the resource schema, and the [car-sharing/istio](https://github.com/Connectum-Framework/examples/tree/main/car-sharing/istio) directory for the example's mesh manifests. ### 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/car-sharing/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/car-sharing/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. Annotate the pod template with `sidecar.istio.io/proxyCPU`, `sidecar.istio.io/proxyMemory` (and their `*Limit` variants) to set CPU and memory requests/limits for the Envoy proxy. See the [Istio resource annotations](https://istio.io/latest/docs/reference/config/annotations/) reference. ## Health Check Configuration with Istio Use the same `/healthz` liveness and `/readyz` readiness probes documented in [Kubernetes health checks](/en/guide/health-checks/kubernetes). Istio 1.20+ rewrites HTTP probes through the sidecar by default, so the Connectum application does not need a second health model. Verify probe rewriting against the Istio version deployed by your cluster before rollout. ## 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', }, }); ``` Choose exactly one key source (`jwksUri`, `publicKey`, or `secret`), then constrain the expected issuer, audience, age, and algorithms for your identity provider. Use `claimsMapping` only for the claims that become application identity. See [`JwtAuthInterceptorOptions`](/en/api/@connectum/auth/interfaces/JwtAuthInterceptorOptions) for the complete field contract and defaults. ## 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: ```mermaid flowchart LR Jwks[jwksUri] -->|preferred over| PublicKey[publicKey] PublicKey -->|preferred over| Secret[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 Kubernetes manifests for a multi-service deployment are available in the [car-sharing/k8s](https://github.com/Connectum-Framework/examples/tree/main/car-sharing/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/car-sharing/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/car-sharing/k8s/configmap.yaml) for the full manifest. ## Secret Store TLS certificates and sensitive configuration in Secrets. For application-level TLS, create a `kubernetes.io/tls` secret holding the service's TLS certificate and private key, and mount it into the pod. The [car-sharing/k8s](https://github.com/Connectum-Framework/examples/tree/main/car-sharing/k8s) example relies on Istio for mTLS rather than application-level TLS secrets; it stores gateway configuration (identity provider endpoints) in `configmap.yaml` instead. ::: 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. A Deployment configures a 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. For full Deployment manifests, see the [car-sharing/k8s](https://github.com/Connectum-Framework/examples/tree/main/car-sharing/k8s) directory — the example ships one Deployment per role (`deployment-fleet.yaml`, `deployment-billing.yaml`, `deployment-trips.yaml`). ## Service ### ClusterIP (Internal gRPC Traffic) For service-to-service communication within the cluster. Create a ClusterIP Service on port 5000 with `appProtocol: grpc` to hint service meshes and ingress controllers about the protocol. See [services.yaml](https://github.com/Connectum-Framework/examples/blob/main/car-sharing/k8s/services.yaml) for the full manifest (the car-sharing example exposes one ClusterIP Service per role — fleet, billing, trips). ### LoadBalancer (External gRPC Access) For direct external gRPC access (without a gateway), create a LoadBalancer Service on port 443 with cloud provider annotations (e.g., AWS NLB with HTTP/2 backend protocol). The car-sharing example fronts external traffic with an Istio Gateway instead of a LoadBalancer Service — see the [Service Mesh guide](./service-mesh.md) and [car-sharing/istio](https://github.com/Connectum-Framework/examples/tree/main/car-sharing/istio). ## 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/car-sharing/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/car-sharing/k8s/rbac.yaml) for the full manifest. ## Shutdown and Probe Alignment The canonical shutdown sequence and hook ordering live in [Graceful shutdown](/en/guide/server/graceful-shutdown); probe semantics and status transitions live in [Kubernetes health checks](/en/guide/health-checks/kubernetes). At deployment level, preserve this deadline invariant: ``` 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. ::: The deployment manifest should use `/healthz` for liveness and `/readyz` for readiness, enable the HTTP health handler, and let the application move readiness to `NOT_SERVING` before drain. Do not maintain a second status or shutdown timeline in Kubernetes manifests. ## 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 At shutdown, mark the service `NOT_SERVING` before Kubernetes removes the pod from endpoints, then leave enough grace time for request draining and hooks. See the canonical [graceful shutdown timeline](/en/guide/server/graceful-shutdown#shutdown-timeline) for the complete sequence and timing boundaries. ::: 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/migration/1.0.md description: >- Required runtime, interceptor, transport, and EventBus changes for pre-1.0 applications. --- # Migrating to Connectum 1.0 This guide applies to applications upgrading from a release candidate or alpha to 1.0 or later. Make each applicable change and run your service's transport, authorization, and broker integration tests before deployment. ## Upgrade Node.js Published packages require Node.js `>=22.13.0`. Upgrade the runtime before installing 1.x packages. Packages ship compiled JavaScript; the higher Node.js floor for directly executing application TypeScript is a separate concern in [Runtime Compatibility](/en/guide/runtime-compatibility). ## Enable Resilience Explicitly `createDefaultInterceptors()` enables error handling and validation by default. Timeout, bulkhead, circuit breaker, retry, and fallback behavior is opt-in. ```typescript // Preserve the former implicit resilience set explicitly. createDefaultInterceptors({ timeout: true, bulkhead: true, circuitBreaker: true, retry: true, }); ``` Review whether each policy belongs on the inbound server path before preserving it. The circuit breaker now classifies infrastructure failures and is primarily an outbound/client pattern. See [Built-in Interceptor Chain](/en/guide/interceptors/built-in) and the exact [`DefaultInterceptorOptions`](/en/api/@connectum/interceptors/defaults/interfaces/DefaultInterceptorOptions). ## Use an HTTP/2-Capable Transport for Bidi Streaming `server.start()` rejects a user service with a bidirectional-streaming method when the effective transport is plaintext HTTP/1.1. Configure h2c with `allowHTTP1: false` or use TLS with HTTP/2 negotiation. Temporary compatibility modes are available through `transportValidation: 'warn' | 'off'`, but they preserve a configuration in which bidi calls can hang or fail. Use the [Transport Matrix](/en/guide/production/transport-matrix) to choose the production transport. ## Remove `PublishOptions.sync` Delete `sync` from EventBus publish options. It was a no-op: each adapter already waits for its broker-specific publish acknowledgement. No replacement is needed. ```typescript await eventBus.publish(OrderCreatedSchema, payload, { topic: 'orders.created', }); ``` ## Migrate Service Registration If the application uses legacy `ServiceRoute` registration or manually builds cross-service clients, follow [Migrating to the Service Catalog](/en/migration/service-catalog). ## Verify the Upgrade * Confirm all installed `@connectum/*` packages are on the intended release line. * Run unary and streaming transport tests. * Exercise validation, auth/authz, and explicitly enabled resilience behavior. * Publish and consume a representative event through every configured adapter. * Start and terminate the service to verify readiness and graceful shutdown. --- --- url: /en/migration/service-catalog.md description: >- Adopt defineService, the remoteResolver, and ctx.call — and the breaking removal of ServiceRoute and server.client fallback --- # Migrating to the Service Catalog > Applies to 1.0.0. 1.0.0 replaces the ad-hoc service-registration and cross-service-call wiring with a **service catalog**: services are declared with `defineService`, remote routing is configured once with a `remoteResolver`, and handlers make typed cross-service calls through `ctx.call`. Two old shapes are **removed** in the same release: * the `ServiceRoute` registration callback (`(router) => void`), and * the per-call `fallback` transport on `server.client(Desc, { fallback })`. This is a compiling breaking change: the removed types no longer exist, so a project that uses them will fail to type-check until migrated. Migration is currently **manual** — automated codemods are tracked as a separate, future change. ## 1. `ServiceRoute` → `defineService` A service is now a `{ descriptor, register }` pair produced by `defineService` (descriptor + handler map), instead of an opaque router callback. Keeping the proto descriptor alongside the handlers lets the framework build the catalog, drive local vs remote activation, and validate the transport. **Before** ```typescript import { createServer } from '@connectum/core'; import { GreeterService } from './gen/greeter_pb.js'; const routes = (router) => { router.service(GreeterService, { async sayHello(req, ctx) { return { message: `Hello, ${req.name}!` }; }, }); }; const server = createServer({ services: [routes] }); ``` **After** ```typescript import { createServer, defineService } from '@connectum/core'; import { GreeterService } from './gen/greeter_pb.js'; const greeter = defineService(GreeterService, { async sayHello(req, ctx) { return { message: `Hello, ${req.name}!` }; }, }); const server = createServer({ services: [greeter] }); ``` Handlers now receive a Connectum `Context` (a superset of the raw ConnectRPC `HandlerContext` that adds `ctx.call` and `ctx.stream`). Existing handlers that read `ctx.signal`, `ctx.timeoutMs()`, `ctx.requestHeader`, `ctx.values`, etc. keep working unchanged — every `HandlerContext` field is forwarded. For DI-heavy services whose handlers are expensive to construct, use `defineLazyService(descriptor, factory)`. The `factory` runs only when the service is actually mounted locally (i.e. listed in `enabledServices`, covered below), so a service routed to a remote process never instantiates its local dependencies. ```typescript import { defineLazyService } from '@connectum/core'; const orders = defineLazyService(OrdersService, () => createOrdersHandlers(deps)); ``` ### Per-service options (interceptors, `jsonOptions`) The third argument of `router.service(Descriptor, impl, options)` — a per-service option bag applied to every method of that service — is preserved as the optional third argument of `defineService` (and `defineLazyService`), typed as `ServiceOptions`. So a service-scoped interceptor chain or `jsonOptions` migrates one-to-one. **Before** ```typescript const routes = (router) => { router.service(GreeterService, handlers, { interceptors: [requireAuth] }); }; ``` **After** ```typescript import { defineService } from '@connectum/core'; const greeter = defineService(GreeterService, handlers, { interceptors: [requireAuth], }); ``` `ServiceOptions` is exactly `@connectrpc/connect`'s `router.service` option bag, so `jsonOptions` and the other per-service handler options carry over unchanged. A pure local monolith needs nothing else — no catalog, no resolver. ## 2. `server.client(Desc, { fallback })` → `remoteResolver` The per-call `fallback` transport is removed. `ServerClientOptions` no longer has a `fallback` field (it now carries only an optional `endpoint` hint for polymorphic deployments). Instead, configure routing **once** on the server with a `remoteResolver`, and `server.client(Desc)` auto-routes: in-process for locally mounted services, resolver-supplied transport for everything else. **Before** ```typescript import { createGrpcTransport } from '@connectrpc/connect-node'; const remoteTransport = createGrpcTransport({ baseUrl: 'https://inventory.internal' }); // A manual fallback transport, repeated at every call site: const client = server.client(InventoryService, { fallback: remoteTransport }); await client.checkStock({ sku }); ``` **After** ```typescript import { createServer, singleTransportResolver } from '@connectum/core'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { serviceCatalog } from './gen/catalog.js'; const server = createServer({ services: [orders], // OrdersService is local catalog: serviceCatalog, // generated: typeName → DescService enabledServices: ['shop.v1.OrdersService'], remoteResolver: singleTransportResolver( createGrpcTransport({ baseUrl: 'https://inventory.internal' }), ), }); // Same call site whether InventoryService is co-located or remote: const client = server.client(InventoryService); await client.checkStock({ sku }); ``` Pick the resolver that matches your topology — all four are exported from `@connectum/core` and must be synchronous (no network I/O; the framework caches the result per `(typeName, endpoint)`): * `singleTransportResolver(transport)` — route every remote service to one upstream (a sidecar or gateway). * `mapResolver({ [typeName]: transport })` — an explicit allow-list; unknown `typeName`s resolve to `null` (→ `Code.Unavailable`). * `dnsResolver({ template })` — derive a base URL per service from a DNS-style template, e.g. `'http://{shortName}.prod.svc.cluster.local:50051'`. * `perServiceEnvResolver({ [typeName]: 'ENV_VAR' })` — read each service's base URL from its own environment variable. The `catalog` is generated by `protoc-gen-catalog` (a `{ typeName → DescService }` map). It drives startup validation and remote routing; it is also what makes `ctx.call` typed (see below). ## 3. Manual `createClient` in handlers → `ctx.call` Cross-service calls from inside a handler no longer require hand-wiring a transport and a generated client. Call `ctx.call("${typeName}/${Method}", req)` instead — it is typed by the generated catalog and automatically cascades the inbound abort signal and deadline (a caller may shorten the deadline but never extend it). **Before** ```typescript import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { InventoryService } from './gen/inventory_pb.js'; const inventoryTransport = createGrpcTransport({ baseUrl: process.env.INVENTORY_URL }); const orders = defineService(OrdersService, { async placeOrder(req, ctx) { const inventory = createClient(InventoryService, inventoryTransport); // Manual signal/deadline plumbing per call: const stock = await inventory.checkStock({ sku: req.sku }, { signal: ctx.signal }); // ... }, }); ``` **After** ```typescript const orders = defineService(OrdersService, { async placeOrder(req, ctx) { // Typed by the generated catalog; signal + deadline cascade automatically. const stock = await ctx.call('shop.v1.InventoryService/CheckStock', { sku: req.sku }); // ... }, }); ``` The method key is `"${typeName}/${Method}"` where `Method` is the PascalCase proto method name (e.g. `"shop.v1.InventoryService/CheckStock"`), matching the ConnectRPC URL convention. For streaming methods, use `ctx.stream(...)` instead of `ctx.call(...)`. `ctx.call` requires a `catalog` on the server — without one it throws `ConnectError(Code.FailedPrecondition)`. Without the generated catalog augmentation the key type is `never`, so `ctx.call` is statically uncallable — the correct default for a service that makes no cross-service calls. ## 4. Manual env→endpoint registry → `parseServicesEnv` + a resolver Two distinct env-driven concerns, previously hand-rolled in boot code, are now first class. Do not conflate them: * **Which services this process hosts locally** — `parseServicesEnv` turns a comma-separated env value into the `enabledServices` list (full proto `typeName`s). * **Where to reach remote services** — `perServiceEnvResolver` maps each remote `typeName` to the env var holding its base URL. **Before** ```typescript // Hand-rolled: parse env, build a registry, look up per call. const REGISTRY = { 'shop.v1.InventoryService': process.env.INVENTORY_URL, 'shop.v1.PaymentService': process.env.PAYMENT_URL, }; ``` **After** ```typescript import { createServer, parseServicesEnv, perServiceEnvResolver } from '@connectum/core'; import { serviceCatalog } from './gen/catalog.js'; const server = createServer({ services: [orders, inventory], catalog: serviceCatalog, // CONNECTUM_SERVICES="shop.v1.OrdersService,shop.v1.InventoryService" enabledServices: parseServicesEnv(process.env.CONNECTUM_SERVICES), remoteResolver: perServiceEnvResolver({ 'shop.v1.PaymentService': 'PAYMENT_URL', }), }); ``` A service listed in `enabledServices` is mounted locally; anything else in the catalog is treated as remote and resolved by the `remoteResolver`. A `perServiceEnvResolver` mapping with no entry, or whose env var is unset, resolves to `null` (→ `Code.Unavailable`). ## 5. Error handling: configuration vs operational failures 1.0.0 splits errors by cause, so a programmer mistake fails loud rather than being mapped to an RPC status: * **Configuration mistake** → `CatalogConfigError` (a plain `Error` with a stack), thrown eagerly. `server.client(Desc)` for a service that is **not** mounted locally **and** has **no** `remoteResolver` configured throws `CatalogConfigError` at the `server.client(...)` call — not later, at dispatch. `enabledServices` entries absent from the catalog throw `CatalogConfigError` at `server.start()`. * **Operational failure** → `ConnectError` with a meaningful `Code`. A resolver that returns `null` (no route) surfaces as `ConnectError(Code.Unavailable)`. ```typescript // Not mounted locally AND no remoteResolver → configuration error, thrown here: const client = server.client(PaymentService); // throws CatalogConfigError // With a remoteResolver that returns null for this service → operational error: const client = server.client(PaymentService); // throws ConnectError(Code.Unavailable) ``` Catch `CatalogConfigError` only in tooling/tests; in normal operation it should crash the process so the misconfiguration is fixed, not swallowed. ## Migration checklist * \[ ] Replace every `(router) => { router.service(...) }` callback with `defineService(Descriptor, handlers)`. * \[ ] Switch DI-heavy services to `defineLazyService(Descriptor, factory)` where the factory should run only when the service is mounted locally. * \[ ] Remove `fallback` from all `server.client(Desc, { ... })` call sites. * \[ ] Configure a single `remoteResolver` on `createServer({ ... })` (`singleTransportResolver` / `mapResolver` / `dnsResolver` / `perServiceEnvResolver`). * \[ ] Pass the generated `catalog` to `createServer` if any process makes cross-service calls or routes to remote services. * \[ ] Replace in-handler `createClient(Svc, transport)` with `ctx.call("${typeName}/${Method}", req)` (and `ctx.stream(...)` for streaming). * \[ ] Replace hand-rolled env registries with `parseServicesEnv` (local activation) and a resolver (remote endpoints). * \[ ] Handle the split error model: expect `CatalogConfigError` for misconfiguration and `ConnectError(Code.Unavailable)` for an unresolvable remote service. --- --- url: /en/guide/observability.md description: >- Route tracing, metrics, logging, and exporter configuration to their canonical Connectum guides. --- # Observability `@connectum/otel` connects RPC telemetry and application instrumentation to OpenTelemetry. Instrumentation decides what signals to create; provider/exporter configuration decides where those signals go. ## Minimal RPC instrumentation ```typescript import { createOtelInterceptor } from '@connectum/otel'; const server = createServer({ services: [routes], interceptors: [ createOtelInterceptor({ filter: ({ service }) => !service.includes('grpc.health'), }), ], }); ``` ## Choose the signal | Need | Canonical guide | |---|---| | Server/client RPC spans, propagation, `traced()` or `traceAll()` | [Tracing](/en/guide/observability/tracing) | | Automatic RPC instruments and application meters | [Metrics](/en/guide/observability/metrics) | | Structured records and trace correlation | [Logging](/en/guide/observability/logging) | | OTLP endpoints, exporters, resource metadata, provider lifecycle | [Backends and configuration](/en/guide/observability/backends) | Focused guides explain task behavior. Exact option fields belong to generated interfaces such as [`OtelInterceptorOptions`](/en/api/@connectum/otel/interfaces/OtelInterceptorOptions) and [`ProviderOptions`](/en/api/@connectum/otel/provider/interfaces/ProviderOptions). Use the [`@connectum/otel` module hub](/en/packages/otel) for installation, key entry points, source, and API routes. Avoid copying exporter tables into tracing or metrics pages; backend configuration is their canonical owner. --- --- url: /en/guide/protocols/reflection.md description: Enable schema discovery for tools and control its production exposure. --- # 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. This page owns operational enablement and client-tool usage. Implementing a new server extension belongs to [Custom protocols](/en/guide/protocols/custom). ## 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 ::: pm \== npm ```bash npm install @connectum/reflection ``` \== pnpm ```bash pnpm add @connectum/reflection ``` \== bun ```bash bun 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/contributing/parity-coverage.md --- # Parity Coverage Report This page reports the current coverage of the [cross-transport parity invariant](./parity-invariant.md). It is updated manually when parity scenarios are added or removed; the underlying numbers can be regenerated from the parity test files at any time. > **Last updated:** Phase 7 of OpenSpec change `in-process-transport`. > **Target coverage:** ≥ 90 % of observable behaviour exercised through the > `transportParityTest()` driver. ## Scenarios by group | Group | Surface | File | Scenarios | |------:|---------|------|----------:| | 3 | Server-side interceptor ordering (3.1), client-side interceptor injection (3.2), timeout (3.3a), retry (3.3b), bulkhead (3.3c), circuit-breaker (3.3d), logger (3.3e), serializer (3.3f) | `packages/testing/tests/parity/interceptors.parity.test.ts` | **8** | | 3a | `protovalidate` / `buf.validate` (success, single-rule violation, aggregated violations, streaming validation, no-bypass API (3a.6)) | `packages/testing/tests/parity/validation.parity.test.ts` | **5** | | 3b | Proto-declared authz (success with scope, unauthenticated, permission denied, public method, no-bypass API (3b.6)) | `packages/testing/tests/parity/authorization.parity.test.ts` | **5** | | 4 | Streaming & cancellation (unary, server-stream, client-stream, bidi, unary cancel, stream mid-cancel) | `packages/testing/tests/parity/streaming.parity.test.ts` | **6** | | 5 | Error mapping (`ConnectError(NotFound)`, plain `Error` → `internal`, interceptor-thrown error) | `packages/testing/tests/parity/errors.parity.test.ts` | **3** | | 6 | HTTP / local coexistence (concurrent observation by one interceptor; `server.start()` not required for local invoke) | `packages/testing/tests/parity/coexistence.parity.test.ts` + `packages/core/tests/integration/localTransport.test.ts` | **2** | | 7a | OTEL tracing & metrics (unary spans, streaming events, error spans, metrics labels, trace-context propagation, instrument subset, `connectum.transport` attribute) | `packages/otel/tests/parity/otel.parity.test.ts` | **7** | | **Total** | | | **36** | Of these, **25 scenarios** (groups 3, 3a, 3b, 4, 5) go through the unified `transportParityTest()` driver in `@connectum/testing/parity` and produce a structural diff between HTTP and local. The 7 OTEL scenarios and 2 coexistence scenarios are written as paired `test()` cases that drive both transports explicitly and assert equality of observable signals — semantically equivalent to the driver, but expressed in long form because they need bespoke exporter setup (OTEL) or asymmetric assertions (coexistence). ## Coverage analysis Observable behaviours that a service can produce, and their current parity coverage: | Observable behaviour | Covered | |---|:---:| | Response payload (unary) | ✅ groups 3a / 3b / 5 | | Response payload (streaming) | ✅ group 4 | | Response headers / trailers | ✅ group 3 (via driver diff) | | `ConnectError` code / message | ✅ groups 3a / 3b / 5 | | `ConnectError` metadata / details | ✅ groups 3a / 3b | | Streaming message order | ✅ group 4 | | Cancellation propagation | ✅ group 4 (5, 6) | | Interceptor chain order | ✅ group 3 | | Validation interceptor outcomes | ✅ group 3a | | Auth/authz interceptor outcomes | ✅ group 3b | | OTEL span attributes / status | ✅ group 7a | | OTEL span events (streaming) | ✅ group 7a | | OTEL trace-context propagation | ✅ group 7a | | OTEL metrics (names, labels, values) | ✅ group 7a | | Coexistence (one server, two transports) | ✅ group 6 | | Server lifecycle (local before `start()`) | ✅ group 6.2 | Behaviours **not** covered by parity (by design, see [`parity-invariant.md`](./parity-invariant.md#when-parity-does-not-apply)): TLS, HTTP/2 framing, content-encoding negotiation, `:authority` / real-host `req.url`, gzip — these are wire-only and have no in-process analogue. **Coverage estimate:** of 16 distinct observable behaviour categories above, all 16 are exercised through the parity mechanism. The 4 explicitly out-of-scope categories (TLS, HTTP/2 framing, content-encoding, real `req.url` host) are wire-specific and excluded by spec. Observable coverage: **16 / 16 = 100 %** of in-scope behaviours; **16 / 20 = 80 %** if wire-only behaviours are counted as denominator. By the spec's "observable behaviour" definition (wire-only is excluded), coverage clears the 90 % target. ## How to add a scenario 1. Pick the right file in `packages/testing/tests/parity/` (or `packages/otel/tests/parity/` for observability). 2. For most cases, wrap the scenario with `transportParityTest`: ```typescript transportParityTest("group N.M: short title", { services: [myRoutes], scenario: async ({ transport }) => { const client = createClient(MyService, transport); return { response: await client.myMethod({ /* ... */ }) }; }, }); ``` 3. Run `./scripts/parity-suite.sh` locally. 4. Update the table above. --- --- 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): ::: runtime \== node ```bash # Check types pnpm typecheck # or: tsc --noEmit # Watch mode for development tsc --noEmit --watch ``` \== bun ```bash # Check types bun run typecheck # or: bunx tsc --noEmit # Watch mode for development bunx tsc --noEmit --watch ``` ::: ## Development Workflow ::: runtime \== node ```bash # Node.js 25+: start with auto-reload (watches for file changes) node --watch src/index.ts # tsx: start with auto-reload (Node.js 22+) tsx --watch src/index.ts # Type check in a separate terminal tsc --noEmit --watch # Or run once pnpm typecheck && pnpm start ``` \== bun ```bash # Start with auto-reload (watches for file changes) bun --watch src/index.ts # Type check in a separate terminal bunx tsc --noEmit --watch # Or run once bun run typecheck && bun run start ``` ::: ## Checklist Before running your Connectum service, verify: * \[ ] `"type": "module"` in `package.json` * \[ ] `verbatimModuleSyntax: true` in `tsconfig.json` * \[ ] `import type` for all type-only imports * \[ ] `node:` prefix for built-in modules ::: runtime \== node * \[ ] Node.js 25+ installed (`node --version`), or tsx installed (`npx tsx --version`) * \[ ] `erasableSyntaxOnly: true` in `tsconfig.json` * \[ ] No `enum` in application code (use `const` objects) -- type stripping cannot execute it * \[ ] `.ts` extensions in relative imports * \[ ] Proto enums handled via [two-step generation](/en/guide/typescript/proto-enums) (if applicable; not needed with tsx) \== bun * \[ ] Bun installed (`bun --version`) * \[ ] `.ts` extensions in relative imports (optional for Bun, but keeps the code portable to Node.js) Bun transpiles TypeScript instead of stripping types, so `enum`, `namespace` and parameter properties execute as written and proto enums need no extra generation step. Keep to the erasable subset anyway if the same code has to run on Node.js. ::: ## 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.method.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: ```mermaid flowchart TD Request["user.v1.UserService/GetUser"] Request --> Global["* → logRequest"] Global --> Service["user.v1.UserService/* → auth"] Service --> Exact["user.v1.UserService/GetUser → cache"] Exact --> Next["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) *** ### getInternalMethods Re-exports [getInternalMethods](../functions/getInternalMethods.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) | | `internal` | `bool` | Mark all methods as internal (service-to-service). Skips end-user JWT auth; requires a trust marker from `createInternalAuthInterceptor`. Since 1.1.0. See [ADR-029](/en/contributing/adr/029-internal-service-to-service-auth). | #### `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 | | `internal` | `bool` | Mark the method as internal (service-to-service). Distinct from `public`: world-open vs. trusted-caller-only. Since 1.1.0. | #### `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: ```mermaid flowchart TD Start[Resolve method authorization] --> Public{Proto public?} Public -->|yes| Allow[Allow · skip authn and authz] Public -->|no| Internal{Proto internal?} Internal -->|yes| Trusted{Auth context exists?} Trusted -->|no| Unauthenticated[Reject · Unauthenticated] Trusted -->|yes| InternalRequires{Proto requires?} InternalRequires -->|no| Allow InternalRequires -->|yes| Requirements[Evaluate roles and scopes] Internal -->|no| Requires{Proto requires?} Requires -->|yes| HasContext{Auth context exists?} HasContext -->|no| Unauthenticated HasContext -->|yes| Requirements Requires -->|no| Policy{Proto policy?} Requirements --> Policy Policy -->|yes| ProtoResult[Apply allow or deny] Policy -->|no| Rules{Programmatic rule matches?} Rules -->|yes| RuleResult[Apply matching rule] Rules -->|no| Callback{authorize callback?} Callback -->|yes| CallbackResult[Apply callback result] Callback -->|no| Default[Apply defaultPolicy · deny by default] ``` 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` | | `internal` | `method_auth.internal` | `service_auth.internal` | `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 * [OpenAPI](/en/guide/openapi) -- publish a contract whose `security` reflects these options * [ADR-024: Auth/Authz Strategy](/en/contributing/adr/024-auth-authz-strategy) -- design rationale --- --- url: /en/guide/protocols.md description: Distinguish built-in operational protocols from custom server extensions. --- # Protocol Extensions The `protocols` array registers capabilities that share the server transport but are not application service routes. Connectum ships operational protocols for health and reflection; advanced consumers can implement the same registration contract for a custom gRPC service or HTTP fallback handler. ```typescript const server = createServer({ services: [routes], protocols: [ Healthcheck({ httpEnabled: true }), Reflection(), ], }); ``` ## Operational protocols * [Health checks](/en/guide/health-checks) own readiness/liveness state and platform probes. * [Server reflection](/en/guide/protocols/reflection) owns schema discovery, tool usage, and the production exposure warning. ## Advanced extension point [Creating a custom protocol](/en/guide/protocols/custom) owns `ProtocolRegistration`, `ProtocolContext`, HTTP handler behavior, registration timing, and examples. Add protocols before `server.start()`; this is an explicit server extension boundary, not general request middleware. Exact core types remain in [`ProtocolRegistration`](/en/api/@connectum/core/types/interfaces/ProtocolRegistration) and the generated [core API](/en/api/@connectum/core/). Package setup belongs to the [`@connectum/healthcheck`](/en/packages/healthcheck) and [`@connectum/reflection`](/en/packages/reflection) module hubs. --- --- 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) * [ResourceAttributeInputs](interfaces/ResourceAttributeInputs.md) ## Functions * [buildResourceAttributes](functions/buildResourceAttributes.md) * [getProvider](functions/getProvider.md) * [initProvider](functions/initProvider.md) * [parseOtelResourceAttributesEnv](functions/parseOtelResourceAttributesEnv.md) * [shutdownProvider](functions/shutdownProvider.md) --- --- url: /en/guide/service-communication/resolvers.md --- # Remote Resolvers A **remote resolver** is the service-catalog routing layer: it maps a proto service identity to the `Transport` used to reach that service. Three APIs consult it: the unified client factory (`server.client(Desc)`), the catalog primitive (`ctx.call(...)`), and the standalone catalog client (`createCatalogClient(...)`). For the first two, locally-mounted services dispatch in-process and never touch the resolver; everything else is resolved through it. `createCatalogClient` has no local server, so every call goes through the resolver unconditionally. You pass a resolver to `createServer({ remoteResolver })` for server-side routing, or directly to `createCatalogClient({ resolver })` for out-of-process workers, schedulers, and CLIs. The framework calls it lazily, on the first route to a given service, and caches the result. ## The `RemoteResolver` contract A resolver is a plain function: ```typescript import type { RemoteResolver, ResolverContext } from '@connectum/core'; // (ctx: { typeName: string; endpoint?: string }) => Transport | null const resolver: RemoteResolver = ({ typeName, endpoint }) => { // map service identity → Transport, or null }; ``` `ResolverContext` carries the proto `typeName` (e.g. `"orders.v1.OrdersService"`) and an optional `endpoint` hint (see [Endpoint hints](#endpoint-hints)). The contract is strict: * **Synchronous.** The signature returns `Transport | null` directly — never a `Promise`. The framework caches per `(typeName, endpoint)` and cannot await a resolver. * **No network I/O.** A resolver must not dial TCP or perform a DNS lookup. It only *maps an identity to a lazily-connecting transport*. ConnectRPC transports (e.g. `createGrpcTransport({ baseUrl })`) do not open a socket until the first RPC, which is exactly what makes a synchronous, I/O-free resolver safe — startup validation never blocks on DNS or a dial. * **`null` means "no route."** Returning `null` is an operational miss: the call fails with `Code.Unavailable` — at dispatch time for `ctx.call`, and eagerly at client construction for `server.client()`. (A *missing* `remoteResolver` for a non-local `server.client()` is a different, configuration-time failure — `CatalogConfigError`.) * **Cached per `(typeName, endpoint)`.** The resolver runs at most once per unique route; the resolved transport is reused for every subsequent call. ## Built-in resolvers `@connectum/core` ships four resolver factories covering the common deployment shapes. ### `singleTransportResolver(transport)` Routes **every** remote service to the same transport. Ideal for a single upstream — a sidecar or an API gateway — that fronts all remote services, and for local development where one process holds everything. ```typescript import { createServer, singleTransportResolver } from '@connectum/core'; import { createGrpcTransport } from '@connectrpc/connect-node'; const gateway = createGrpcTransport({ baseUrl: 'http://gateway:50051' }); const server = createServer({ services: [myRoutes], remoteResolver: singleTransportResolver(gateway), }); ``` ### `mapResolver({ [typeName]: transport })` An explicit per-service map. Use it when each remote service has its own transport and you want an exact allow-list — any `typeName` not in the map resolves to `null` (→ `Code.Unavailable`). ```typescript import { createServer, mapResolver } from '@connectum/core'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { OrdersService } from '#gen/orders/v1/orders_pb.js'; import { InventoryService } from '#gen/inventory/v1/inventory_pb.js'; const server = createServer({ services: [myRoutes], remoteResolver: mapResolver({ [OrdersService.typeName]: createGrpcTransport({ baseUrl: 'http://orders:50051' }), [InventoryService.typeName]: createGrpcTransport({ baseUrl: 'http://inventory:50051' }), }), }); ``` ### `dnsResolver(options)` Derives a base URL per service from a DNS-style template, then builds a transport for it. This mirrors container/Kubernetes service-name routing, where the service identity *is* its DNS name. `DnsResolverOptions`: | Field | Type | Description | |-------|------|-------------| | `template` | `string` | URL template with `{shortName}` (alias `{name}`) placeholders. | | `createTransport?` | `(baseUrl: string) => Transport` | Builds a transport from the resolved URL. Defaults to a gRPC (HTTP/2) transport via `createGrpcTransport({ baseUrl })`. | The **short name** is the last dot-segment of the `typeName`, lower-cased, with a trailing `Service` stripped — `orders.v1.OrdersService` becomes `orders`. Both `{shortName}` and `{name}` expand to the same value. ```typescript import { createServer, dnsResolver } from '@connectum/core'; const server = createServer({ services: [myRoutes], remoteResolver: dnsResolver({ template: 'http://{shortName}.prod.svc.cluster.local:50051', }), }); ``` `dnsResolver` **always resolves** — it never returns `null`, because the template is assumed to cover every remote service. If you need an explicit allow-list (unknown services rejected as `Unavailable`), use `mapResolver` instead. ### `perServiceEnvResolver(map, options?)` Reads each service's base URL from an environment variable. `map` pairs each `typeName` with the *name* of the env var holding its URL. This replaces hand-rolled env registries in boot code. `PerServiceEnvResolverOptions`: | Field | Type | Description | |-------|------|-------------| | `createTransport?` | `(baseUrl: string) => Transport` | Builds a transport from the resolved URL. Defaults to a gRPC (HTTP/2) transport. | A service with no mapping, or whose env var is unset or empty, resolves to `null` (→ `Code.Unavailable`). ```typescript import { createServer, perServiceEnvResolver } from '@connectum/core'; import { OrdersService } from '#gen/orders/v1/orders_pb.js'; // Reads process.env.ORDERS_URL at resolve time. const server = createServer({ services: [myRoutes], remoteResolver: perServiceEnvResolver({ [OrdersService.typeName]: 'ORDERS_URL', }), }); ``` ## Endpoint hints For services reachable at several endpoints (multi-region, blue/green, or a tenant-specific upstream), pass an opaque `endpoint` hint. It is forwarded to the resolver as `ctx.endpoint` and is part of the cache key, so distinct endpoints resolve to distinct cached transports. The hint is **ignored for locally-mounted services**. From the unified client factory (`ServerClientOptions`): ```typescript const ordersEu = server.client(OrdersService, { endpoint: 'eu-west' }); const ordersUs = server.client(OrdersService, { endpoint: 'us-east' }); ``` From inside a handler (`CallOptions`): ```typescript const inner = await ctx.call( 'orders.v1.OrdersService/GetOrder', create(GetOrderRequestSchema, { id }), { endpoint: 'eu-west' }, ); ``` Your resolver decides what the hint means: ```typescript const regional: RemoteResolver = ({ typeName, endpoint }) => { const region = endpoint ?? 'eu-west'; const shortName = typeName.split('.').pop()!.replace(/Service$/, '').toLowerCase(); return createGrpcTransport({ baseUrl: `http://${shortName}.${region}.svc:50051` }); }; ``` ## Composing resolvers A resolver returning `null` is the natural delegation signal: write a composite that tries each resolver in order and takes the first non-null result. Because resolvers are synchronous, the composite is a plain loop. ```typescript import type { RemoteResolver } from '@connectum/core'; /** Try each resolver in order; first non-null wins, null if all miss. */ function fallback(...resolvers: RemoteResolver[]): RemoteResolver { return (ctx) => { for (const resolve of resolvers) { const transport = resolve(ctx); if (transport) return transport; } return null; }; } // Explicit overrides first, DNS convention as the catch-all. const server = createServer({ services: [myRoutes], remoteResolver: fallback( mapResolver({ [OrdersService.typeName]: ordersOverride }), dnsResolver({ template: 'http://{shortName}.prod.svc.cluster.local:50051' }), ), }); ``` Order `dnsResolver` last in such a chain — it always resolves, so any resolver after it is unreachable. ## Testing with mocks `@connectum/testing` (not `@connectum/core`) provides a resolver and a context helper for serving canned, in-process responses with no network hop. `mockResolver([mockService(Service, impl)])` builds a `RemoteResolver` that serves each mocked service in-process and returns `null` for anything not mocked — so it composes with real resolvers via the `null`-fallback pattern above. Every mock response carries the response header `MOCK_RESPONSE_HEADER` (`"x-connectum-mock"`) set to `"true"`, so a test can prove the call was served by a mock rather than a real transport. ```typescript import { create } from '@bufbuild/protobuf'; import { createServer } from '@connectum/core'; import { mockResolver, mockService, MOCK_RESPONSE_HEADER } from '@connectum/testing'; import { InventoryService, StockSchema } from '#gen/inventory/v1/inventory_pb.js'; const server = createServer({ services: [], remoteResolver: mockResolver([ mockService(InventoryService, { getStock: () => create(StockSchema, { units: 7 }), }), ]), }); // The mock tag is a *response header*. Read it via the connect client's // header hook — ctx.call surfaces only the message, not headers. const client = server.client(InventoryService); let servedByMock: string | null = null; const stock = await client.getStock( create(GetStockRequestSchema, { sku: 'x' }), { onHeader: (h) => { servedByMock = h.get(MOCK_RESPONSE_HEADER); } }, ); // servedByMock === 'true'; stock.units === 7 ``` To unit-test a handler's `ctx.call` / `ctx.stream` logic in isolation, `createMockContext({ catalog, mocks })` builds a `Context` that drives the **same** dispatch path as a live request (a real `Server` is constructed with a `mockResolver`), so resolver lookup, cascade injection, interceptor composition, and error semantics all match production. ```typescript import { create } from '@bufbuild/protobuf'; import { defineCatalog } from '@connectum/core'; import { createMockContext, mockService } from '@connectum/testing'; import { InventoryService, StockSchema } from '#gen/inventory/v1/inventory_pb.js'; import { CreateOrderSchema } from '#gen/orders/v1/orders_pb.js'; const ctx = createMockContext({ catalog: defineCatalog({ [InventoryService.typeName]: InventoryService }), mocks: [ mockService(InventoryService, { getStock: () => create(StockSchema, { units: 7 }), }), ], }); // Drive the handler directly with the mock context. const res = await orderHandler(create(CreateOrderSchema, { sku: 'x' }), ctx); ``` `CreateMockContextOptions` also accepts `outgoingInterceptors`, `requestHeader`, `timeoutMs`, and `propagateHeaders` to reproduce production header propagation and the deadline cascade. ## Kubernetes, Istio, and service meshes `dnsResolver` covers Docker Compose and Kubernetes service discovery directly: the template points at the service's DNS name (`http://{shortName}..svc.cluster.local:`), and Kubernetes resolves it to the service's cluster IP. No external service registry is required. When a mesh (Istio, Linkerd) or an Envoy sidecar is present, routing and mTLS are handled transparently at the sidecar — the resolver still just points at the local service DNS name, and the sidecar intercepts the connection to apply load balancing, retries, and certificate-based identity. The resolver layer does not change between a plain Kubernetes deployment and a meshed one. ## Related * [Communication Patterns](./patterns) -- request-response, fan-out, streaming * [Service Communication](/en/guide/service-communication) -- overview, transport configuration, service discovery * [Client Interceptors](./client-interceptors) -- OTel, resilience, circuit breaker configuration * [@connectum/core API](/en/api/@connectum/core/) -- full API reference --- --- 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/runtime-compatibility.md --- # Runtime Compatibility Connectum targets **Node.js 22+** as the primary runtime and is exercised on Bun in CI on every pull request. This page documents the current state of runtime compatibility across all `@connectum/*` packages. ::: tip Runtime switcher Pages that show runtime-specific commands have a **Node.js | Bun** switch in the navigation bar (and next to each affected block). This page deliberately shows both runtimes side by side. ::: ::: tip Scaffolding emits runtime-appropriate defaults `connectum init --runtime bun` (see [Scaffolding a Service](/en/guide/scaffolding)) emits Bun-appropriate defaults — the `bun test` runner and the in-process `createLocalClient` test transport, which opens no socket and behaves identically on both runtimes. The CLI itself is exercised on Node.js, so run it with `npx` even inside a Bun project. ::: ## Compatibility Matrix | Package | Node.js 22 | Node.js 25 | Bun >= 1.2.6 | |---------|:----------:|:----------:|:------------:| | `@connectum/core` | Yes | Yes | Yes | | `@connectum/interceptors` | Yes | Yes | Yes | | `@connectum/healthcheck` | Yes | Yes | Yes | | `@connectum/reflection` | Yes | Yes | Yes | | `@connectum/auth` | Yes | Yes | Yes | | `@connectum/events` | Yes | Yes | Yes | | `@connectum/events-nats` | Yes | Yes | Yes | | `@connectum/events-kafka` | Yes | Yes | Yes | | `@connectum/events-redis` | Yes | Yes | Yes | | `@connectum/events-amqp` | Yes | Yes | Yes | | `@connectum/otel` | Yes | Yes | Partial | | `@connectum/cli` | Yes | Yes | Partial | | `@connectum/testing` | Yes | Yes | Partial | **Legend:** Yes = fully supported, Partial = works with limitations (see details below). **Bun floor:** HTTP/2 client transports require **Bun >= 1.2.6** (see [HTTP/2 Client Transport](#http2-client)); the examples repository pins Bun >= 1.3.6. `@connectum/cli` is exercised on Node.js only -- run it with `npx` even in a Bun project. ## HTTP/2 Client Transport {#http2-client} ### Node.js On Node.js, HTTP/2 gRPC clients work out of the box with `createGrpcTransport()`: ```typescript import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { GreeterService } from '#gen/greeter/v1/greeter_pb.js'; const transport = createGrpcTransport({ baseUrl: 'http://localhost:5000', httpVersion: '2', }); const client = createClient(GreeterService, transport); const res = await client.sayHello({ name: 'Alice' }); ``` This uses Node.js native `node:http2` module for full HTTP/2 multiplexing. ### Bun **Bun >= 1.2.6 needs no special client code** -- the snippet above works unchanged. Unary, server-streaming and bidi-streaming calls over `createGrpcTransport()` and `createConnectTransport({ httpVersion: '2' })` all complete, and status codes carried in HTTP/2 trailers arrive intact. Bun's `node:http2` **client** was incomplete before 1.2.6: on those versions the transport is constructed without error and the **first RPC hangs** -- the call never completes and no error is thrown. Bun 1.2.6 rewrote the `node:http2` client and closed this. ::: warning Bun <= 1.2.5 Upgrade Bun. If you cannot, the only working option is `createConnectTransport()` over HTTP/1.1 (the default `httpVersion`): ```typescript import { createClient } from '@connectrpc/connect'; import { createConnectTransport } from '@connectrpc/connect-node'; import { GreeterService } from '#gen/greeter/v1/greeter_pb.js'; const transport = createConnectTransport({ baseUrl: 'http://localhost:5000', // httpVersion defaults to '1.1' -- omit or set explicitly }); const client = createClient(GreeterService, transport); const res = await client.sayHello({ name: 'Alice' }); ``` That path carries unary and server-streaming calls but **not bidi streaming**, gives up HTTP/2 multiplexing, and requires the server to accept HTTP/1.1 (`allowHTTP1: true`, the default). Connectum servers speak the Connect protocol alongside gRPC, so no server change is needed. ::: **Servers are unaffected on every Bun version.** A Connectum server -- including plaintext h2c (`allowHTTP1: false`) -- serves HTTP/2 correctly; the limitation was always client-side. ## Streaming RPC {#streaming} ### Node.js On Node.js, both unary and streaming RPCs work with `createGrpcTransport()` or `createConnectTransport()`: ```typescript import { createClient } from '@connectrpc/connect'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { MonitorService } from '#gen/monitor/v1/monitor_pb.js'; const transport = createGrpcTransport({ baseUrl: 'http://localhost:5000', httpVersion: '2', }); const client = createClient(MonitorService, transport); // Server streaming -- works with any transport for await (const event of client.watchEvents({ filter: 'error' })) { console.log(`Event: ${event.type} -- ${event.message}`); } ``` ### Bun The same code runs on Bun -- **no runtime branching is needed**, and Connectum itself contains none. * **Server streaming** works on every Bun version tested, over both HTTP/2 and HTTP/1.1 transports. * **Bidi streaming** requires HTTP/2, and therefore Bun >= 1.2.6 for the client. This is a protocol constraint, not a Bun one: bidi streaming is impossible over HTTP/1.1 on any runtime, and Connectum refuses to start a server that hosts bidi methods on plaintext HTTP/1.1 (`CONNECTUM_UNSUPPORTED_STREAMING_TRANSPORT`). ::: warning Do not hand-build a fetch transport Earlier revisions of this page suggested `createTransport()` from `@connectrpc/connect/protocol-connect` together with `createFetchClient(globalThis.fetch)`. Do not use that pattern: `createTransport` is marked internal by ConnectRPC and is not covered by semantic versioning, and on Bun 1.1.x it silently drops the request body. ::: ## Testing Utilities {#testing} `@connectum/testing` is a public, production-ready package. Its mock helpers -- including `createMockNext()`, `createMockNextError()`, `createMockNextSlow()`, and the underlying `createMockFn()` spy -- are implemented on top of a portable spy factory that does **not** depend on `node:test`, so the same test code runs on Node.js, Bun, Deno, and bundler environments. ```typescript import { describe, it } from 'node:test'; // or 'bun:test' import { createMockNext, createMockRequest } from '@connectum/testing'; describe('my interceptor', () => { it('calls next', async () => { const next = createMockNext(); await myInterceptor(createMockRequest(), next); // next.mock.callCount() === 1 }); }); ``` `createMockFn()` is API-compatible with the subset of `node:test`'s `mock.fn()` that the testing helpers rely on (`.mock.calls`, `.mock.callCount()`), so assertions written against one runtime work on the other. Full API: [@connectum/testing](/en/packages/testing). The package is marked **Partial** on Bun for one reason: the `@connectum/testing/parity` subpath registers a `node:test` test (`transportParityTest`) and therefore runs on Node.js only. The main entry point has no such dependency. ## OpenTelemetry {#otel} `@connectum/otel` depends on the official `@opentelemetry/*` SDK packages, which use `node:perf_hooks`, `node:diagnostics_channel`, and other Node.js-specific APIs. | Feature | Node.js | Bun | |---------|:-------:|:---:| | Tracing (spans) | Yes | Partial -- basic spans work, some auto-instrumentation may fail | | Metrics | Yes | Partial -- manual metrics work, automatic HTTP metrics may not | | Logging | Yes | Yes | | Auto-instrumentation | Yes | No -- `@opentelemetry/auto-instrumentations-node` is not compatible | ::: warning If you use `@connectum/otel` on Bun, test your specific instrumentation setup thoroughly. Manual instrumentation (explicit span creation) is more reliable than auto-instrumentation on Bun. ::: ## Known Issues {#known-issues} | Issue | Runtime | Status | Workaround | |-------|---------|--------|------------| | HTTP/2 client transports (`createGrpcTransport()`, `createConnectTransport({ httpVersion: '2' })`) hang on the first RPC | Bun <= 1.2.5 | **Fixed in Bun 1.2.6** | Upgrade Bun; on older Bun use `createConnectTransport()` over HTTP/1.1 (no bidi) | | `node:test` mock API unavailable | Bun | By design | Use `bun:test` mock directly | | `@connectum/testing/parity` requires `node:test` | Bun | By design | Use the main entry point; run parity tests on Node.js | | `@connectum/cli` is exercised on Node.js only | Bun | Open | Run the CLI with `npx`; generated code is unaffected | | OpenTelemetry auto-instrumentation | Bun | Open (OTel) | Use manual instrumentation | ## Related * [Runtime Support](/en/guide/typescript/runtime-support) -- how each runtime executes TypeScript and loads `@connectum/*` packages * [Service Communication](/en/guide/service-communication) -- client transport configuration and patterns * [Testing](/en/guide/testing) -- scenario-based API testing * [@connectum/testing](/en/packages/testing) -- Package Guide * [@connectum/otel](/en/packages/otel) -- Package Guide --- --- url: /en/guide/scaffolding.md --- # Scaffolding a New Service The `connectum` CLI scaffolds a production-ready Connectum project and adds services to an existing one. It fetches the dogfooded `getting-started` example as the base — so the starter layout comes from a real, tested example rather than a template copy — and composes the modules you select on top. ::: tip Requirements `connectum init` fetches the base from GitHub, so it needs network access the first time. It produces a standalone project that depends on the published `@connectum/*` packages. The CLI itself is exercised on Node.js — run it with `npx` even when the project you are scaffolding targets Bun. ::: ::: tip The base is pinned per CLI release Each CLI release fetches a **fixed tag** of the examples repository, not its default branch, so the same CLI version always scaffolds the same base. Pass `--ref` to fetch a different one (`--ref main` for the latest example). Drift between the pinned base and the live example is caught by CI on the framework repository, not by your `init`. ::: ## `connectum init` Create a new project. Run it interactively: ```bash npx @connectum/cli init ``` Run the CLI with `npx` whatever you use day to day: it reaches a server through the Node.js gRPC transport and is exercised on Node.js only. The project it generates has no such restriction. The wizard asks for a project name, runtime, package manager, and which modules to include. Or pass everything as flags for a non-interactive run: ```bash npx @connectum/cli init payments \ --package-manager pnpm \ --otel \ --events nats \ --auth \ --yes ``` Then: ```bash cd payments ``` ::: pm \== npm ```bash npm install npm run start ``` \== pnpm ```bash pnpm install pnpm run start ``` \== bun ```bash bun install bun run start ``` ::: ::: tip `--package-manager` and `--runtime` are independent `--package-manager` decides what installs dependencies and runs scripts; `--runtime` decides what executes your TypeScript. Either accepts `bun`, and they do not have to agree: `bun install` lays out an ordinary `node_modules`, so a bun-installed project runs on Node.js and an npm-installed one runs on Bun. Both crossings are exercised in CI. ::: `buf generate` is wired into the `start`, `test`, and `typecheck` scripts, so the generated code under `gen/` is always current — you never hit a "cannot find module `#gen/...`" wall. The generated scripts match the runtime you picked: ::: runtime \== node * `start` — `buf generate && node src/index.ts` (raw `.ts` execution; Node >= 25.2, or `tsx` on Node >= 22.13) * `test` — `buf generate && node --test tests/**/*.test.ts` \== bun * `start` — `buf generate && bun src/index.ts` * `test` — `buf generate && bun test tests/` ::: The generated e2e test itself is runtime-agnostic: it uses the in-process `createLocalClient`, which opens no socket and behaves identically on both runtimes. It also calls the project's own `buildServer()` rather than assembling a throwaway server, so the request travels the same interceptor chain, protocols and services your process entry starts. That matters: a test that builds its own bare server passes even when a module has made the service unreachable. ### Options | Flag | Values | Description | |------|--------|-------------| | `--runtime` | `node` (default), `bun` | Target runtime | | `--package-manager` | `pnpm` (default), `npm`, `bun` | Package manager | | `--node-exec` | `raw` (default), `tsx` | Node execution model: `raw` runs `.ts` directly (Node ≥25.2); `tsx` compiles (Node ≥22.13) | | `--otel` | — | Add OpenTelemetry (interceptor + provider lifecycle) | | `--events` | `nats`, `kafka`, `redpanda`, `redis`, `amqp` | Add an EventBus with the chosen adapter | | `--auth` | — | Add JWT authentication + proto-driven authorization | | `--catalog` | — | Add the service catalog (typed `ctx.call` / `ctx.stream`) | | `--resilience` | comma list of `timeout,bulkhead,circuitBreaker,retry,fallback` | Enable resilience interceptors | | `--healthcheck` / `--no-healthcheck` | — | Include the gRPC health protocol (default on) | | `--reflection` / `--no-reflection` | — | Include gRPC server reflection (default on) | | `--sample` / `--no-sample` | — | Emit the runnable sample Greeter service (default on) | | `--yes`, `-y` | — | Non-interactive; use flags and defaults | | `--force` | — | Overwrite existing files | | `--ref` | any git ref | Base example ref to fetch (advanced; defaults to the tag pinned for this CLI release) | ### What `--auth` generates Proto-driven authorization is **deny-by-default**, so the sample service is annotated to be both usable and demonstrative: * `SayHello` carries `option (connectum.auth.v1.method_auth) = { public: true }` — it skips authentication and authorization, so the scaffolded project answers a call the moment it starts; * `SayGoodbye` is left unannotated — it requires a valid JWT. The generated e2e test asserts both directions: the public rpc succeeds and the authenticated one is rejected without credentials. Remove the option from `SayHello` once you want every method to require a token. ### Interceptor order When multiple interceptor-adding modules are selected, `init` emits a single, consistent order (outermost → innermost): **OpenTelemetry → error handler → auth → validation → resilience → your custom interceptors**. OpenTelemetry is outermost so a span always covers the whole request, including errors. ## `connectum generate service` Add a service to an existing project: ```bash npx @connectum/cli generate service billing # with an event handler too: npx @connectum/cli generate service inventory --with-events ``` This scaffolds: * `proto//v1/.proto` — a starter service (and, with `--with-events`, an event-handler service annotated with a topic); * `src/services/Service.ts` — a `defineService` skeleton whose rpc handlers throw `Code.Unimplemented` until you implement them (and, with `--with-events`, an `EventRoute` with an ack-by-default handler). It never edits your `src/server.ts` (that file is yours). Instead it prints the exact registration to add: ```text Register the new service in src/server.ts: import { billingService } from "#services/billingService.ts"; // add billingService to the services: [...] array passed to createServer ``` ## Adding a service by hand The one-shot commands are conveniences — the underlying loop is small: 1. Add a `proto//v1/.proto`. 2. Run `buf generate` (or just `pnpm run test` / `start` — it runs first). 3. Write `defineService(MyService, { ... })` in `src/services/`. 4. Register it in the `services: [...]` array in `src/server.ts`. --- --- 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/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 description: >- Understand the Connectum server boundary and choose the guide that owns each lifecycle task. --- # Server `createServer()` is the composition boundary for a Connectum process. It registers service routes, protocols and interceptors, selects the transport, and owns startup and shutdown. This page is the mental model; configuration values and lifecycle details live in their focused guides and generated API. ## Minimal server ```typescript import { createServer } from '@connectum/core'; import { Healthcheck } from '@connectum/healthcheck'; import { createDefaultInterceptors } from '@connectum/interceptors'; import { Reflection } from '@connectum/reflection'; 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 }, }); await server.start(); ``` ## Choose the owning guide | Need | Canonical destination | |---|---| | Understand states, events, and `shutdownSignal` | [Lifecycle](/en/guide/server/lifecycle) | | Configure listen address, TLS, environment, or schema extension | [Configuration](/en/guide/server/configuration) | | Drain requests, coordinate hooks, and fit a deployment deadline | [Graceful shutdown](/en/guide/server/graceful-shutdown) | | Select HTTP/1.1, h2c, or TLS/ALPN behavior | [Transport matrix](/en/guide/production/transport-matrix) | | Find an exact server option or method | [`CreateServerOptions`](/en/api/@connectum/core/types/interfaces/CreateServerOptions) and [core API](/en/api/@connectum/core/) | The stable state progression is `created → starting → running → stopping → stopped`. React to lifecycle events for application coordination; pass `server.shutdownSignal` to long-running work so cancellation follows the same shutdown boundary. ## Module route Use the [`@connectum/core` module hub](/en/packages/core) for installation, the smallest package example, related guides, source, and API entry points. --- --- 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 ```mermaid stateDiagram-v2 [*] --> created created --> starting: server.start() / start starting --> running: ready running --> stopping: server.stop() or signal / stopping stopping --> stopped: stop 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 the transition order shown in the state diagram: `start`, `ready`, `stopping`, then `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/service-communication/service-catalog.md --- # Service Catalog The **service catalog** turns cross-service calls into a single declarative primitive. Instead of constructing a transport and a client at every call site, a handler writes `ctx.call(...)` (or `ctx.stream(...)`) and the framework chooses the transport for you: an **in-process** call when the target service is mounted locally, a **remote** call (via a resolver) when it lives in another process. The call site never changes — split a service out of a monolith and the same `ctx.call` line keeps working. A catalog is a plain readonly map of proto `typeName → DescService`. It carries **no topology**: what is local versus remote is decided at boot by `enabledServices` and a `remoteResolver`, never baked into the proto. The catalog exists to do two jobs — make `ctx.call` / `ctx.stream` **fully typed**, and let the server **resolve** a target to a transport. ## Defining services Register a service with `defineService(descriptor, handlers)`. Each handler receives a Connectum `Context` as its second argument. `Context` extends ConnectRPC's `HandlerContext` — every field you already know (`signal`, `timeoutMs()`, `requestHeader`, `values`, …) is still there — and adds `ctx.call` and `ctx.stream`. ```typescript import { create } from '@bufbuild/protobuf'; import { defineService } from '@connectum/core'; import { OrdersService, CreateOrderResponseSchema } from './gen/orders/v1/orders_pb.js'; import { ReserveRequestSchema } from './gen/inventory/v1/inventory_pb.js'; const orders = defineService(OrdersService, { async createOrder(req, ctx) { // Cross-service call — local or remote is decided by the framework. const reservation = await ctx.call( 'inventory.v1.InventoryService/Reserve', create(ReserveRequestSchema, { sku: req.sku, quantity: req.quantity }), ); return create(CreateOrderResponseSchema, { orderId: reservation.orderId }); }, }); ``` `defineService` returns a `ServiceDefinition` (`{ descriptor, register }`) that you pass to `createServer({ services })`. `defineLazyService(descriptor, factory)` is the same, but `factory()` runs **only when the service is actually mounted locally** — i.e. when its `typeName` is in `enabledServices` (or `enabledServices` is `undefined`). A service routed to a remote process never instantiates its local dependencies, which is useful for DI-heavy monoliths where wiring a service is expensive. ## Configuring the catalog Pass the catalog to `createServer({ catalog })`. The catalog drives startup validation and remote routing. It is **optional** — a process that hosts everything locally and makes no cross-service calls needs none of these fields. ```typescript import { createServer, defineCatalog } from '@connectum/core'; import { OrdersService } from './gen/orders/v1/orders_pb.js'; import { InventoryService } from './gen/inventory/v1/inventory_pb.js'; const catalog = defineCatalog({ [OrdersService.typeName]: OrdersService, [InventoryService.typeName]: InventoryService, }); const server = createServer({ services: [orders], catalog, }); ``` `defineCatalog` freezes the record and preserves the literal key type for inference. `mergeCatalogs(...catalogs)` combines several catalogs into one (handy in a polyrepo); it throws `CatalogConfigError` on a duplicate `typeName`. ### Generating the catalog Writing the catalog and its type augmentations by hand is tedious and drifts from proto. The `@connectum/protoc-gen-catalog` buf plugin generates a `catalog.gen.ts` per buf module containing both the runtime `serviceCatalog` object and the type augmentations. ```yaml # buf.gen.yaml version: v2 plugins: - local: protoc-gen-es out: gen opt: [target=ts, import_extension=.js] - local: protoc-gen-connectum-catalog out: gen opt: [target=ts, import_extension=.js] ``` The generated file augments `@connectum/core`'s `ConnectumCallMap` (one entry per unary RPC) and `ConnectumStreamMap` (one per streaming RPC), keying each `"/"` to its request/response types: ```typescript // catalog.gen.ts (generated — DO NOT EDIT) import type {} from '@connectum/core'; import { GreeterService } from './greeter_pb.js'; import type { SayHelloRequest, SayHelloResponse } from './greeter_pb.js'; export const serviceCatalog = { 'greeter.v1.GreeterService': GreeterService, } as const; declare module '@connectum/core' { interface ConnectumCallMap { 'greeter.v1.GreeterService/SayHello': { request: SayHelloRequest; response: SayHelloResponse }; } interface ConnectumStreamMap {} } ``` ::: warning The generated file must be loaded The `declare module` augmentation is types-only and is **erased** unless something imports the file. Re-export it from your contracts package's `index.ts`, or add a top-level `import './catalog.gen.js';`. Without it, consumers silently see `keyof ConnectumCallMap` as `never` and `ctx.call` becomes uncallable. Keep the mandatory `import type {} from '@connectum/core';` line — it lets the augmentation merge across packages. ::: With no augmentation, `keyof ConnectumCallMap` is `never`, so `ctx.call` is statically uncallable — the right default for a service that makes no cross-service calls. ## Unary calls — `ctx.call` ```typescript const response = await ctx.call(method, request, options?); ``` `method` is a `"${typeName}/${Method}"` key. **Note the casing:** the key uses the proto method name (PascalCase, e.g. `.../SayHello`), which is distinct from the camelCase handler name (`sayHello`). `request` is the request message; build it with `create(Schema, { ... })`. The return is a `Promise`. The transport is chosen automatically: an in-process call when the target is mounted locally (proven by the in-process dispatch — no TCP socket is opened), otherwise the transport supplied by the configured `remoteResolver`. Resolved remote transports are cached per `(typeName, endpoint)` so the resolver runs at most once per route. ```typescript async secureEcho(req, ctx) { const inner = await ctx.call( 'inventory.v1.InventoryService/CheckStock', create(CheckStockRequestSchema, { sku: req.sku }), ); return create(EchoResponseSchema, { available: inner.available }); } ``` ## Streaming calls — `ctx.stream` `ctx.stream(method)` is **curried**: it returns a kind-specific factory, then you call that factory. The shape depends on the streaming kind recorded in `ConnectumStreamMap`. **Server-streaming** → factory takes the request and returns an `AsyncIterable`: ```typescript for await (const item of ctx.stream('streaming.v1.StreamingService/Server')( create(ItemSchema, { value: req.message, sequence: 3 }), )) { // consume item } ``` **Client-streaming** → factory returns a `ClientStreamHandle`: `send()` N requests, then `close()` resolves the single aggregated response: ```typescript const handle = ctx.stream('streaming.v1.StreamingService/Client')(); handle.send(create(ItemSchema, { value: 'a', sequence: 0 })); handle.send(create(ItemSchema, { value: 'b', sequence: 1 })); const count = await handle.close(); // Promise ``` **Bidi-streaming** → factory returns a `BidiStreamHandle`: `send()` requests while iterating `responses`; `close()` ends **only** the request (send) half — the response half keeps yielding until the server completes: ```typescript const handle = ctx.stream('streaming.v1.StreamingService/Bidi')(); handle.send(create(ItemSchema, { value: 'a', sequence: 0 })); handle.send(create(ItemSchema, { value: 'b', sequence: 1 })); handle.close(); // void — ends the send half only for await (const item of handle.responses) { // consume server responses } ``` On a **mid-stream transport failure** the iterator follows a **deliver-then-error** policy: it delivers the messages received so far, then throws the terminal `ConnectError`. Consumers that `break` early do not hang. ## Cascade behaviour (`CallOptions`) `ctx.call(method, request, options?)` accepts an optional `CallOptions` as its third argument. `ctx.stream(method)` takes only the method key and returns a factory — pass `CallOptions` to that returned factory, not to `ctx.stream` itself (e.g. `ctx.stream(method)(request, options)` for server-streaming, or `ctx.stream(method)(options)` for client/bidi). Omitted dimensions cascade from the incoming request. ```typescript type CallOptions = { signal?: AbortSignal; // default: inbound ctx.signal timeoutMs?: number; // default: remaining inbound deadline headers?: HeadersInit; // default: none (see below) endpoint?: string; // hint for the remoteResolver }; ``` * **`signal`** — when omitted, the inbound request's `ctx.signal` is injected, so cancelling the inbound RPC cancels every in-flight `ctx.call`. A supplied signal **replaces** the cascade — it is **not** AND-linked with `ctx.signal`. * **`timeoutMs`** — when omitted, the remaining inbound deadline (`ctx.timeoutMs()`) is injected. A caller may **shorten** the deadline but never extend it; the effective value is `min(timeoutMs, remaining)`. An over-long override is clamped to the remaining deadline. * **Trace context** flows implicitly via the `@connectum/otel` client interceptor when it is mounted in `outgoingInterceptors` — no header plumbing needed. * **Headers are NOT propagated by default.** No inbound header leaks onto an outgoing call. ### Header propagation Opt in by listing header names in `createServer({ propagateHeaders })`. `defaultPropagateHeaders` is a ready-made allow-list of W3C trace-context headers (`["traceparent", "tracestate"]`) you can spread and extend. `authorization` is deliberately excluded — forwarding credentials is a security-sensitive choice you must make explicitly. ```typescript import { createServer, defaultPropagateHeaders } from '@connectum/core'; const server = createServer({ services: [orders], catalog, propagateHeaders: [...defaultPropagateHeaders, 'x-tenant-id'], }); ``` Explicit `CallOptions.headers` always win over a propagated value. ## Single-image, multiple roles — `enabledServices` `enabledServices` is a list of full proto `typeName`s a process mounts **locally**. Any service in `services` whose `typeName` is not listed is treated as remote and reached via the `remoteResolver`. `undefined` mounts every provided service locally. This lets one image play different roles depending on configuration — a modular monolith in one deployment, split processes in another, with no code change. ```typescript import { createServer, parseServicesEnv } from '@connectum/core'; const server = createServer({ services: [orders, inventory, payments], catalog, // e.g. CONNECTUM_SERVICES="orders.v1.OrdersService,inventory.v1.InventoryService" enabledServices: parseServicesEnv(process.env.CONNECTUM_SERVICES), remoteResolver, // see the Resolvers guide }); ``` Full `typeName`s are mandatory — short names collide (`catalog.v1.UsersService` and `auth.v1.UsersService` both shorten to `users`). Three helpers support env-driven configuration: * `parseServicesEnv(value)` — parses a comma-separated env string into a `string[]`, trimming whitespace and dropping empties. Returns `[]` for an empty/undefined value. * `matchServicesPattern(pattern, names)` — returns the subset of `names` matching a glob `pattern`, where `*` matches any run of characters including dots (e.g. `"acme.*"` matches `"acme.v1.UsersService"`). This is a **glob, not a RegExp** — only `*` is special. * `mergeEnabledServices(...lists)` — merges several lists, de-duplicating while preserving first-seen order. ## Calling from outside a handler `ctx.call` only exists inside a handler (it needs a live `HandlerContext`). For boot code, scripts, or tests, use the server's client factories: * **`server.localClient(Desc)`** — a fully-typed client that dispatches directly to handlers on this server, with no TCP socket. Safe to call before `server.start()`. * **`server.client(Desc, options?)`** — auto-routes: in-process when the service is mounted locally, otherwise via the configured `remoteResolver`. The same call site works for monolith and split deployments. `options.endpoint` is an opaque hint forwarded to the resolver. ```typescript const client = server.client(InventoryService); // local or remote — same call const stock = await client.checkStock({ sku: 'A-1' }); ``` Both `server.localClient` and `server.client` require a `Server` instance. For a process with **no server at all** — a Temporal worker, a scheduler, a CLI — use `createCatalogClient`. It provides the same catalog-typed `call`/`stream` surface as the handler `ctx`, routing every call through the supplied resolver (there is no in-process path without a `Server`): ```typescript import { createCatalogClient, mapResolver } from '@connectum/core'; import { createGrpcTransport } from '@connectrpc/connect-node'; import { serviceCatalog } from './gen/catalog.js'; const client = createCatalogClient({ catalog: serviceCatalog, resolver: mapResolver({ 'inventory.v1.InventoryService': createGrpcTransport({ baseUrl: process.env.INVENTORY_ADDR }), }), }); const stock = await client.call('inventory.v1.InventoryService/CheckStock', { sku: 'A-1' }); // ctx.stream mirrors: client.stream('...')(request) for server-streaming, etc. ``` ## Error model Connectum splits **configuration mistakes** (programmer errors, thrown eagerly) from **operational failures** (runtime, mapped to RPC status codes). **`CatalogConfigError`** — a configuration mistake; fails loud with a stack trace. Thrown for: * `server.client(Desc)` on a service that is **not mounted locally and has no `remoteResolver`** configured (the catalog is not consulted in this path); * `enabledServices` that is **not a subset of the catalog** at `start()` (caught by an always-on shape check); * a **duplicate `typeName`** during `mergeCatalogs`. **`ConnectError`** — operational failures from `ctx.call` / `ctx.stream`, with the appropriate Connect status code: | Situation | Code | |-----------|------| | No catalog configured | `Code.FailedPrecondition` | | Unknown service, or known service with unknown method | `Code.Unimplemented` | | `remoteResolver` returns `null` (no route) | `Code.Unavailable` | | `remoteResolver` throws | `Code.Internal` (original error preserved as `cause`) | ## Related * [Resolvers](/en/guide/service-communication/resolvers) -- resolver patterns (`singleTransportResolver`, `mapResolver`, `dnsResolver`, `perServiceEnvResolver`) for remote routing * [Communication Patterns](./patterns) -- request-response chains, fan-out/fan-in, streaming * [Client Interceptors](./client-interceptors) -- OTel, resilience, the `outgoingInterceptors` chain --- --- url: /en/guide/service-communication.md description: >- Select synchronous, catalog, in-process, streaming, or event-driven communication by coupling and delivery needs. --- # Service Communication Start from the interaction contract, not the transport library. A caller that needs an immediate answer has different failure and coupling requirements from a consumer reacting asynchronously to an event. ## Choose the mechanism | Need | Canonical route | |---|---| | Decide between request/response, streaming, and events | [Choosing a mechanism](/en/guide/service-communication/choosing-a-mechanism) | | Call generated internal services through `ctx.call` / `ctx.stream` | [Service catalog](/en/guide/service-communication/service-catalog) | | Select and compose endpoint resolution | [Resolvers](/en/guide/service-communication/resolvers) | | Add client tracing, deadlines, retry, or credentials | [Client interceptors](/en/guide/service-communication/client-interceptors) | | Model fan-out, partial failure, or server streaming | [Communication patterns](/en/guide/service-communication/patterns) | | Keep calls in the same process with transport parity | [In-process transport](/en/guide/production/in-process-transport) | | React asynchronously through a broker | [Events](/en/guide/events) | ## Synchronous baseline Use the generated service catalog when services are part of the Connectum contract. It keeps target resolution, propagated request context, deadlines, and typed call names in one boundary. Use a direct ConnectRPC `createClient()` only for an external or deliberately uncatalogued API. For direct gRPC calls, the transport must support HTTP/2. Client-side resilience belongs around the outgoing transport, and only client-safe interceptors should be composed there. The exact supported set and ordering are owned by [Client interceptors](/en/guide/service-communication/client-interceptors), not repeated on every communication page. ## Operational rule Synchronous calls couple caller availability and latency to the downstream service. Events decouple time but add delivery, idempotency, ordering, and broker operations. The [mechanism decision guide](/en/guide/service-communication/choosing-a-mechanism) owns that trade-off; the focused pages own implementation details. --- --- 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 }, }); ``` The two application-owned callbacks are `verifySession`, which talks to the session backend, and `mapSession`, which creates the stable `AuthContext`. Override token extraction only when the framework cannot read your credential shape. The full cache and callback contract lives in [`SessionAuthInterceptorOptions`](/en/api/@connectum/auth/interfaces/SessionAuthInterceptorOptions). ## 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. ```mermaid flowchart LR Request[Request] --> Extract[Extract token or cookies] Extract --> Verify["verifySession(token, headers)"] Verify --> Map["mapSession(session)"] Map --> Context[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) * [detectConnectumTransport](functions/detectConnectumTransport.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. ## Interfaces * [RsaTestKeypair](interfaces/RsaTestKeypair.md) * [TestJwksServer](interfaces/TestJwksServer.md) ## Variables * [TEST\_JWT\_KID](variables/TEST_JWT_KID.md) * [TEST\_JWT\_SECRET](variables/TEST_JWT_SECRET.md) ## Functions * [createMockAuthContext](functions/createMockAuthContext.md) * [createTestJwt](functions/createTestJwt.md) * [createTestJwtRS256](functions/createTestJwtRS256.md) * [generateRsaTestKeypair](functions/generateRsaTestKeypair.md) * [startTestJwksServer](functions/startTestJwksServer.md) * [withAuthContext](functions/withAuthContext.md) --- --- url: /en/api/@connectum/events-amqp/testing.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / testing # testing Programmable AMQP test double — the `@connectum/events-amqp/testing` subpath (#203). `FakeAmqpAdapter` models the REAL adapter's observable contracts without a broker and without importing `amqplib` at runtime: * the typed error taxonomy (`AmqpConnectionError`, `AmqpTopologyError`, …) — inject outcomes per publish via [FakeAmqpControl.nextPublish](interfaces/FakeAmqpControl.md#nextpublish), including `AmqpPublishTimeoutError`: the state-UNKNOWN outcome that no real broker (or even Toxiproxy) reproduces deterministically; * the canonical lifecycle union ([AmqpLifecycleCallbacks.onLifecycle](../types/interfaces/AmqpLifecycleCallbacks.md#onlifecycle)) INCLUDING the deprecated flat-callback shim — events go through the real adapter's dispatch, so ordering, shim payloads, and exception isolation match the real adapter by construction; * the state machine: `connect()` on a live or recovering (or dead retries-exhausted) adapter throws `already connected` like the real one; a mid-recovery `subscribe()` PARKS and settles with the recovery outcome; the probe-then-recover `connect()` semantics gate on `AmqpTopologyError` exactly like the real probe. Deliberately NOT modeled (documented divergences): * timing: there is no backoff — recovery advances only via explicit [FakeAmqpControl.completeRecovery](interfaces/FakeAmqpControl.md#completerecovery) / [FakeAmqpControl.exhaustRecovery](interfaces/FakeAmqpControl.md#exhaustrecovery) calls, and `reconnecting.delay` is always `0`; * a queued topology `failSetup` at `connect()` WITHOUT fail-fast reports `setup-failed { initial: true }` and then connects anyway (the real adapter would keep retrying inside recovery); a NON-topology queued failure at `connect()` is consumed silently and the connect proceeds (the real adapter treats it as transient and blocks in recovery); * broker-driven settlement: handler `ack`/`nack` calls are RECORDED (see the [FakeAmqpControl.deliver](interfaces/FakeAmqpControl.md#deliver) result) but do not drive redelivery — re-deliver explicitly with a higher `attempt` to model it. Handler rejections are swallowed exactly like the real consumer (which nacks for redelivery instead of propagating); * the wire-level envelope: [FakeAmqpControl.published](interfaces/FakeAmqpControl.md#published) records the bus-facing call as the adapter received it. Wire fidelity is the real adapter's integration-tested domain. Incoming envelope headers ARE handled with parity: `deliver()` honors `x-event-id`/`x-published-at` and strips the internal keys from handler-visible metadata. ## Interfaces * [FakeAmqpAdapterInstance](interfaces/FakeAmqpAdapterInstance.md) * [FakeAmqpAdapterOptions](interfaces/FakeAmqpAdapterOptions.md) * [FakeAmqpControl](interfaces/FakeAmqpControl.md) * [FakeDeliveryResult](interfaces/FakeDeliveryResult.md) * [FakePublishedRecord](interfaces/FakePublishedRecord.md) ## Type Aliases * [FakePublishOutcome](type-aliases/FakePublishOutcome.md) ## Functions * [FakeAmqpAdapter](functions/FakeAmqpAdapter.md) --- --- url: /en/guide/testing.md description: >- Choose the smallest Connectum testing layer that proves the behavior you changed. --- # Testing Connectum supports two complementary testing layers. Use TypeScript tests for fast handler and transport feedback, then scenario tests for the deployed protocol boundary. ## Choose a layer | Question | Recommended surface | |---|---| | Does a handler or interceptor return the expected value/error? | [`@connectum/testing`](/en/packages/testing) with an in-process server/client | | Do service-catalog calls resolve without a network listener? | [In-process transport](/en/guide/production/in-process-transport) | | Does a running service expose correct gRPC, Connect, health, auth, TLS, or streaming behavior? | [runn](/en/guide/testing/runn) | | Do you need Go plugins or JUnit-oriented scenario output? | [scenarigo](/en/guide/testing/scenarigo) | ## Recommended path 1. Test handler and middleware decisions in process. 2. Start the real service with its generated proto contract. 3. Exercise one successful call and the important failure paths through `runn`. 4. Add deployment-specific probe or TLS scenarios only where that boundary matters. Scenario tests catch serialization, validation, interceptor-order, reflection, and health-endpoint integration that isolated unit tests cannot. They should not repeat every business-rule case already covered in TypeScript. See the complete [runn example suite](https://github.com/Connectum-Framework/examples/tree/main/runn) for the executable end-to-end shape. Exact testing helpers remain in the [`@connectum/testing` API](/en/api/@connectum/testing/); shared low-level fixtures are separated in [`@connectum/test-fixtures`](/en/packages/test-fixtures). --- --- 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/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/production/transport-matrix.md --- # Transport Matrix Which RPC types work on which server transport. The Connect protocol states: **"Bidirectional streaming requires HTTP/2, but the other RPC types also support HTTP/1.1"** — a bidi service on an HTTP/1.1 transport does not fail at startup by itself; the first client send simply hangs forever (or the client receives `HTTP 505`). Connectum turns this into a startup diagnostic — see [Startup validation](#startup-validation) below. ## Server transport modes `createServer()` picks the transport from `tls` and `allowHTTP1`: | Configuration | Transport | Node server | |---|---|---| | no `tls`, `allowHTTP1: true` (**default**) | plaintext HTTP/1.1 | `http.createServer` | | no `tls`, `allowHTTP1: false` | plaintext HTTP/2 (h2c) | `http2.createServer` | | `tls` configured | TLS + ALPN (HTTP/2 and HTTP/1.1 negotiated) | `http2.createSecureServer` | ## RPC type support | Transport | Unary | Server streaming | Client streaming | Bidi streaming | |---|---|---|---|---| | Plaintext HTTP/1.1 (default) | ✅ | ✅ | ✅ | ❌ blocked at startup | | Plaintext h2c (`allowHTTP1: false`) | ✅ | ✅ | ✅ | ✅ | | TLS + ALPN, HTTP/2 negotiated | ✅ | ✅ | ✅ | ✅ | | TLS + ALPN, **HTTP/1.1 negotiated** | ✅ | ✅ | ✅ | ❌ hangs at runtime | ::: warning Residual risk: TLS with an HTTP/1.1 client A TLS server with `allowHTTP1: true` is *streaming-capable* (HTTP/2 is negotiable), so startup validation does not hard-fail — but a client or intermediary that negotiates HTTP/1.1 over TLS (a client without `h2` in its ALPN list, a proxy with an HTTP/1.1 upstream leg) hits the same silent hang on bidi calls. When bidi methods are present on such a server, Connectum logs a **one-time warning** at startup. Remove the risk entirely by setting `allowHTTP1: false` (the server then refuses HTTP/1.1 at ALPN, so HTTP/1.1 clients fail the handshake explicitly instead of hanging on bidi), or keep bidi clients on HTTP/2 transports (`createGrpcTransport`, or `createConnectTransport` with `httpVersion: "2"`). Silence the warning with `transportValidation: "off"`. ::: ::: tip Pure gRPC protocol needs HTTP/2 even for unary The matrix above is for the **Connect protocol**. The classic gRPC protocol (used by `grpcurl`, gRPC reflection clients, and `createGrpcTransport`) requires HTTP/2 for *every* RPC type — on the default plaintext HTTP/1.1 server, gRPC clients and `grpcurl` do not work at all. Use h2c or TLS. ::: ## Serving gRPC and HTTP/1.1 on one **plaintext** port A single **plaintext** (no-TLS) port cannot serve **both** native gRPC (which needs HTTP/2 / h2c) **and** plain HTTP/1.1 clients. Per-connection protocol selection is done by **ALPN**, a TLS handshake extension — a cleartext socket has no handshake, so the server cannot tell an HTTP/1.1 request from the HTTP/2 connection preface. This is a **Node runtime limitation, not a Connectum one**: Node core has declined to add cleartext `allowHTTP1` ([nodejs/node#26795](https://github.com/nodejs/node/issues/26795), [#44887](https://github.com/nodejs/node/issues/44887) — both closed; maintainers prescribe userland byte-sniffing), and `Upgrade: h2c` is deprecated by RFC 9113. So `createServer()` offers `allowHTTP1: true` (HTTP/1.1 only) **or** `false` (h2c only) on a plaintext port — never both. This matters when a reverse proxy / API gateway that speaks **HTTP/1.1** (e.g. [Ory Oathkeeper](https://www.ory.sh/oathkeeper/), nginx) fronts a service whose internal peers use **native gRPC**. Resolve it with one of these, in order of preference: 1. **Put a sidecar / edge proxy in front (recommended — runtime-agnostic).** A proxy that multiplexes protocols — [Envoy](https://www.envoyproxy.io/) or [Caddy](https://caddyserver.com/) — terminates the mixed edge and forwards a single protocol upstream. The proxy does the protocol detection the runtime cannot, and it works the same on **every** JS runtime (see the matrix below). See [Envoy Gateway](/en/guide/production/envoy-gateway) and [Service Mesh](/en/guide/production/service-mesh). 2. **Use TLS + ALPN.** A TLS server serves HTTP/1.1 and HTTP/2 on one port (ALPN negotiates per client). If app-level TLS is acceptable, this is the built-in mixed-port answer. 3. **Two listeners.** Serve native gRPC (h2c) and Connect/HTTP-1.1 on separate ports/roles. Lower complexity, but not one port. ::: tip Connect and gRPC-Web do not need any of this Only **native gRPC** needs HTTP/2. The **Connect** and **gRPC-Web** protocols run over HTTP/1.1, so the default plaintext HTTP/1.1 server already serves both — a gateway that downgrades to HTTP/1.1 works for them with no extra setup. ::: ## Runtime support for native gRPC Native gRPC depends on **HTTP/2 response trailers** (`grpc-status`). The fetch-style `Response` used by `Bun.serve`, `Deno.serve`, and Cloudflare Workers carries no trailers, so those `serve()` APIs **cannot serve native gRPC at all** — they serve Connect and gRPC-Web (which fold trailers into the body) over HTTP/1.1. Connectum does not use them: `createServer()` builds on `node:http2`. | Runtime | Native gRPC server | Connect / gRPC-Web | gRPC + HTTP/1.1 on one plaintext port | |---|---|---|---| | **Node** (`node:http2` — what Connectum uses) | ✅ | ✅ | ❌ — use a sidecar proxy or TLS + ALPN | | **Bun** (`node:http2` — what Connectum uses) | ✅ | ✅ | ❌ | | **Bun** (`Bun.serve`) | ❌ (no HTTP/2 at all)\* | ✅ | ❌ | | **Deno** (`Deno.serve`) | ❌ (no HTTP/2 trailers)† | ✅ | ❌ | | **Cloudflare Workers** | ❌ (edge-terminated, no raw ports) | ✅ (Connect / gRPC-Web) | ❌ (n/a) | \* **Measured on Bun 1.3.13 and 1.3.14**, and the reason is stronger than the missing trailers: `Bun.serve` has **no HTTP/2 server at all**. Offered `["h2","http/1.1"]` over TLS it selects no protocol (`alpnProtocol === false`); an h2c prior-knowledge request fails with `ERR_HTTP2_ERROR Protocol error`; and `Response` has no trailer member, so a `Trailer:` header is echoed but nothing follows the body. There is no `http2` option to enable -- `Bun.serve` silently ignores unknown keys, so passing one proves nothing. Bun 1.3.14 adds an HTTP/3 server and an experimental HTTP/2 client for `fetch()`; neither is an HTTP/2 *server*. † `Deno.serve` is marked from its documented fetch-style `Response` API. **This project has not executed that case** -- unlike the Bun row above it. Connectum builds on neither API. Everything it *does* use is covered in [Verified behaviour by runtime](#verified). **Takeaway:** the fetch-style `serve()` APIs cannot host native gRPC, but that does not apply to a Connectum server on Bun: `createServer()` builds on `node:http2`, whose server side delivers trailers on Bun as well — including plaintext h2c. **Connect + gRPC-Web over HTTP/1.1 work on every runtime.** If you deploy on Deno / Workers, or write your own `Bun.serve` handler, and must expose gRPC, terminate it at a **sidecar proxy** (Envoy / Caddy) and let the runtime serve Connect / HTTP-1.1 — the proxy owns the protocol multiplexing the runtime cannot do. ::: tip Bun client versions Serving is unaffected on every Bun version, but Bun's `node:http2` **client** only became usable in **Bun 1.2.6** — see [Runtime Compatibility](/en/guide/runtime-compatibility#http2-client). ::: ## Verified behaviour by runtime {#verified} The tables above describe intent. This one records what was **executed**, so you can tell a tested guarantee from a reasonable expectation. Every row was run against a Connectum server built by `createServer()`, with `@connectum/core` 1.2.0 and `@connectrpc/connect-node` (both 2.0.0 and 2.1.2), asserting the response payload and the gRPC status code -- not merely that a call did not throw. Each scenario ran three times. ### Server side -- `createServer()` | Runtime | Plaintext HTTP/1.1 (default) | Plaintext h2c (`allowHTTP1: false`) | |---|---|---| | **Node.js** | Connect unary ✅ · server streaming ✅ · error status ✅ · native gRPC ❌ (needs HTTP/2) · bidi ❌ refused at startup | unary ✅ · server streaming ✅ · bidi ✅ · gRPC status in trailers ✅ | | **Bun** (1.1.38 and up) | identical to Node.js, including the startup refusal | unary ✅ · server streaming ✅ · bidi ✅ · gRPC status in trailers ✅ | The startup refusal was run on both runtimes: a service with a bidi method on `allowHTTP1: true` rejects `server.start()` with `CONNECTUM_UNSUPPORTED_STREAMING_TRANSPORT` rather than starting and hanging on the first send. **Serving was never the Bun problem.** A Connectum h2c server under Bun delivers HTTP/2 trailers in both directions -- verified down to Bun 1.1.38, including an error whose `grpc-status` arrives only in the trailer. This is because `createServer()` builds on `node:http2`; it is not affected by what `Bun.serve` can or cannot do. ### Client side -- `@connectrpc/connect-node` | Runtime | `createGrpcTransport` / `createConnectTransport({httpVersion:'2'})` | `createConnectTransport()` (HTTP/1.1) | |---|---|---| | **Node.js** | unary ✅ · server streaming ✅ · bidi ✅ · trailers ✅ | unary ✅ · server streaming ✅ · bidi ❌ (protocol) | | **Bun <= 1.2.5** | ❌ **first RPC hangs** -- the transport is constructed without error, the call never completes, nothing is thrown | unary ✅ · server streaming ✅ · bidi ❌ (protocol) | | **Bun >= 1.2.6** | unary ✅ · server streaming ✅ · bidi ✅ · trailers ✅ | unary ✅ · server streaming ✅ · bidi ❌ (protocol) | The Bun boundary was located by bisection over 1.1.38, 1.2.0, 1.2.5, 1.2.6, 1.2.7, 1.2.8, 1.2.9, 1.2.10, 1.2.15, 1.2.21, 1.3.0, 1.3.13 and 1.3.14: **1.2.5 hangs, 1.2.6 passes**, and every later version passes. Bun 1.2.6 rewrote the `node:http2` client. Re-checked on **Bun 1.3.14** (the latest release at the time of writing) with no regressions, in-process and cross-process in both directions -- a Connectum server hosted by Bun answering a Node.js gRPC client, and the reverse. The cross-process runs captured a real DATA-then-TRAILERS frame sequence carrying `grpc-status`, so the trailer claim is a wire observation rather than an inference from a client library. ::: warning A hang, not an error Earlier revisions of these docs described this as a `TypeError`. That symptom was **not reproducible on any tested combination**. What actually happens is worse to diagnose: the call simply never settles, so a service waits forever rather than failing fast. If you are pinned below Bun 1.2.6 and see a request that never returns, this is the first thing to check. ::: ### The two limitations are different in kind | | Node.js | Bun | |---|---|---| | **What** | one plaintext port serves HTTP/1.1 **or** h2c, never both | the `node:http2` client was unusable | | **Why** | protocol selection needs ALPN, which is a TLS handshake extension; a cleartext socket has no handshake | incomplete `node:http2` implementation | | **Scope** | server side | client side only | | **Status** | **permanent** -- Node core declined it ([#26795](https://github.com/nodejs/node/issues/26795), [#44887](https://github.com/nodejs/node/issues/44887), both closed) and `Upgrade: h2c` is deprecated by RFC 9113 | **fixed** in Bun 1.2.6 | | **Work around it by** | TLS + ALPN, or a sidecar proxy | upgrading Bun | Bidi streaming over HTTP/1.1 is a third thing again: impossible on **every** runtime, because HTTP/1.1 has no full duplex. That is why Connectum refuses to start rather than letting it hang -- see [Startup validation](#startup-validation). ### Not tested Stated so the table above is not read as broader than it is: * **TLS + ALPN under Bun.** Every run above was plaintext (h2c or HTTP/1.1). * **Bun below 1.1.38.** * **`Deno.serve`**, and Deno generally. * **`@connectrpc/connect-node` 1.x** (protobuf-es v1). Only 2.x was exercised, so the original `TypeError` report cannot be disproved for that generation. ## Startup validation When a registered service defines bidi-streaming methods and the effective transport is plaintext HTTP/1.1, `server.start()` rejects with a `TransportValidationError` carrying the stable code `CONNECTUM_UNSUPPORTED_STREAMING_TRANSPORT`, the affected `service.method` list, and both fixes: ```typescript const server = createServer({ services: [bidiRoutes], // no TLS + allowHTTP1 default → plaintext HTTP/1.1 }); await server.start(); // ✖ TransportValidationError [CONNECTUM_UNSUPPORTED_STREAMING_TRANSPORT]: // - acme.v1.ScannerService.StreamCodes (bidi_streaming) // Fix: allowHTTP1: false (h2c) or configure TLS. ``` Downgrade the check with `transportValidation: "warn"` (log once, start anyway) or `"off"` — for example behind an HTTP/2-terminating proxy where the bidi method is intentionally unused. ## Learn More * [Security & TLS](/en/guide/security) — TLS configuration, mTLS * [Server Configuration](/en/guide/server/configuration) — `createServer()` options --- --- url: /en/guide/security.md description: >- Choose TLS or mutual TLS and keep certificate policy at the deployment boundary. --- # Transport Security TLS encrypts the connection and authenticates the server. Mutual TLS additionally authenticates the client certificate. Neither replaces application-level identity and authorization when a request crosses a user or gateway trust boundary. ## Choose the task | Need | Canonical guide | |---|---| | Load a server key/certificate, use environment paths, or test TLS locally | [TLS configuration](/en/guide/security/tls) | | Require and validate client certificates for service-to-service traffic | [Mutual TLS](/en/guide/security/mtls) | | Decide between HTTP/1.1, h2c, and TLS/ALPN transports | [Transport matrix](/en/guide/production/transport-matrix) | | Add JWT, session, gateway, or per-method policy | [Auth and authz](/en/guide/auth) | | Find exact server TLS fields | [`CreateServerOptions`](/en/api/@connectum/core/types/interfaces/CreateServerOptions) | ```typescript const server = createServer({ services: [routes], tls: { dirPath: './keys' }, }); ``` Production certificates should be issued and rotated by the platform rather than baked into an image. Keep private keys out of source control, validate the full chain, and do not disable peer verification as a production workaround. The mTLS page owns client-certificate policy; the TLS page owns certificate loading and server transport setup. --- --- url: /en/api/@connectum/events-amqp/types/type-aliases/AmqpLifecycleEvent.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpLifecycleEvent # Type Alias: AmqpLifecycleEvent > **AmqpLifecycleEvent** = { `reconnected`: `boolean`; `type`: `"connected"`; } | { `error`: `Error`; `type`: `"disconnected"`; } | { `attempt`: `number`; `delay`: `number`; `error`: `Error`; `type`: `"reconnecting"`; } | { `error`: `Error`; `type`: `"reconnect-failed"`; } | { `attempt`: `number`; `error`: `Error`; `initial`: `boolean`; `type`: `"setup-failed"`; } | { `reason`: `string`; `type`: `"blocked"`; } | { `type`: `"unblocked"`; } Defined in: [packages/events-amqp/src/types.ts:446](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L446) Discriminated connection lifecycle event, delivered to [AmqpLifecycleCallbacks.onLifecycle](../interfaces/AmqpLifecycleCallbacks.md#onlifecycle). Exactly-once guarantees (pinned by integration tests): * `connected` fires once per successful (re)connect; `reconnected` is `false` for the initial connect and `true` after a recovery. * `disconnected` fires once per connection loss (a socket-level cut no longer double-fires via the raw `error` event — fixed in 1.3.0). * `reconnecting` fires once per scheduled retry — after the connection has been established once, and also per attempt of the bounded initial phase when [AmqpRecoveryOptions.initialConnectMaxRetries](../interfaces/AmqpRecoveryOptions.md#initialconnectmaxretries) is set. `reconnect-failed` is terminal and fires for any of its three triggers: the retry budget is exhausted (`maxRetries`), the fatal topology policy stopped the cycle (`treatTopologyErrorAsFatal`), or the initial connect budget ran out (`initialConnectMaxRetries`). * `setup-failed` reports a topology/setup failure with `initial: true` for the startup window (`attempt: 0` on the probe; the 0-based attempt index in the bounded initial phase) or `initial: false` for a reconnect re-assert (`attempt` >= 1). * `blocked`/`unblocked` surface broker flow control (RabbitMQ `connection.blocked`, e.g. under a memory/disk alarm); they have no flat callback equivalent. Scope: with amqplib's own initial loop (default), the retry loop of the INITIAL connect (broker unreachable when `connect()` is called) happens before the lifecycle wiring can attach, so its per-retry events are not surfaced; the startup probe covers the deterministic-misconfiguration case (`setup-failed { initial: true }`). Set [AmqpRecoveryOptions.initialConnectMaxRetries](../interfaces/AmqpRecoveryOptions.md#initialconnectmaxretries) (since 1.3.0) to make the adapter own that window — its bounded phase surfaces per-attempt `reconnecting`/`setup-failed` events and a terminal `reconnect-failed` on budget exhaustion. The `type` values are deliberately broker-agnostic so a future cross-adapter generalization stays non-breaking. --- --- url: /en/api/@connectum/events-amqp/types/type-aliases/AmqpTopologyMode.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpTopologyMode # Type Alias: AmqpTopologyMode > **AmqpTopologyMode** = *typeof* [`AmqpTopologyMode`](../variables/AmqpTopologyMode.md)\[keyof *typeof* [`AmqpTopologyMode`](../variables/AmqpTopologyMode.md)] Defined in: [packages/events-amqp/src/types.ts:263](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L263) Topology establishment mode. --- --- url: /en/api/@connectum/events-amqp/type-aliases/AmqpTopologyObject.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / AmqpTopologyObject # Type Alias: AmqpTopologyObject > **AmqpTopologyObject** = { `kind`: `"exchange"` | `"queue"`; `name`: `string`; } | { `destination`: `string`; `destinationType`: `"queue"` | `"exchange"`; `kind`: `"binding"`; `routingKey`: `string`; `source`: `string`; } Defined in: [packages/events-amqp/src/errors.ts:75](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/errors.ts#L75) Machine-readable identity of the topology object a declaration or verification failed on. A binding has no name of its own — it is identified by its endpoints and routing key — hence the discriminated shape. --- --- 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/main/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/main/packages/auth/src/types.ts#L67) Authorization rule effect --- --- url: /en/api/@connectum/core/type-aliases/CallOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / CallOptions # Type Alias: CallOptions > **CallOptions** = `object` Defined in: [packages/core/src/context.ts:40](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L40) Per-call overrides for [Context.call](../interfaces/Context.md#call). Every field is optional; omitted dimensions cascade from the incoming request (see the auto-injection rules on [Context.call](../interfaces/Context.md#call)). This is the Connectum catalog `CallOptions`, intentionally distinct from `@connectrpc/connect`'s client `CallOptions`. ## Properties ### endpoint? > `optional` **endpoint?**: `string` Defined in: [packages/core/src/context.ts:65](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L65) Opaque endpoint hint forwarded to the configured `remoteResolver` for services reachable at several endpoints. Ignored for locally-mounted services. *** ### headers? > `optional` **headers?**: `HeadersInit` Defined in: [packages/core/src/context.ts:59](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L59) Extra request headers. Only these explicit headers are sent; no inbound headers are auto-propagated (trace context flows implicitly via the OTel client interceptor in `outgoingInterceptors`). *** ### signal? > `optional` **signal?**: `AbortSignal` Defined in: [packages/core/src/context.ts:47](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L47) Abort signal for the outgoing call. When omitted, the incoming request's `ctx.signal` is injected, so cancelling the inbound RPC cancels every in-flight `ctx.call`. A supplied signal **replaces** the cascade (it is not linked with `ctx.signal`). *** ### timeoutMs? > `optional` **timeoutMs?**: `number` Defined in: [packages/core/src/context.ts:53](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L53) Timeout in milliseconds. When omitted, the remaining inbound deadline (`ctx.timeoutMs()`) is injected. A caller may **shorten** the deadline, never extend it (the effective value is `min(timeoutMs, remaining)`). --- --- url: /en/api/@connectum/core/type-aliases/CatalogCall.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / CatalogCall # Type Alias: CatalogCall > **CatalogCall** = <`K`>(`method`, `request`, `options?`) => `Promise`<[`ConnectumCallMap`](../interfaces/ConnectumCallMap.md)\[`K`]\[`"response"`]> Defined in: [packages/core/src/context.ts:113](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L113) The typed **unary** catalog-call surface: `call(method, request, options?)` keyed off [ConnectumCallMap](../interfaces/ConnectumCallMap.md). Shared by the handler [Context](../interfaces/Context.md) and the standalone `CatalogClient` (`createCatalogClient`) so both expose an identical, fully-typed `call`. ## Type Parameters ### K `K` *extends* keyof [`ConnectumCallMap`](../interfaces/ConnectumCallMap.md) A `"${typeName}/${Method}"` key of [ConnectumCallMap](../interfaces/ConnectumCallMap.md). ## Parameters ### method `K` ### request [`ConnectumCallMap`](../interfaces/ConnectumCallMap.md)\[`K`]\[`"request"`] ### options? [`CallOptions`](CallOptions.md) ## Returns `Promise`<[`ConnectumCallMap`](../interfaces/ConnectumCallMap.md)\[`K`]\[`"response"`]> --- --- url: /en/api/@connectum/core/type-aliases/CatalogStream.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / CatalogStream # Type Alias: CatalogStream > **CatalogStream** = <`K`>(`method`) => [`StreamReturn`](StreamReturn.md)<[`ConnectumStreamMap`](../interfaces/ConnectumStreamMap.md)\[`K`]> Defined in: [packages/core/src/context.ts:122](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L122) The typed **streaming** catalog-call surface: `stream(method)` returns a kind-specific factory keyed off [ConnectumStreamMap](../interfaces/ConnectumStreamMap.md). Shared by the handler [Context](../interfaces/Context.md) and the standalone `CatalogClient`. ## Type Parameters ### K `K` *extends* keyof [`ConnectumStreamMap`](../interfaces/ConnectumStreamMap.md) A `"${typeName}/${Method}"` key of [ConnectumStreamMap](../interfaces/ConnectumStreamMap.md). ## Parameters ### method `K` ## Returns [`StreamReturn`](StreamReturn.md)<[`ConnectumStreamMap`](../interfaces/ConnectumStreamMap.md)\[`K`]> --- --- 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:66](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L66) 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/main/packages/core/src/config/envSchema.ts#L133) Connectum environment configuration type --- --- url: /en/api/@connectum/core/type-aliases/ConnectumMethodImpl.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ConnectumMethodImpl # Type Alias: ConnectumMethodImpl\ > **ConnectumMethodImpl**<`M`> = `M` *extends* `DescMethodUnary`\ ? (`request`, `context`) => `Promise`<`MessageInitShape`<`O`>> | `MessageInitShape`<`O`> : `M` *extends* `DescMethodServerStreaming`\ ? (`request`, `context`) => `AsyncIterable`<`MessageInitShape`<`O`>> : `M` *extends* `DescMethodClientStreaming`\ ? (`requests`, `context`) => `Promise`<`MessageInitShape`<`O`>> : `M` *extends* `DescMethodBiDiStreaming`\ ? (`requests`, `context`) => `AsyncIterable`<`MessageInitShape`<`O`>> : `never` Defined in: [packages/core/src/context.ts:164](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L164) The implementation of a single RPC, receiving a Connectum [Context](../interfaces/Context.md). Mirrors `@connectrpc/connect`'s `MethodImpl` but substitutes `Context` for the raw `HandlerContext`, so `ctx.call` is visible inside handlers. ## Type Parameters ### M `M` *extends* `DescMethod` --- --- url: /en/api/@connectum/core/type-aliases/ConnectumServiceImpl.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ConnectumServiceImpl # Type Alias: ConnectumServiceImpl\ > **ConnectumServiceImpl**<`Desc`> = `{ [P in keyof Desc["method"]]: ConnectumMethodImpl }` Defined in: [packages/core/src/context.ts:182](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L182) The full implementation of a service: one [ConnectumMethodImpl](ConnectumMethodImpl.md) per method. Accepted by [defineService](../functions/defineService.md) / [defineLazyService](../functions/defineLazyService.md). Mirrors `@connectrpc/connect`'s `ServiceImpl` with the Connectum [Context](../interfaces/Context.md). ## Type Parameters ### Desc `Desc` *extends* `DescService` --- --- url: /en/api/@connectum/core/type-aliases/EffectiveTransport.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / EffectiveTransport # Type Alias: EffectiveTransport > **EffectiveTransport** = *typeof* [`EffectiveTransport`](../variables/EffectiveTransport.md)\[keyof *typeof* [`EffectiveTransport`](../variables/EffectiveTransport.md)] Defined in: [packages/core/src/TransportValidation.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L54) Effective transport resolved from `tls` + `allowHTTP1`. * `plaintext-h1` — no TLS, `allowHTTP1: true` (default): HTTP/1.1 only. * `h2c` — no TLS, `allowHTTP1: false`: plaintext HTTP/2. * `tls-h1-negotiable` — TLS, `allowHTTP1: true`: ALPN offers both; a client may negotiate HTTP/1.1 (residual bidi risk). * `tls-h2-only` — TLS, `allowHTTP1: false`: ALPN refuses HTTP/1.1. --- --- url: /en/api/@connectum/events/types/type-aliases/EventAdapterFactory.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventAdapterFactory # Type Alias: EventAdapterFactory > **EventAdapterFactory** = () => [`EventAdapter`](../interfaces/EventAdapter.md) Defined in: [packages/events/src/types.ts:148](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L148) A zero-argument factory producing a fresh [EventAdapter](../interfaces/EventAdapter.md). Used where each consumer needs its OWN broker connection — e.g. `createBroadcastSubscribers` invokes it once per reactor so every reactor bus gets an independent connection / durable consumer. DI guidance: for wiring a service, prefer injecting an `EventAdapter` INSTANCE (constructed at the composition root) — a test double with its own configuration does not fit a zero-argument factory signature without a wrapper closure. Reach for the factory only where per-consumer connections are the point. Since 1.3.0. ## Returns [`EventAdapter`](../interfaces/EventAdapter.md) --- --- url: /en/api/@connectum/events/types/type-aliases/EventMiddleware.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventMiddleware # Type Alias: EventMiddleware > **EventMiddleware** = (`event`, `ctx`, `next`) => `Promise`<`void`> Defined in: [packages/events/src/types.ts:270](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L270) Event middleware function ## Parameters ### event [`RawEvent`](../interfaces/RawEvent.md) ### ctx [`EventContext`](../interfaces/EventContext.md) ### next [`EventMiddlewareNext`](EventMiddlewareNext.md) ## Returns `Promise`<`void`> --- --- url: /en/api/@connectum/events/types/type-aliases/EventMiddlewareNext.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventMiddlewareNext # Type Alias: EventMiddlewareNext > **EventMiddlewareNext** = (`updatedEvent?`) => `Promise`<`void`> Defined in: [packages/events/src/types.ts:265](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L265) Event middleware next function. Optionally accepts an updated event to replace the current one in the pipeline (e.g., retry middleware sets a new attempt number without mutating the readonly original). ## Parameters ### updatedEvent? [`RawEvent`](../interfaces/RawEvent.md) ## Returns `Promise`<`void`> --- --- url: /en/api/@connectum/events/types/type-aliases/EventRoute.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / EventRoute # Type Alias: EventRoute > **EventRoute** = (`events`) => `void` Defined in: [packages/events/src/types.ts:252](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L252) Event route function -- mirrors ServiceRoute from @connectum/core ## Parameters ### events [`EventRouter`](../interfaces/EventRouter.md) ## Returns `void` --- --- 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/main/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/events-amqp/testing/type-aliases/FakePublishOutcome.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [testing](../index.md) / FakePublishOutcome # Type Alias: FakePublishOutcome > **FakePublishOutcome** = `"ack"` | `Error` Defined in: [packages/events-amqp/src/testing.ts:64](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/testing.ts#L64) One publish outcome: `"ack"` resolves; an `Error` rejects the publish with it. --- --- 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:62](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L62) 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/main/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/main/packages/interceptors/src/types.ts#L16) Interceptor factory function type ## Type Parameters ### TOptions `TOptions` = `void` Options type for the interceptor --- --- url: /en/api/@connectum/auth/type-aliases/InternalTrustSource.md --- [Connectum API Reference](../../../index.md) / [@connectum/auth](../index.md) / InternalTrustSource # Type Alias: InternalTrustSource > **InternalTrustSource** = (`req`) => [`AuthContext`](../interfaces/AuthContext.md) | `null` | `Promise`<[`AuthContext`](../interfaces/AuthContext.md) | `null`> Defined in: [packages/auth/src/types.ts:336](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/types.ts#L336) A pluggable internal trust source (ADR-029). Given the incoming request, returns an [AuthContext](../interfaces/AuthContext.md) for the calling service when the internal trust marker is present and valid, or `null` when it is missing/invalid. [createInternalAuthInterceptor](../functions/createInternalAuthInterceptor.md) converts `null` (and any thrown error from the trust source) into `Code.Unauthenticated`. The returned `AuthContext.subject` is the service identity; `roles`/`scopes` come from the trust source (allow-list entry or verified token claims) so the call composes with the existing `requires {roles,scopes}` authz model. ## Parameters ### req The request (read-only access to headers). #### header `Headers` ## Returns [`AuthContext`](../interfaces/AuthContext.md) | `null` | `Promise`<[`AuthContext`](../interfaces/AuthContext.md) | `null`> AuthContext for a trusted internal caller, or null to reject. --- --- 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:166](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L166) 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/main/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 ### internal > **internal**: `boolean` Mark the method as internal (service-to-service). Skips end-user (JWT) authentication, but requires an internal trust marker established by createInternalAuthInterceptor (a per-service trust source). Distinct from `public`: `public` is world-open (no auth at all); `internal` is reachable only by a trusted internal caller. A method is at most one of `public` / `internal` / gated. See ADR-029. #### Generated from field: optional bool internal = 4; ### 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:254](https://github.com/Connectum-Framework/connectum/blob/main/packages/interceptors/src/types.ts#L254) 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:22](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L22) 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:25](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L25) 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/main/packages/otel/src/types.ts#L24) Filter callback to exclude specific attributes from spans/metrics ## Parameters ### key `string` Attribute key ### value `string` | `number` | `boolean` Attribute value ## 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/main/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/events/types/type-aliases/RawEventHandler.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / RawEventHandler # Type Alias: RawEventHandler > **RawEventHandler** = (`event`, `ack`, `nack`) => `Promise`<`void`> Defined in: [packages/events/src/types.ts:38](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L38) Raw event handler function type. Adapters call this with the deserialized event and broker-specific ack/nack callbacks. The EventBus wires these into the EventContext for end-user handlers. ## Parameters ### event [`RawEvent`](../interfaces/RawEvent.md) ### ack () => `Promise`<`void`> ### nack (`requeue?`) => `Promise`<`void`> ## Returns `Promise`<`void`> --- --- url: /en/api/@connectum/core/type-aliases/RemoteResolver.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / RemoteResolver # Type Alias: RemoteResolver > **RemoteResolver** = (`ctx`) => `Transport` | `null` Defined in: [packages/core/src/remoteResolver.ts:40](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/remoteResolver.ts#L40) Resolve a remote service to a `Transport`, or `null` if there is no route. Synchronous by contract — see the module note. ## Parameters ### ctx [`ResolverContext`](../interfaces/ResolverContext.md) ## Returns `Transport` | `null` --- --- 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:148](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L148) 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:81 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; ### internal > **internal**: `boolean` Mark all methods in the service as internal (service-to-service). Skips end-user (JWT) authentication, but requires an internal trust marker established by createInternalAuthInterceptor. Method-level `internal` overrides this. See ADR-029. #### Generated from field: optional bool internal = 4; ### 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/type-aliases/ServiceCatalog.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ServiceCatalog # Type Alias: ServiceCatalog > **ServiceCatalog** = `Readonly`<`Record`<`string`, `DescService`>> Defined in: [packages/core/src/serviceCatalog.ts:20](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/serviceCatalog.ts#L20) A readonly registry mapping a proto service `typeName` (e.g. `"orders.v1.OrdersService"`) to its `DescService` descriptor. --- --- url: /en/api/@connectum/events/types/type-aliases/ServiceEventHandlers.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / ServiceEventHandlers # Type Alias: ServiceEventHandlers\ > **ServiceEventHandlers**<`S`> = { \[K in keyof S\["method"]]: TypedEventHandler\> | EventHandlerConfig\> } Defined in: [packages/events/src/types.ts:220](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L220) Maps service methods to typed event handlers. Each handler can be either: * A simple function (uses global middleware) * An object with `handler` and optional `middleware` (per-handler override) ## Type Parameters ### S `S` *extends* `DescService` --- --- url: /en/api/@connectum/core/type-aliases/ServiceOptions.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / ServiceOptions # Type Alias: ServiceOptions > **ServiceOptions** = `NonNullable`<`Parameters`<`ConnectRouter`\[`"service"`]>\[`2`]> Defined in: [packages/core/src/defineService.ts:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/defineService.ts#L28) Per-service handler options forwarded to ConnectRPC's `router.service()` — e.g. per-service `interceptors` (applied to every method of this service) and `jsonOptions`. Derived from the underlying `ConnectRouter.service` signature so it always matches the installed `@connectrpc/connect`. --- --- url: >- /en/api/@connectum/healthcheck/@connectum/healthcheck/types/type-aliases/ServingStatus.md --- [Connectum API Reference](../../../../../../index.md) / [@connectum/healthcheck](../../../../index.md) / [@connectum/healthcheck/types](../index.md) / ServingStatus # Type Alias: ServingStatus > **ServingStatus** = `HealthCheckResponse_ServingStatus` Defined in: [types.ts:14](https://github.com/Connectum-Framework/connectum/blob/main/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:40](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L40) Shutdown hook function type A function called during graceful shutdown. May be synchronous or async. ## Returns `void` | `Promise`<`void`> --- --- url: /en/api/@connectum/core/type-aliases/StreamReturn.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / StreamReturn # Type Alias: StreamReturn\ > **StreamReturn**<`E`> = `E` *extends* `object` ? (`request`, `options?`) => `AsyncIterable`<`Res`> : `E` *extends* `object` ? (`options?`) => [`ClientStreamHandle`](../interfaces/ClientStreamHandle.md)<`Req`, `Res`> : `E` *extends* `object` ? (`options?`) => [`BidiStreamHandle`](../interfaces/BidiStreamHandle.md)<`Req`, `Res`> : `never` Defined in: [packages/core/src/context.ts:97](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/context.ts#L97) Maps a [ConnectumStreamMap](../interfaces/ConnectumStreamMap.md) entry to the ergonomic shape returned by [Context.stream](../interfaces/Context.md#stream), discriminated by the entry's `kind`. ## Type Parameters ### E `E` --- --- 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:28](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L28) Underlying transport server — HTTP/1.1, HTTP/2 plaintext, or HTTP/2 TLS --- --- url: /en/api/@connectum/core/type-aliases/TransportValidationMode.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / TransportValidationMode # Type Alias: TransportValidationMode > **TransportValidationMode** = *typeof* [`TransportValidationMode`](../variables/TransportValidationMode.md)\[keyof *typeof* [`TransportValidationMode`](../variables/TransportValidationMode.md)] Defined in: [packages/core/src/TransportValidation.ts:37](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L37) Validation severity. --- --- url: /en/api/@connectum/events/types/type-aliases/TypedEventHandler.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events](../../index.md) / [types](../index.md) / TypedEventHandler # Type Alias: TypedEventHandler\ > **TypedEventHandler**<`I`> = (`event`, `ctx`) => `Promise`<`void`> Defined in: [packages/events/src/types.ts:197](https://github.com/Connectum-Framework/connectum/blob/main/packages/events/src/types.ts#L197) Typed event handler for a specific message type ## Type Parameters ### I `I` ## Parameters ### event `I` ### ctx [`EventContext`](../interfaces/EventContext.md) ## Returns `Promise`<`void`> --- --- 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) * [EventBusLike](interfaces/EventBusLike.md) * [ProtocolContext](interfaces/ProtocolContext.md) * [ProtocolRegistration](interfaces/ProtocolRegistration.md) * [Server](interfaces/Server.md) * [ServerClientOptions](interfaces/ServerClientOptions.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) * [ShutdownHook](type-aliases/ShutdownHook.md) * [TransportServer](type-aliases/TransportServer.md) ## Variables * [LifecycleEvent](variables/LifecycleEvent.md) * [ServerState](variables/ServerState.md) --- --- url: /en/api/@connectum/events-amqp/types.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-amqp](../index.md) / types # types Configuration types for the AMQP/RabbitMQ adapter. ## Interfaces * [AmqpAdapterOptions](interfaces/AmqpAdapterOptions.md) * [AmqpBindingDeclaration](interfaces/AmqpBindingDeclaration.md) * [AmqpConsumerOptions](interfaces/AmqpConsumerOptions.md) * [AmqpExchangeDeclaration](interfaces/AmqpExchangeDeclaration.md) * [AmqpExchangeOptions](interfaces/AmqpExchangeOptions.md) * [AmqpLifecycleCallbacks](interfaces/AmqpLifecycleCallbacks.md) * [AmqpPublisherOptions](interfaces/AmqpPublisherOptions.md) * [AmqpPublishRetryOptions](interfaces/AmqpPublishRetryOptions.md) * [AmqpQueueDeclaration](interfaces/AmqpQueueDeclaration.md) * [AmqpQueueOptions](interfaces/AmqpQueueOptions.md) * [AmqpQueueOverride](interfaces/AmqpQueueOverride.md) * [AmqpRecoveryOptions](interfaces/AmqpRecoveryOptions.md) * [AmqpSerializationOptions](interfaces/AmqpSerializationOptions.md) * [AmqpTopology](interfaces/AmqpTopology.md) ## Type Aliases * [AmqpLifecycleEvent](type-aliases/AmqpLifecycleEvent.md) * [AmqpTopologyMode](type-aliases/AmqpTopologyMode.md) ## Variables * [AmqpTopologyMode](variables/AmqpTopologyMode.md) --- --- url: /en/api/@connectum/events-kafka/types.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-kafka](../index.md) / types # types Configuration types for the Kafka event adapter. ## Interfaces * [KafkaAdapterOptions](interfaces/KafkaAdapterOptions.md) --- --- url: /en/api/@connectum/events-nats/types.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-nats](../index.md) / types # types Configuration types for the NATS JetStream adapter. ## Interfaces * [NatsAdapterOptions](interfaces/NatsAdapterOptions.md) * [NatsConsumerOptions](interfaces/NatsConsumerOptions.md) --- --- url: /en/api/@connectum/events-redis/types.md --- [Connectum API Reference](../../../index.md) / [@connectum/events-redis](../index.md) / types # types Configuration types for the Redis Streams adapter. ## Interfaces * [RedisAdapterOptions](interfaces/RedisAdapterOptions.md) * [RedisBrokerOptions](interfaces/RedisBrokerOptions.md) --- --- url: /en/api/@connectum/events/types.md --- [Connectum API Reference](../../../index.md) / [@connectum/events](../index.md) / types # types Type definitions for the event adapter layer. ## Interfaces * [AdapterContext](interfaces/AdapterContext.md) * [DlqOptions](interfaces/DlqOptions.md) * [EventAdapter](interfaces/EventAdapter.md) * [EventBus](interfaces/EventBus.md) * [EventBusOptions](interfaces/EventBusOptions.md) * [EventContext](interfaces/EventContext.md) * [EventContextInit](interfaces/EventContextInit.md) * [EventHandlerConfig](interfaces/EventHandlerConfig.md) * [EventRouteEntry](interfaces/EventRouteEntry.md) * [EventRouter](interfaces/EventRouter.md) * [EventSubscription](interfaces/EventSubscription.md) * [MiddlewareConfig](interfaces/MiddlewareConfig.md) * [PublishOptions](interfaces/PublishOptions.md) * [RawEvent](interfaces/RawEvent.md) * [RawSubscribeOptions](interfaces/RawSubscribeOptions.md) * [RetryOptions](interfaces/RetryOptions.md) ## Type Aliases * [EventAdapterFactory](type-aliases/EventAdapterFactory.md) * [EventMiddleware](type-aliases/EventMiddleware.md) * [EventMiddlewareNext](type-aliases/EventMiddlewareNext.md) * [EventRoute](type-aliases/EventRoute.md) * [RawEventHandler](type-aliases/RawEventHandler.md) * [ServiceEventHandlers](type-aliases/ServiceEventHandlers.md) * [TypedEventHandler](type-aliases/TypedEventHandler.md) --- --- url: /en/api/@connectum/test-fixtures/types.md --- [Connectum API Reference](../../../index.md) / [@connectum/test-fixtures](../index.md) / types # types Type definitions for @connectum/test-fixtures. ## Interfaces * [FakeMethodOptions](interfaces/FakeMethodOptions.md) * [FakeServiceOptions](interfaces/FakeServiceOptions.md) * [MockDescFieldOptions](interfaces/MockDescFieldOptions.md) * [MockDescMessageOptions](interfaces/MockDescMessageOptions.md) * [MockDescMethodOptions](interfaces/MockDescMethodOptions.md) * [MockNextOptions](interfaces/MockNextOptions.md) * [MockRequestOptions](interfaces/MockRequestOptions.md) * [MockStreamOptions](interfaces/MockStreamOptions.md) --- --- url: /en/api/@connectum/testing/types.md --- [Connectum API Reference](../../../index.md) / [@connectum/testing](../index.md) / types # types Type definitions for @connectum/testing. Mock/fixture option types live in `@connectum/test-fixtures` and are re-exported from this module for backwards compatibility. ## Interfaces * [CreateTestServerOptions](interfaces/CreateTestServerOptions.md) * [TestServer](interfaces/TestServer.md) ## References ### FakeMethodOptions Re-exports [FakeMethodOptions](../index/interfaces/FakeMethodOptions.md) *** ### FakeServiceOptions Re-exports [FakeServiceOptions](../index/interfaces/FakeServiceOptions.md) *** ### MockDescFieldOptions Re-exports [MockDescFieldOptions](../index/interfaces/MockDescFieldOptions.md) *** ### MockDescMessageOptions Re-exports [MockDescMessageOptions](../index/interfaces/MockDescMessageOptions.md) *** ### MockDescMethodOptions Re-exports [MockDescMethodOptions](../index/interfaces/MockDescMethodOptions.md) *** ### MockNextOptions Re-exports [MockNextOptions](../index/interfaces/MockNextOptions.md) *** ### MockRequestOptions Re-exports [MockRequestOptions](../index/interfaces/MockRequestOptions.md) *** ### MockStreamOptions Re-exports [MockStreamOptions](../index/interfaces/MockStreamOptions.md) --- --- url: /en/guide/typescript.md description: >- Understand Connectum's compiled packages and the application syntax used for direct TypeScript execution. --- # TypeScript Native TypeScript execution via type stripping on Node.js 25+. Packages compile to JS + DTS via tsup for any runtime (Node.js 22+, 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` | | **Import extensions** | Relative source imports use `.ts`; generated imports match `buf.gen.yaml` | | **`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 * [Execution Models](/en/guide/typescript/runtime-support) -- native Node.js, Bun, and tsx workflows * [Runtime Compatibility](/en/guide/runtime-compatibility) -- canonical supported-version and limitation matrix * [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/guide/typescript/runtime-support.md description: >- Choose native Node.js, Bun, or tsx for application TypeScript without duplicating the runtime support matrix. --- # TypeScript Execution Models All `@connectum/*` packages ship compiled ESM JavaScript, declarations, and source maps. Your application can therefore choose how to execute its own TypeScript without loading Connectum through a custom register hook. For supported versions, feature coverage, and known limitations, use the canonical [Runtime Compatibility](/en/guide/runtime-compatibility) matrix. ## Native Node.js Type Stripping The current development baseline can execute erasable TypeScript directly: ```bash node src/index.ts node --watch src/index.ts ``` This path requires the syntax and import rules in [Erasable Syntax](/en/guide/typescript/erasable-syntax). `tsc --noEmit` still performs type checking; Node.js removes types but does not type-check the program. ## Bun Bun can execute the same application sources directly: ```bash bun src/index.ts bun --watch src/index.ts ``` Use runtime variants in task guides where a command or limitation genuinely differs. Do not assume Node.js-only OpenTelemetry auto-instrumentation works under Bun; check [Runtime Compatibility](/en/guide/runtime-compatibility#otel). ## tsx on the Consumer Node.js Line Applications on the supported consumer Node.js line can execute TypeScript with tsx instead of relying on native type stripping: ```bash npx tsx src/index.ts npx tsx watch src/index.ts ``` Install `tsx` as a development dependency for repeatable project scripts. This choice changes how application source is executed; it does not change the compiled format of Connectum packages. ## Choose an Execution Model | Need | Use | |---|---| | Match Connectum's native-TypeScript development workflow | Native Node.js type stripping | | Run and test the application on Bun | Bun, after checking the compatibility matrix | | Stay on the consumer Node.js line while executing TypeScript source | tsx | | Publish a compiled application artifact | Your normal ESM build pipeline | Generated import extensions must match the project's `buf.gen.yaml` and execution model. The Quickstart executes generated TypeScript directly and therefore uses `.ts`; compiled distributions normally generate imports for their emitted `.js`. ## Related * [Runtime Compatibility](/en/guide/runtime-compatibility) — supported versions and limitations * [Erasable Syntax](/en/guide/typescript/erasable-syntax) — native type-stripping constraints * [Proto Enums](/en/guide/typescript/proto-enums) — generation when enums require transformation * [Patterns and Workflow](/en/guide/typescript/patterns) — project conventions --- --- url: /en/migration.md description: >- Determine whether a Connectum upgrade requires changes and follow focused migration guides. --- # Upgrade and Migration The documentation describes the **{{ site.documentedVersion }}** release line. Check every installed `@connectum/*` version before applying a migration; packages can use different compatible release lines, so verify the complete set rather than inferring it from one package. ::: code-group ```bash [npm] npm ls '@connectum/*' ``` ```bash [pnpm] pnpm list '@connectum/*' ``` ```bash [bun] bun pm ls ``` ::: ## Current Release Line The portal may document the next release line before every package on that line is published. Use the package-specific [GitHub releases](https://github.com/Connectum-Framework/connectum/releases) and the [Runtime Compatibility](/en/guide/runtime-compatibility) matrix to decide whether an upgrade applies to your installed set. If you are upgrading from a pre-1.0 or 1.0 installation, review every applicable action below rather than assuming a direct upgrade to the documented line. ## Required Actions by Starting Version | Installed version | Required action | |---|---| | 1.2.x | Review the 1.3 release notes for each installed package and run your existing tests | | 1.1.x | Review the 1.2 and 1.3 release notes for each installed package and run your existing tests | | 1.0.x | Review package release notes; 1.1 capabilities are additive | | RC or alpha | Follow [Migrating to 1.0](/en/migration/1.0), then review the [Service Catalog migration](/en/migration/service-catalog) | ## Focused Migrations * [Migrating to 1.0](/en/migration/1.0) — Node.js floor, explicit resilience, streaming transport validation, and removed EventBus `sync` option. * [Migrating to the Service Catalog](/en/migration/service-catalog) — replace legacy service registration and manual client routing. ## Release History Full release narratives, pull requests, and package-specific dependency updates belong in [Connectum GitHub releases](https://github.com/Connectum-Framework/connectum/releases) and package changelogs. This page keeps only information needed to decide and complete an upgrade. ## Legacy Anchors The following aliases preserve links published by the former combined changelog. They now route readers to the focused migration or public release history. Continue with [Migrating to 1.0](/en/migration/1.0) for required changes or the [release history](https://github.com/Connectum-Framework/connectum/releases) for non-actionable detail. --- --- 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. **Outcome:** add constraints to a request message, regenerate code, and verify that an invalid RPC is rejected before its handler runs. The generated [`@connectum/interceptors` API](/en/api/@connectum/interceptors/) remains the exact symbol reference. ## 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. ```mermaid flowchart LR Client[Client] --> Error[errorHandler] Error --> More["..."] More --> Validation[validation] Validation --> Serializer[serializer] Serializer --> Handler[Handler] Validation -->|Invalid request| Rejected[INVALID_ARGUMENT] ``` Validation runs as the 7th interceptor in the default chain (before serializer, after any explicitly enabled resilience interceptors). Invalid requests are rejected with `INVALID_ARGUMENT` before reaching the handler. ## Setup Install the required packages: ::: pm \== npm ```bash npm install @bufbuild/protovalidate @connectrpc/validate ``` \== pnpm ```bash pnpm add @bufbuild/protovalidate @connectrpc/validate ``` \== bun ```bash bun add @bufbuild/protovalidate @connectrpc/validate ``` ::: The proto dependency is declared separately, in `buf.yaml` (below). 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/events-amqp/types/variables/AmqpTopologyMode.md --- [Connectum API Reference](../../../../index.md) / [@connectum/events-amqp](../../index.md) / [types](../index.md) / AmqpTopologyMode # Variable: AmqpTopologyMode > `const` **AmqpTopologyMode**: `object` Defined in: [packages/events-amqp/src/types.ts:263](https://github.com/Connectum-Framework/connectum/blob/main/packages/events-amqp/src/types.ts#L263) Topology establishment mode. ## Type Declaration ### ASSERT > `readonly` **ASSERT**: `"assert"` = `"assert"` ### CHECK > `readonly` **CHECK**: `"check"` = `"check"` ### SKIP > `readonly` **SKIP**: `"skip"` = `"skip"` --- --- url: /en/api/@connectum/otel/attributes/variables/ATTR_CONNECTUM_TRANSPORT.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_CONNECTUM\_TRANSPORT # Variable: ATTR\_CONNECTUM\_TRANSPORT > `const` **ATTR\_CONNECTUM\_TRANSPORT**: `"connectum.transport"` = `"connectum.transport"` Defined in: [packages/otel/src/attributes.ts:43](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L43) Connectum-specific span attribute that distinguishes RPC observations carried by the in-process router transport from those carried by HTTP/2. Values: * `"in-process"` — the call traversed `createLocalTransport` * `"http"` — the call traversed `createGrpcTransport` / `createConnectTransport` (the network path) Parity tests strip this attribute before structural diffing so that the remaining shape (spans, events, metric instruments) is invariant across transports. ## See ATTR\_CONNECTUM\_TRANSPORT\_METRIC for the metric-label counterpart --- --- url: >- /en/api/@connectum/otel/attributes/variables/ATTR_CONNECTUM_TRANSPORT_METRIC.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / ATTR\_CONNECTUM\_TRANSPORT\_METRIC # Variable: ATTR\_CONNECTUM\_TRANSPORT\_METRIC > `const` **ATTR\_CONNECTUM\_TRANSPORT\_METRIC**: `"transport"` = `"transport"` Defined in: [packages/otel/src/attributes.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L50) Metric-label counterpart of [ATTR\_CONNECTUM\_TRANSPORT](ATTR_CONNECTUM_TRANSPORT.md). Uses the short, lowercase form to align with OpenTelemetry metric label conventions and existing `network.*` keys. --- --- 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/main/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/main/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/main/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/main/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/main/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/main/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:59](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L59) --- --- 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:58](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L58) --- --- 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:60](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L60) --- --- 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/main/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/main/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/main/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/main/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/main/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/main/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:87](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/context.ts#L87) Process-wide AsyncLocalStorage for auth context. Uses globalThis + Symbol.for() to guarantee singleton even when the module is evaluated multiple times (e.g., mixed src/dist imports in dev). 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/main/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"` | `"yes"` | `"false"` | `"no"`>> Defined in: [packages/core/src/config/envSchema.ts:35](https://github.com/Connectum-Framework/connectum/blob/main/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:66](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L66) 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:90](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L90) Reverse map: numeric code -> string name for span attributes --- --- url: >- /en/api/@connectum/otel/attributes/variables/CONNECTUM_INTERNAL_TRANSPORT_HEADER.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / CONNECTUM\_INTERNAL\_TRANSPORT\_HEADER # Variable: CONNECTUM\_INTERNAL\_TRANSPORT\_HEADER > `const` **CONNECTUM\_INTERNAL\_TRANSPORT\_HEADER**: `"connectum-internal-transport"` = `"connectum-internal-transport"` Defined in: [packages/otel/src/attributes.ts:52](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L52) Marker request header set by `createLocalTransport` from `@connectum/core`. --- --- url: >- /en/api/@connectum/otel/attributes/variables/CONNECTUM_INTERNAL_TRANSPORT_IN_PROCESS.md --- [Connectum API Reference](../../../../index.md) / [@connectum/otel](../../index.md) / [attributes](../index.md) / CONNECTUM\_INTERNAL\_TRANSPORT\_IN\_PROCESS # Variable: CONNECTUM\_INTERNAL\_TRANSPORT\_IN\_PROCESS > `const` **CONNECTUM\_INTERNAL\_TRANSPORT\_IN\_PROCESS**: `"in-process"` = `"in-process"` Defined in: [packages/otel/src/attributes.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L54) Header value indicating an in-process call (the only one currently defined). --- --- 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"` | `"yes"` | `"false"` | `"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"` | `"yes"` | `"false"` | `"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/main/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/core/variables/defaultPropagateHeaders.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / defaultPropagateHeaders # Variable: defaultPropagateHeaders > `const` **defaultPropagateHeaders**: readonly `string`\[] Defined in: [packages/core/src/propagateHeaders.ts:30](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/propagateHeaders.ts#L30) Recommended default allow-list: W3C trace-context headers only. Trace headers let a downstream call continue the inbound trace even without an OpenTelemetry SDK. When the `@connectum/otel` client interceptor is also mounted in `outgoingInterceptors`, it overwrites `traceparent` with the active span's context (so the OTel value wins — no conflicting double value). `authorization` is intentionally excluded: forwarding credentials is a deliberate, security-sensitive choice the caller must opt into explicitly. --- --- url: /en/api/@connectum/core/variables/EffectiveTransport.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / EffectiveTransport # Variable: EffectiveTransport > `const` **EffectiveTransport**: `object` Defined in: [packages/core/src/TransportValidation.ts:54](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L54) Effective transport resolved from `tls` + `allowHTTP1`. * `plaintext-h1` — no TLS, `allowHTTP1: true` (default): HTTP/1.1 only. * `h2c` — no TLS, `allowHTTP1: false`: plaintext HTTP/2. * `tls-h1-negotiable` — TLS, `allowHTTP1: true`: ALPN offers both; a client may negotiate HTTP/1.1 (residual bidi risk). * `tls-h2-only` — TLS, `allowHTTP1: false`: ALPN refuses HTTP/1.1. ## Type Declaration ### H2C > `readonly` **H2C**: `"h2c"` = `"h2c"` ### PLAINTEXT\_H1 > `readonly` **PLAINTEXT\_H1**: `"plaintext-h1"` = `"plaintext-h1"` ### TLS\_H1\_NEGOTIABLE > `readonly` **TLS\_H1\_NEGOTIABLE**: `"tls-h1-negotiable"` = `"tls-h1-negotiable"` ### TLS\_H2\_ONLY > `readonly` **TLS\_H2\_ONLY**: `"tls-h2-only"` = `"tls-h2-only"` --- --- 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/main/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/@connectum/healthcheck/variables/healthcheckManager.md --- [Connectum API Reference](../../../../../index.md) / [@connectum/healthcheck](../../../index.md) / [@connectum/healthcheck](../index.md) / healthcheckManager # Variable: healthcheckManager > `const` **healthcheckManager**: [`HealthcheckManager`](../classes/HealthcheckManager.md) Defined in: [Healthcheck.ts:41](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/Healthcheck.ts#L41) 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:166](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L166) 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/main/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/main/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/main/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:121 ## 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:75 Describes the message connectum.auth.v1.MethodAuth. Use `create(MethodAuthSchema)` to create a new message. --- --- url: /en/api/@connectum/testing/index/variables/MOCK_RESPONSE_HEADER.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / MOCK\_RESPONSE\_HEADER # Variable: MOCK\_RESPONSE\_HEADER > `const` **MOCK\_RESPONSE\_HEADER**: `"x-connectum-mock"` = `"x-connectum-mock"` Defined in: [testing/src/mockResolver.ts:19](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/mockResolver.ts#L19) Response header set on every mock-served response. --- --- 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/main/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:113](https://github.com/Connectum-Framework/connectum/blob/main/packages/cli/src/commands/proto-sync.ts#L113) 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:57](https://github.com/Connectum-Framework/connectum/blob/main/packages/otel/src/attributes.ts#L57) --- --- 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/main/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:148](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/types.ts#L148) 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:125 ## 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:117 Describes the message connectum.auth.v1.ServiceAuth. Use `create(ServiceAuthSchema)` to create a new message. --- --- url: >- /en/api/@connectum/healthcheck/@connectum/healthcheck/types/variables/ServingStatus.md --- [Connectum API Reference](../../../../../../index.md) / [@connectum/healthcheck](../../../../index.md) / [@connectum/healthcheck/types](../index.md) / ServingStatus # Variable: ServingStatus > `const` **ServingStatus**: *typeof* `HealthCheckResponse_ServingStatus` = `HealthCheckResponse_ServingStatus` Defined in: [types.ts:14](https://github.com/Connectum-Framework/connectum/blob/main/packages/healthcheck/src/types.ts#L14) Service serving status Re-export generated const from proto. --- --- url: /en/api/@connectum/auth/testing/variables/TEST_JWT_KID.md --- [Connectum API Reference](../../../../index.md) / [@connectum/auth](../../index.md) / [testing](../index.md) / TEST\_JWT\_KID # Variable: TEST\_JWT\_KID > `const` **TEST\_JWT\_KID**: `"connectum-test-key"` = `"connectum-test-key"` Defined in: [packages/auth/src/testing/test-jwt-rs256.ts:50](https://github.com/Connectum-Framework/connectum/blob/main/packages/auth/src/testing/test-jwt-rs256.ts#L50) Default `kid` shared by the generated keypair and the minted tokens. --- --- 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/main/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/main/packages/core/src/TLSConfig.ts#L63) Exported for backward compatibility --- --- url: /en/api/@connectum/testing/index/variables/TRANSPORT_METRIC_ATTRIBUTE.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / TRANSPORT\_METRIC\_ATTRIBUTE # Variable: TRANSPORT\_METRIC\_ATTRIBUTE > `const` **TRANSPORT\_METRIC\_ATTRIBUTE**: `"transport"` = `"transport"` Defined in: [testing/src/otel-collectors.ts:29](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L29) Metric attribute key produced by `@connectum/otel` to distinguish transports. --- --- url: /en/api/@connectum/testing/index/variables/TRANSPORT_SPAN_ATTRIBUTE.md --- [Connectum API Reference](../../../../index.md) / [@connectum/testing](../../index.md) / [index](../index.md) / TRANSPORT\_SPAN\_ATTRIBUTE # Variable: TRANSPORT\_SPAN\_ATTRIBUTE > `const` **TRANSPORT\_SPAN\_ATTRIBUTE**: `"connectum.transport"` = `"connectum.transport"` Defined in: [testing/src/otel-collectors.ts:27](https://github.com/Connectum-Framework/connectum/blob/main/packages/testing/src/otel-collectors.ts#L27) Span attribute key produced by `@connectum/otel` to distinguish transports. --- --- url: /en/api/@connectum/core/variables/TRANSPORT_VALIDATION_ERROR_CODE.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / TRANSPORT\_VALIDATION\_ERROR\_CODE # Variable: TRANSPORT\_VALIDATION\_ERROR\_CODE > `const` **TRANSPORT\_VALIDATION\_ERROR\_CODE**: `"CONNECTUM_UNSUPPORTED_STREAMING_TRANSPORT"` = `"CONNECTUM_UNSUPPORTED_STREAMING_TRANSPORT"` Defined in: [packages/core/src/TransportValidation.ts:34](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L34) Stable error code for the streaming-vs-transport startup diagnostic. Searchable in logs and docs. --- --- url: /en/api/@connectum/core/variables/TransportValidationMode.md --- [Connectum API Reference](../../../index.md) / [@connectum/core](../index.md) / TransportValidationMode # Variable: TransportValidationMode > `const` **TransportValidationMode**: `object` Defined in: [packages/core/src/TransportValidation.ts:37](https://github.com/Connectum-Framework/connectum/blob/main/packages/core/src/TransportValidation.ts#L37) Validation severity. ## Type Declaration ### ERROR > `readonly` **ERROR**: `"error"` = `"error"` ### OFF > `readonly` **OFF**: `"off"` = `"off"` ### WARN > `readonly` **WARN**: `"warn"` = `"warn"` --- --- url: /en/guide/about.md description: A concise mental model for the Connectum framework and where to begin. --- # What Is Connectum? Connectum is a modular framework for gRPC and ConnectRPC microservices on Node.js. It standardizes the service runtime—registration, lifecycle, middleware, protocols, and shutdown—while keeping security, communication, observability, and broker integrations explicit. The framework is for teams that want consistent production behavior across services without adopting an application platform that hides transport and middleware decisions. ## Mental Model Every service begins with three things: 1. **A proto contract** defines messages and RPC methods. 2. **A service implementation** connects generated descriptors to typed handlers. 3. **`createServer()`** composes services, protocols, interceptors, and lifecycle policy. Capabilities are modules around that core: * Interceptors protect the request path with error handling, validation, and explicitly enabled resilience. * Auth modules establish identity and authorization context. * The service catalog and EventBus connect services synchronously or asynchronously. * Health, reflection, and OpenTelemetry make services inspectable and operable. ## What Connectum Owns | Concern | Connectum responsibility | |---|---| | Service runtime | Server creation, registration, lifecycle events, shutdown, TLS | | Request pipeline | Ordered ConnectRPC interceptors and method filtering | | Contracts | Proto-first validation and generated service/catalog types | | Communication | Typed service catalog calls and pluggable event brokers | | Security | Authentication, authorization, context propagation, TLS/mTLS | | Operations | Health/readiness, reflection, traces, metrics, and logs | | Tooling | Scaffolding, service generation, contract sync, and test utilities | Connectum does not provide an ORM, frontend framework, or CommonJS build. Public packages ship compiled ESM for Node.js `>=22.13.0`; direct TypeScript execution requirements are documented in [Runtime Compatibility](/en/guide/runtime-compatibility). ## Choose Your Path {#choose-your-path} * New service: [Build Your First Connectum Service](/en/guide/quickstart) * Server behavior: [Server](/en/guide/server) * Validation and middleware: [Interceptors](/en/guide/interceptors) * Service-to-service design: [Choosing a Communication Mechanism](/en/guide/service-communication/choosing-a-mechanism) * Authentication and authorization: [Auth and Authz](/en/guide/auth) * Production signals: [Observability](/en/guide/observability) * Deployment: [Docker](/en/guide/production/docker) or [Kubernetes](/en/guide/production/kubernetes) * Exact interfaces: [API and Reference](/en/reference/) ## Architecture and Boundaries {#architecture-overview} Package dependency layers prevent capability modules from becoming an implicit monolith. They are an implementation constraint, not the primary way readers choose packages. See [Connectum Runtime Architecture](/en/guide/production/architecture) for the process boundary and extension seams, and the [ADR index](/en/contributing/adr/) for package-decomposition rationale. ## Non-Goals * Managing application databases or domain models * Replacing deployment platforms or service meshes * Hiding transport, auth, or resilience policy behind implicit defaults * Supporting CommonJS or legacy Node.js runtimes ## External Resources * [ConnectRPC documentation](https://connectrpc.com/docs) * [OpenTelemetry for JavaScript](https://opentelemetry.io/docs/languages/js/) * [gRPC health checking protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) * [Protovalidate](https://github.com/bufbuild/protovalidate)