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.PlatformService
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScript

Supplying Platform Services

A host needs the standard operational services before it can run application workflows: a clock, a logger, randomness, GUIDs, and environment variables. Axial.PlatformService bundles all five as BaseRuntime.

Use the live bundle:

Show page setup
// Setup for the checked example on this page.
open System
open Axial
open Axial.Layers
open Axial.PlatformService
/// Fails the docs test when an example's result differs from the value shown.
let shouldEqual expected actual =
    if actual <> expected then failwithf "Expected %A but got %A" expected actual
Verified
open Axial
open Axial.PlatformService
Verified
let runtime : BaseRuntime = BaseRuntime.liveValue
Requires & types
Requires: open Axial.PlatformService (packages: Axial.PlatformService)
val runtime: BaseRuntime

BaseRuntime implements one contract per service, so any workflow requiring some combination of them runs against this single value:

Verified
let startup : Flow<BaseRuntime, EnvironmentVariableError, int> =
    flow {
        let! port = EnvironmentVariable.getInt "PORT"
        do! Log.info $"Listening on {port}"
        return port
    }
Requires & types
Requires: open Axial; open Axial.PlatformService (packages: Axial, Axial.PlatformService)
val startup: Flow<BaseRuntime,EnvironmentVariableError,int>

The block does not name the service types. IHasEnvironmentVariables and IHasLog are distinct contracts, so their constraints merge.

Use BaseRuntime.live when the environment is composed from layers, and BaseRuntime.fromServiceProvider when the host already has an IServiceProvider; that variant reports missing registrations as typed BaseRuntimeError startup failures rather than resolution exceptions.

BaseRuntime.live is a Layer<unit, Never, BaseRuntime>, so provision it over a workflow whose environment it satisfies. The checked run below overrides the clock with a fixed value so the year is deterministic; the layer shape and the Layer.provide call are the same as the real BaseRuntime.live:

Verified
let currentYear : Flow<BaseRuntime, Never, int> =
    Clock.now |> Flow.map _.Year
Requires & types
Requires: open Axial; open Axial.PlatformService (packages: Axial, Axial.PlatformService)
val currentYear: Flow<BaseRuntime,Never,int>
Verified
let fixedRuntime =
    { BaseRuntime.liveValue with Clock = Clock.fromValue (DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero)) }

currentYear |> Layer.provide (Layer.succeed fixedRuntime) |> Flow.run () |> function
| Exit.Success year -> shouldEqual 2026 year
| other -> failwithf "Expected a year, got %A" other
Requires & types
Requires: open Axial; open Axial.Layers; open Axial.PlatformService; open System (packages: Axial, Axial.Layers, Axial.PlatformService)
val fixedRuntime: BaseRuntime

BaseRuntime.fromServiceProvider consumes an IServiceProvider and reports a missing registration as BaseRuntimeError.MissingService before the workflow runs; the checked composition and failure are in Providing the environment.

Applications normally extend the bundle with their own services rather than using it bare; see building a base runtime.

Service details

Platform services documents each service's operations, deterministic test doubles, and typed environment-variable error model.