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.JavaScriptAxial integrates seamlessly with ordinary .NET code — nothing stops a workflow from calling File.ReadAllText or
new HttpClient() directly. The built-in services exist for the capabilities most applications reach for often
enough to be worth wrapping: the clock and other operational services, the console, the file system, external
processes, and HTTP. Each one is an ordinary explicit dependency. Nothing here is a new mechanism — these pages
apply service contracts and layers to capabilities the
library already wrote for you.
Every built-in service is built in two layers:
- A direct wrap. The service contract mirrors the .NET API it replaces —
IClock.UtcNow(),FileSystem.readAllText,Process.command— so nothing you already know about the underlying capability stops applying. Wrapping it, rather than calling it ambiently, is what earns three things for free: a typed error channel in place of unclassified exceptions, concurrency handling through the Flow runtime instead of raw tasks, and a mockable dependency — every service ships a deterministic test double beside itsliveimplementation, and the platform services also compose intoBaseRuntimeas one bundle. - An improved ergonomic API, where it earns its place. Some services stop at the wrap —
ConsoleandClockare thin enough that the .NET API was already the right shape. Others add real value on top:FileSystemandHttpClientreplace a dozen exception types with one typed union each;EnvironmentVariableadds required, parsed reads over the raw string lookup;Processadds composable piping and secret redaction thatSystem.Diagnostics.Processhas no equivalent for.
| Service | Wraps | Ergonomic layer on top |
|---|---|---|
| Clock | DateTimeOffset.UtcNow |
Derived readers (utcDateTime, unixTimeSeconds, unixTimeMilliseconds) |
| Logging | An ambient logger | logException preserves the exception object instead of flattening it to a string; live defaults to a safe no-op |
| Randomness and GUIDs | System.Random, Guid.NewGuid() |
nextInt min max bounds a value in one call; Random.bytes count allocates and fills in one step |
| Environment variables | Environment.GetEnvironmentVariable |
EnvironmentVariable module: required, parsed reads failing with EnvironmentVariableError instead of null or an exception |
| Console | System.Console |
None — the wrap is the whole surface; failures are defects, not a typed channel |
| FileSystem | System.IO.File / Directory |
Every operation returns FileSystemError in place of a dozen exception types |
| Processes | System.Diagnostics.Process |
Composable piping, secretArg redaction, a DSL, typed timeouts and transcripts |
| HTTP | HttpClient |
DSL builders with automatic URL-encoding, typed HttpError, per-request timeout, retryTransient with backoff |
open Axial
open Axial.Console
AxialConsole| Part | Purpose |
|---|---|
IHasClock, IHasConsole, IHasFileSystem, IHasProcess, IHasHttp |
The contract a workflow constrains its environment with |
Console.live, FileSystem.live, Http.live |
The implementation backed by the real platform |
Layer.succeed Console.live, Layer.succeed FileSystem.live, Layer.succeed (Http.live …) |
The same implementation as a Layer for runtime composition |
A workflow names the contract and never the implementation:
let greet name : Flow<#IHasConsole, Never, unit> =
Console.writeLine $"Hello, {name}."
greet: 'a -> Flow<'b,Never,unit>name: 'aAxial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
Axial.Console.IHasConsoleDeclares that an environment supplies the console service. Implement this on the environment supplied at the host edge. A workflow that reads or writes the console constrains its environment with 'env :> IHasConsole.
Axial.NeverRepresents an error channel that cannot occur.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
Axial.Console.ConsoleModulewriteLine: string -> Flow<'env,'error,unit>When to declare your own instead
Use these services when you genuinely need the real capability. When a workflow only needs "somewhere to report
progress" or "the current configuration", declare a narrow application dependency instead — see
choosing an approach. Depending on IConsole to print one line couples the workflow to a
terminal it may not have.
In this section
- Platform services — clock, logging, randomness, GUIDs, and environment variables.
- Console — standard streams, redirection, and terminal control.
- FileSystem — files, directories, paths, and typed
FileSystemErrorvalues. - Processes — external commands as composable, typed workflows.
- HTTP — typed requests, decoded responses, and transient-failure retries.
- Tutorial: Composing built-in services — embedding
BaseRuntimeand your own dependencies in one environment.
Two related capabilities are documented elsewhere because they are not services in this sense. Telemetry is cross-cutting instrumentation of the runtime rather than a dependency a workflow declares, and hosting supplies environments instead of being one.

