Windows service runtime¶
Use CPython 3.14 and install the package in the downstream application's virtual
environment. Start the foreground service with its .lclcfg file:
lcl-fastapi serve -o config service.lclcfg
server.host accepts 127.0.0.1 or 0.0.0.0; the default is loopback. Set
server.workers in the configuration. The runtime uses Uvicorn's native
multiprocess manager even for one worker, giving the service one consistent
master identity and shutdown path. Uvicorn handles crashed-worker replacement.
Workers use Winloop through Uvicorn's supported winloop:new_event_loop
factory. The Windows-only dependency is installed automatically. This avoids
the stalled shared-listener accepts observed with the standard Selector loop
and the shared-socket IOCP errors observed with Proactor in multi-worker tests.
The master only binds and shares the listener; it does not run an event loop.
Real-process tests require idle sampling, crashed-worker replacement, and
graceful shutdown; the composed example requires both workers to serve HTTP.
See Uvicorn's event-loop integration.
Shared-listener connection assignment follows Windows scheduling; the framework
does not promise round-robin or evenly distributed requests. A lightly loaded
service may send all new connections to one worker while its siblings remain
ready. The composed verifier briefly pauses the observed worker under a bounded
connection burst, requires a sibling response, resumes it in finally, and
then validates every response. This checks both workers without assuming fair
assignment by the operating system.
If worker configuration, application import, or lifespan initialization fails,
the service stops, preserves the original error diagnostic on stderr, cleans
its runtime files, and the console command returns a nonzero exit status. A
normal accepted shutdown remains a successful exit, including shortly after
startup.
The master records its actual PID, which can differ from the Windows virtual environment's Python launcher PID. Each worker reads configuration independently and owns its application lifespan and log sink.
The runtime supports starting without an attached console. The authenticated
shutdown endpoint publishes a marker belonging to the current service start.
A lightweight bridge wakes Uvicorn's normal manager exit event. Each worker
identifies Uvicorn 0.52's Server through its installed SIGTERM bound handler,
checks the instance type, and sets that server's public should_exit flag. An
unexpected handler fails startup explicitly. Uvicorn
drains requests and exits application lifespans before the master joins workers.
This does not use Windows os.kill(pid, SIGTERM), which forcibly terminates a
process, or require a visible terminal or console control group.
The native whoami and icacls commands restrict the shutdown token to the
current service-account SID. If either permission operation fails, startup
fails instead of exposing a usable token with inherited permissions.
The integration test starts a real two-worker service using
subprocess.CREATE_NO_WINDOW, exercises a worker crash and replacement,
checks server-issued request IDs and token rejection, changes the configured
port before stop, and verifies business shutdown logs and cleanup. Run it from
the repository development environment:
venv\Scripts\python.exe -m pytest tests/test_runtime_process.py
See runtime ownership and observations for state consistency, configuration changes, graceful deadlines, and the route-override warning.
Development reload¶
Use serve -o config service.lclcfg -o hot_reload with
server.reload_dirs: ["./src", "../shared"] to select recursive Python roots.
Windows retires a reload worker through an identity-specific marker and its
existing graceful Server exit bridge, retaining console-free operation.
Hot reload forces one worker and warns when the configured count is higher.
Native automatic reload is disabled. See the shared contract
for errors, cleanup, and configuration boundaries.