Decision record
AD-0010 The router as a child process from the copied binary
Status: accepted, amended once the supervisor ran the router. Date: 2026-08-27.
Amendment, 2026-08-27: the decision as first accepted gave the router child
three variables of its own beside the four telemetry variables of SR-03. Two
more were added once the composed stack ran. SUBGRAPH_ERROR_PROPAGATION_MODE
is set to pass-through, without which a refused edit reached a client nested
under the router’s own fetch-failure text rather than in the words the subgraph
wrote. PROMETHEUS_ENABLED=false closes the scrape endpoint the router opens
by default. SR-24 and SR-25 joined the requirements the record affects.
Context
The demo ships as one image started by one docker run, and the
Cosmo router has to be inside it. The vendor publishes the router as an
image, ghcr.io/wundergraph/cosmo/router, built with CGO_ENABLED=0 onto
distroless static for linux/amd64 and linux/arm64, with the binary at
/router and about 40 MB compressed. It is also a Go module, and
the module can in principle be embedded:
core.NewRouter, WithStaticExecutionConfig and Router.Start are
exported, and the documented custom-binary pattern is routercmd.Main()
with blank-imported modules.
What the module does not offer is a stable footing. Its release tags are of the
form [email protected], which Go tooling cannot resolve, so only commit
pseudo-versions exist. The official router-examples README requires a block of
OpenTelemetry replace directives and says compatibility is not guaranteed
without them. The module API broke at 0.188.0 and 0.278.0, a new module system
is announced for the next major release, WithModulesConfig takes
map[string]interface{}, and no docs page describes direct construction
through core.NewRouter. No Go API stability promise is published anywhere.
Embedding would also pull graphql-go-tools, the process extension library and
the NATS, Kafka and Redis clients into this module’s dependency graph. The
empty-interface signature would then sit in hand-written code, which the
repository’s own rule forbids outside a reviewed exception.
Copying the binary has its own conditions. COPY --from accepts an image
reference, the router is statically linked, and copying /router out of
the pinned image is legitimate under Apache 2.0 provided the LICENSE
travels with it. The router binary has no probe subcommand, so
a container built on it alone could carry no HEALTHCHECK. Whether
COPY --from resolves the target platform’s variant in a multi-platform
build was unverified when this record was written. A Go supervisor that is
PID 1 and the router’s direct parent reaps it through os/exec without tini.
Reaping of grandchildren is only likely, and the two known sources of
grandchildren are the usage tracker and the router’s extension mechanism.
Decision
We will run the Cosmo router as a child process of the supervisor, from the
binary copied out of ghcr.io/wundergraph/cosmo/router:0.343.1 with a
COPY --from stage, driven by environment and the committed execution
configuration alone. The image copies /router and fetches the vendor’s
LICENSE at the pinned tag with a pinned checksum to sit beside it. The
supervisor starts the child with LISTEN_ADDR=127.0.0.1:3002,
EXECUTION_CONFIG_FILE_PATH=/app/config.json, PLAYGROUND_PATH=/playground,
the four telemetry variables of SR-03,
SUBGRAPH_ERROR_PROPAGATION_MODE=pass-through and PROMETHEUS_ENABLED=false,
and waits for /health/ready before opening the published port. Pass-through
puts a subgraph’s own error at the top of the answer. A refused edit therefore
reaches the client as the subgraph wrote it rather than nested under the
router’s fetch-failure text, which is what SR-24 and SR-25 exist for. The other
closes the Prometheus scrape endpoint the router serves by default, which
nothing here reads.
Alternatives considered
Embedding the router as a Go library through core.NewRouter and
WithStaticExecutionConfig, with the subgraphs as in-process servers. One
static binary, one process, the smallest image, and programmatic control of
playground, telemetry and listen address. It lost on the module’s footing:
pseudo-versions only, the replace block, two API breaks, an announced
rewrite, an undocumented construction path and no stability promise. An
empty-interface signature counted against it too, as did a dependency graph
that would carry message brokers the demo never uses.
The vendor’s custom-binary form, routercmd.Main() with custom modules,
which is the supported shape of embedding. It takes
over main, reads its configuration from file and environment as the stock
binary does, and offers the demo nothing the copied binary does not, while
carrying every dependency and version problem of the library route.
The router’s extension mechanism under Cosmo Connect, or standalone gRPC services, as the way to co-locate the subgraphs with the router. Officially supported, and the router would supervise the extensions itself. Neither supports subscriptions, and the two apps depend on live version events (AD-0014), so this route would have replaced live push with polling.
Consequences
The router is a vendor artefact inside the image, pinned by tag and bumped by
rebuilding. Nothing in the Go module depends on it, so the router’s Go API can
change without touching this repository. A router upgrade is a change to the
Dockerfile and to the wgc version composed with it, which must move together.
The licence obligation is explicit: the image carries the router’s LICENSE next
to /router (SR-07), and NOTICE names the router with its version (SR-08).
The bump has a second obligation. SUBGRAPH_ERROR_PROPAGATION_MODE is a name
the vendor documents nowhere: the binary’s help lists no environment variables
at all, and the name is readable only in the struct tags compiled into the
router’s own configuration. Should a release rename or drop it, the router
returns to its default. The default puts Failed to fetch from Subgraph at the
top of the errors array with the subgraph’s own message two levels below, which
is where SR-24 and SR-25 came from. Both would then be met on paper and not in
the answer a client reads, and the statement in the example README that a
refusal arrives unwrapped would be false. No test would fail, because the tests
assert the environment the supervisor builds and not what the router makes of
it. A router bump therefore means driving one refused edit through the composed
stack and reading errors[0].message before the image is tagged.
PROMETHEUS_ENABLED is undocumented on the same terms and is checked in the
same pass, where losing it reopens a listener rather than breaking a
requirement (AD-0013).
The cost is a second process to start, watch and stop. The supervisor is
the parent of a child it cannot inspect from the inside, so readiness is
read from /health/ready and the healthcheck subcommand of
AD-0011 probes on the router’s behalf. What /health/ready
reports before the subgraphs answer is a spike, and the supervisor’s
ordering, subgraphs first, router last, published port after readiness, is
chosen so as not to depend on the answer.
The router sets the floor of the image size, around 40 MB compressed of
SR-06’s 80 MB budget. SR-05 rests on another spike: if COPY --from
does not resolve the target platform, the arm64 image would carry an amd64
router and fail on Apple silicon, which is why the arm64 layer is inspected
before the first tag. A third settles whether the copied binary starts
from environment alone in a distroless image with no YAML file present.
All three are reported in
Five spikes before the first line.
Grandchild reaping stays likely rather than verified. The design removes the two known sources, since the usage tracker is disabled by SR-03 and no router extension is loaded, and no tini is added.
Requirements affected
SR-03, SR-05, SR-06, SR-07, SR-24, SR-25
Sources
The Cosmo router image and its LICENSE at 0.343.1, the router module’s release tags and the replace block its examples repository requires. The vendor’s pages on running the router from environment and a static execution configuration. The architecture views for the runtime and deployment views, and What the research overturned for the embedding claim as it was checked.