gateway_main
Gateway service entry point — platform ingress and egress.
The Gateway is the only service that holds live platform connections
(Discord, Matrix, WebChat, …). It runs GatewayService, a
StargazerService that:
Creates a
PlatformAdapterper enabled platform (viaplatforms.factory.create_platform()) and registers inbound callbacks for new messages, edits, deletes, and reactions.On each inbound message, translates the
IncomingMessageinto a validatedInboundEnvelopeModel(_make_inbound_envelope()) and publishes it to thesg:stream:inboundRedis stream viapublish_inbound().Runs one
OutboundStreamConsumerper platform that readssg:stream:outbound:{platform}and dispatches each response back through the real adapter (send/send_file/send_with_buttons/ reactions).Periodically publishes a platform inventory (servers/channels) to Redis for the web dashboard, throttled to at most every 12 hours.
Since Phase T3 (2026-06-02) the StranglerRouter has been removed: the monolith is gone and all inbound traffic routes unconditionally over the event bus to the Inference service.
Launched standalone (python gateway_main.py) by
scripts/systemd/stargazer-gateway.service.
- class gateway_main.GatewayService(config, redis_client, instance_id, use_health_server=True)[source]
Bases:
StargazerService- __init__(config, redis_client, instance_id, use_health_server=True)[source]
Construct the Gateway service and initialise empty runtime state.
Wires the base
StargazerServicecontract (service name"gateway", instance id, Redis client, mandatory Redis, optional health server) and stashesconfigonself.cfg. All heavy resources (event bus, media cache, adapters, outbound consumers, inventory task) are left asNone/empty here and only created later inon_start();self._stop_eventis theasyncio.Eventthatrun()blocks on andon_stop()sets to unblock shutdown.Delegates base-class setup to
StargazerService.__init__(which builds the optionalHealthServer). Called bymain()when launching the service standalone, and directly by the gateway migration tests undertests/core/migration/(e.g.test_gateway_service.py,test_strangler_removal.py).- Parameters:
config (
Config) – LoadedConfigholding platform definitions, media-cache settings, andtools_dir.redis_client (
Any) – Async Redis client (decode_responses=False) shared across the event bus, outbound consumers, and inventory cache.instance_id (
str) – Unique id for this process (e.g."gateway-ab12cd34"), used as the consumer name for outbound stream reads and for service-registry registration.use_health_server (
bool) – WhenTrue(default) the base class starts an HTTP health server; tests passFalseto skip it.
- async on_start()[source]
Build and start every Gateway dependency: event bus, adapters, consumers.
This is the Gateway’s implementation of the base-class startup phase. It constructs the
RedisEventBusand ensures the Redis streams exist, creates theMediaCache, then for each enabled platform inself.cfg.platformsbuilds aPlatformAdapterviaplatforms.factory.create_platform()(registering_handle_inbound_message()plus the edit/delete/reaction handlers). Crucially it starts everyOutboundStreamConsumerbefore starting the adapters, so the response path onsg:stream:outbound:{platform}exists before any inbound message can arrive. Finally it launches the background_publish_platform_inventory_loop()as a named asyncio task.Interactions: calls
event_bus.ensure_streams,create_platform,consumer.start(), and eachadapter.start(); populatesself.event_bus,self.media_cache,self.adapters,self.outbound_consumers, andself._inventory_task. Adapter construction failures are logged and swallowed so one broken platform does not abort the whole service. Called byboot()during phase 3-7 (whichmain()invokes), and exercised directly by the adapter-lifecycle and strangler-removal migration tests.- Return type:
- async run()[source]
Run the Gateway’s main loop by blocking until shutdown is requested.
The Gateway has no polling work of its own in the foreground: all activity happens in adapter callbacks and the outbound consumers started during
on_start(). This coroutine therefore simply awaitsself._stop_event, whichon_stop()sets during graceful shutdown. Called bymain()immediately afterboot()completes.- Return type:
- async on_stop()[source]
Gracefully tear down the Gateway: stop the loop, consumers, and adapters.
Sets
self._stop_event(unblockingrun()), cancels and awaits the backgroundself._inventory_task(swallowing the resultingasyncio.CancelledError), stops everyOutboundStreamConsumerso no further outbound responses are dispatched, and stops each platform adapter that exposes astopmethod to close live connections. Called byshutdown()(which the SIGINT/ SIGTERM handler inmain()triggers viaservice.shutdown()), and exercised by the gateway adapter-lifecycle migration tests.- Return type:
- async gateway_main.main()[source]
Configure logging, build the Gateway service, and run it until signalled.
Process entry point for the standalone Gateway. It sets up basic logging, loads
ConfigviaConfig.load(), mints a uniqueinstance_id, builds a binary (decode_responses=False) async Redis client, and constructsGatewayService. It installs SIGINT/SIGTERM handlers that scheduleservice.shutdown(), then drives the lifecycle withservice.boot()followed byservice.run()(which blocks until shutdown). Thefinallyblock always closes the Redis client viaaclose().Invoked by the
__main__guard at the bottom of this module throughasyncio.run(main())when launched byscripts/systemd/stargazer-gateway.service. No internal Python callers.- Return type: