LAB429/ Cheetah product page ↗

Cheetah / Cheetah documentation

Compose the server

Server composition has three separate decisions: choose compatible implementations, create the roles that consume them, and integrate those roles into the host application's network and lifecycle.

Server composition from a component family through role factories into the host application.

A preset selects implementations. Role factories select responsibilities. The host owns ports, route mounting, startup, readiness, and shutdown.

Choose a component family

create_dev_components() returns a coherent in-memory family for one-process development and tests. It includes authentication, registry, dispatcher, correlation, history, deduplication, notification, tracing, and a parser registry. Selected topology registries can be enabled when the application needs worker contexts.

create_redis_components() provides cross-process implementations for presence, dispatch, correlation, notification, history, deduplication, contexts, and browser-window topology. The caller must supply a Redis client, authentication, a stable deployment ID, and any parser registry. All participating processes use the same deployment ID and compatible backing services; each process receives a unique node ID.

create_production_components() builds on the Redis family and rejects selected unsafe inputs. It requires a deployment ID, rejects development authentication when explicitly provided, and retains a strict-transport requirement for component-set-aware WebSocket composition. It is a guarded component baseline, not a complete production environment.

For mutually untrusted tenants sharing infrastructure, use the dedicated shared-tenancy constructor. It performs a fail-closed component audit and returns a restricted application-facing surface. Do not assemble ordinary nodes and assume that adding a tenant field later provides equivalent isolation. The accepted component set and current exclusions are defined in Shared-tenancy composition.

Create the roles from the same family

The development shape can be assembled as follows:

from servercheetah.presets import create_dev_components
from servercheetah.servers import (
    AppNodeConfig,
    RestNodeConfig,
    WebSocketNodeConfig,
    create_app_node,
    create_rest_node,
    create_websocket_node,
)
from servercheetah.types.messages import TransportSecurityMode

components = create_dev_components(
    node_id="development-1",
    api_keys={"development-key": "development-user"},
)

websocket_node = create_websocket_node(
    auth=components.auth,
    registry=components.registry,
    correlator=components.correlator,
    tracer=components.tracer,
    dispatcher=components.dispatcher,
    node_id=components.node_id,
    config=WebSocketNodeConfig(
        rest_message_endpoint="http://127.0.0.1:8080/api/ingest",
        transport_security_mode=TransportSecurityMode.debug_insecure,
    ),
)

rest_node = create_rest_node(
    registry=components.registry,
    deduplicator=components.deduplicator,
    history=components.history,
    correlator=components.correlator,
    tracer=components.tracer,
    response_notifier=components.response_notifier,
    config=RestNodeConfig(),
)

app_node = create_app_node(
    registry=components.registry,
    dispatcher=components.dispatcher,
    correlator=components.correlator,
    history=components.history,
    tracer=components.tracer,
    response_notifier=components.response_notifier,
    config=AppNodeConfig(),
)

This uses explicit insecure-development transport and a fixed development credential. Use it only on a controlled local machine. A real deployment uses strict transport and its real identity boundary.

The role dependencies reflect ownership. The WebSocket role needs authentication, registry, dispatch, correlation, and tracing. The REST role needs registry, deduplication, history, correlation, and tracing. The App role needs registry, dispatch, correlation, history, and tracing. Optional token signing, notification, policy, parsers, and topology services are wired where their consumers live.

Let the host own execution

The factories return node services. They do not bind sockets, mount a FastAPI route, run an ASGI server, start every component, or install TLS. The host application must:

  1. start the component set before accepting dependent traffic;
  2. mount or serve the WebSocket and HTTP roles at the configured endpoints;
  3. expose readiness only after required dependencies are usable;
  4. stop listeners before tearing down shared components;
  5. after every dependent component set has stopped, let the creating host shut down its OpenTelemetry tracer and close Redis, storage, and application resources.

ComponentSet.stop() does not own a shared tracer's final shutdown, and a general ITracer implementation is not required to provide shutdown().

Do not create separate in-memory component sets for roles that are meant to cooperate. Their objects would have matching interfaces but different registries, histories, and correlators.

Add strictness at the real boundary

Transport mode is one part of production composition. Also configure forwarded headers, external endpoint advertisement, client authentication, host authorization, administrative authentication, secret management, request limits, tracing, storage retention, and deployment health. A secure preset cannot infer those choices from inside a library.

Issue commands and use their results