Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.HttpClient
open Axial.PlatformService
open Axial.Process
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptProviding the Environment
Everything so far has been about reading the environment. This page is about producing the value in the first place — the step that happens once, at startup, and again in each test.
There are three ways, in the order you should reach for them.
1. Construct it
Build the record and hand it over. Nothing else is involved:
let live =
{ Users = SqlUserStore(connectionString)
Audit = FileAuditLog(logPath)
Clock = Clock.live }
let exit = program |> Flow.run liveFor the operational services, Axial.PlatformService ships a ready-made bundle so you do not have to name all five:
let result = Clock.now |> Flow.run BaseRuntime.liveValue2. Take it from a host container
.NET hosts already have an IServiceProvider. Use it to build the environment, then leave it behind:
let handler : Flow<IServiceProvider, unit, unit> =
flow {
let! orders = ServiceProvider.get<IOrderRepository, _, _>()
do! orders.Flush()
}The rule is one line: use IServiceProvider to build the world; do not make every business workflow depend on it.
A workflow typed Flow<IServiceProvider, _, _> can reach anything, which is exactly the property the environment
channel exists to remove. Convert at the edge and let the rest of the application name what it needs.
3. Provision it with a layer
When building the environment is itself effectful — it can fail with a typed startup error, needs a resource released later, or must await something — construction becomes a workflow of its own. That is what layers are, and they live in a separate package because most applications never reach this case.
The signal is in the type. Layer<IServiceProvider, BaseRuntimeError, BaseRuntime> says: consumes a provider, may
fail with a typed startup error, produces a runtime. Axial.PlatformService ships exactly that as
BaseRuntime.fromServiceProvider, which turns dynamic registrations into an explicit BaseRuntime and reports
anything missing as BaseRuntimeError.MissingService before the first workflow runs.
let runnable = workflow |> Layer.provide BaseRuntime.fromServiceProvider| Situation | Use |
|---|---|
| You can build the value | Construct it and call Flow.run |
| A host container owns the implementations | ServiceProvider.get at the edge |
| Construction can fail, block, or acquire | A layer |
Tests almost always want the first row, whatever production uses.

