Table of Contents

πŸ‡«πŸ‡· Version franΓ§aise

Orkeon.Hosting β€” runner & host bootstrap

Orkeon.Hosting is the shared bootstrap layer that turns the Orkeon libraries into a runnable host. It owns the wiring order that a working runtime needs β€” LLM provider, core services, the standard tool suites, the virtual file system, and the DI-backed tool registry β€” plus the end-to-end execution flows (one-shot kickoff, interactive loop, tool listing) used by every Orkeon runner and CLI.

Inside this repository it is consumed by Orkeon.Scripting.Cli (the orkeon tool) and by Orkeon.Host (the orkeon-host daemon). It is not distributed as a NuGet package β€” see Distribution below. This page documents its public bootstrap surface.

Distribution

Orkeon.Hosting is not a NuGet package. Its csproj sets IsPackable=false, and the publication matrix lists it under Discontinued packages: it is pushed neither to NuGet.org nor to GitHub Packages, so dotnet add package Orkeon.Hosting cannot resolve (NU1101).

It is not embedded in the Orkeon umbrella package either. src/packaging/Orkeon/Orkeon.csproj embeds eleven assemblies β€” Orkeon.Domain, Orkeon.Application, Orkeon.Infrastructure, Orkeon.Constants.{Llm,FileSystem,Configuration}, Orkeon.Tools.Abstractions, Orkeon.Analysis{,.Abstractions}, Orkeon.Rag{,.Abstractions} β€” and Orkeon.Hosting is not one of them.

Assembly Orkeon.Hosting.dll (src/hosting/Orkeon.Hosting)
Packable no β€” IsPackable=false, on no feed
Depends on the core Orkeon.* libraries (Domain, Application, Infrastructure, Analysis, Scripting), the Orkeon.Constants.* satellites, and eight of the nine Orkeon.Tools.* suites (Orkeon.Tools.Rag is deliberately absent β€” RAG stays opt-in), plus CommandLineParser and Microsoft.Extensions.Hosting
Ships through the CLI and installer channels only: the orkeon dotnet tool (Orkeon.Scripting.Cli) and the release.yml installer archives / .deb / MSIs, where Orkeon.Hosting.dll sits next to orkeon and orkeon-host as a private implementation assembly β€” never as a reference a consumer adds

Building an external host against it therefore means building from source: clone the repository and add a ProjectReference to src/hosting/Orkeon.Hosting/Orkeon.Hosting.csproj. The supported package surface for consumers is the Orkeon umbrella (plus Orkeon.Tools and the opt-ins); Orkeon.Hosting is an internal bootstrap layer, documented here for in-tree callers.

RunnerHost.Build

RunnerHost is a static host builder. Build returns a fully configured IHost:

IHost host = RunnerHost.Build(
    settingsPath: "appsettings.json",   // resolved appsettings path (nullable)
    mounts: new RunnerMountPlan          // the whole VFS surface, in one object
    {
        CliMounts = ["/data:/data:ro"],   // CLI --mount args ("physical:virtual:rights")
        InternalMounts = [],              // mounts registered MountVisibility.Internal β€” resolvable
                                          // by the VFS, never listed to an agent (ADR-008)
        AllowExternalMounts = false,      // whitelist mount base paths outside the workspace root
        SelectedMountIds = [],            // --mount-id values, parsed: the settings entries kept when
                                          // several declare one virtual root (VFS-90)
        CrewMountReferences = [],         // the crew's mounts: block β€” selects and validates, never restricts
        LlmLogVirtualPath = null,         // when set, a VIRTUAL directory the caller has mounted:
                                          // LLM HTTP exchanges are captured there as .jsonl
    },
    configureLogging: null,              // optional ILoggingBuilder customization
    configureServices: null,             // optional hook to register runner-specific services
    configureBuilder: null);             // optional IHostBuilder hook β€” orkeon-host uses it for UseSystemd()/UseWindowsService()

A virtual path is always a name starting with / β€” never a disk path (ADR-008). RunnerVirtualRoots β€” in the dependency-free Orkeon.Constants.FileSystem package (ADR-009) so the engine and the tooling read one declaration β€” names the roots the shipped runners take for themselves: /crew (the crew definition's directory), /script (a scripting entry point's directory), /llm-logs, and /sandbox (where the code sandboxes stage what they run). RunnerVirtualRoots.All is the set a caller refuses a user --mount against; asking for the set rather than comparing the roots one by one is deliberate, because the omission of /sandbox survived a pairwise check for as long as it was green. A caller that enables exchange logging mounts its log directory internally and passes RunnerVirtualRoots.LlmLogs here β€” that is what the CLI does.

LoadCrewAsync likewise takes the crew target as a virtual path: it asks the VFS whether the target is a directory rather than probing the disk, so a physical path handed to it is denied.

It composes Host.CreateDefaultBuilder() with:

  • App configuration β€” appsettings resolution plus the CLI mount arguments folded into configuration.
  • Services β€” ConfigureRunnerServices (below).

RunnerHost carries an [SuppressVfsCompliance] bootstrap exception because it resolves user-supplied settings paths and provisions VFS mounts before the DI container (and thus IFileSystemService) exists.

ConfigureRunnerServices behavior

The registration order is deliberate:

  1. Logging β€” runner logging (a single-line console at Warning level by default; --verbose 1/2 or a configureLogging callback raises it) and, when RunnerMountPlan.LlmLogVirtualPath is set, the LLM exchange logging DelegatingHandler.
  2. LLM provider first β€” RegisterLlmProvider reads the Llm config section and registers the provider (and its IChatClient) before AddOrkeonApplication / AddOrkeonInfrastructure. This ordering matters: Orkeon infrastructure registers its LLM/IChatClient fallbacks with TryAdd, so a host-supplied provider must be registered first to win.
  3. Core services β€” AddOrkeonApplication() then AddOrkeonInfrastructure().
  4. Strict tools β€” CrewFactoryOptions.StrictTools defaults to true here (a crew referencing an unknown tool fails loudly with unknown tool(s): …; available: …); opt out with "Orkeon:CrewFactory:StrictTools": false. (The library default stays lenient.)
  5. Permission gate β€” AddOrkeonPermissionGate(configuration) (config opt-in Orkeon:Security:PermissionGate:Enabled; a no-op otherwise).
  6. Core tool suites β€” file system, data, web, code, abstractions, session tools; then the in-memory EventHub plus its agent tools and the EventHub ACL (AddOrkeonEventHubAcl, permissive default so a crew without a links: block behaves as before).
  7. VFS mounts β€” AddOrkeonFileSystem when Orkeon:FileSystem:Mounts or Orkeon:FileSystem:InternalMounts exists and holds at least one entry (two empty arrays register nothing). Either list alone makes the VFS real: --list-tools has only the second. Several entries of Mounts may declare one root when each carries an id (VFS-90): MountSelection.Resolve decides, while the configuration is composed, which one this run keeps β€” a --mount on the root, else SelectedMountIds, else CrewMountReferences β€” and writes the others to null at their own index; a selection nothing resolves throws with the very text the runners' guards print, so a host built without them refuses the same way.
  8. Late tool suites β€” RaggableTree (semantic-graph tools, opt out with "RaggableTree:Enabled": false; pre-registers local embeddings when they are the selected provider), the WebSearch and cache_search tools, and the Brave search tool when BRAVE_API_KEY is present.
  9. Tool registry β€” ServiceProviderToolRegistry is registered as the singleton IToolRegistry.
  10. Runner services β€” the caller's configureServices hook runs last.

ServiceProviderToolRegistry

The IToolRegistry implementation that resolves YAML/TS tool names to IBaseTool instances from DI. Its constructor takes IEnumerable<IBaseTool> β€” every tool the tool suites registered β€” and indexes them by name (case-insensitive). CrewFactory consumes it to build agents with their declared tools, which is why every tool suite registers under IBaseTool: a tool that is not registered cannot be resolved (and, with StrictTools, fails crew loading rather than silently dropping).

RunnerExecution β€” execution flows

RunnerExecution is the shared execution glue: graceful shutdown (SIGTERM/SIGINT), AutoSummaryWriter wiring when an /output:rw mount is declared, verbosity presets, and the run flows. All entry points build the host internally (via the same bootstrap), resolve settings/mounts, and return a process exit code.

Entry point Purpose
RunOneShotAsync(opts, loggerCategory, configureServices?, externalCt?) Runs a single crew kickoff end-to-end. Exit codes: 0 success, 1 config error, 2 crew failure, 130 canceled.
RunInteractiveLoopAsync(opts, loggerCategory, stopWords, kickoffPerInputAsync, onSessionStart, …) REPL loop; each input drives a kickoff via the caller-supplied delegate; a stop word ends the loop (exit 0).
RunListToolsAsync(opts, loggerCategory, configureServices?) Builds the host with no crew and prints the sorted, de-duplicated runtime tool names to stdout (logs to stderr) β€” the runtime tool contract consumed by packaging/lint tooling.
RunValidateAsync(opts, loggerCategory, configureServices?) Dry-run behind --validate: builds the host and loads the crew (strict tool resolution) without probing the LLM or running a kickoff.
LoadCrewAsync(host, opts) Loads and maps the crew definition from the resolved target β€” the building block the flows above share.

Consuming from a long-running host

A long-running service can simply wrap RunnerHost.Build β€” that is exactly what the orkeon-host daemon does (Orkeon.Host/Program.cs), passing configureBuilder for UseSystemd()/UseWindowsService(). The daemon stays out of the mount selection (VFS-90, D-11): its crews mount under per-crew roots (/crews*), so no root is ever declared twice there, an operator --mount carrying an id prefix parses like any other, and no crew mounts: block is read. A host that already owns its IHostBuilder (an ASP.NET app, for example) instead replicates ConfigureRunnerServices' registration order inside its own Program.cs β€” there is no packaged shortcut for this; the REPL console inlines the same sequence by hand:

// 1. Register the LLM provider FIRST (before AddOrkeonInfrastructure, whose TryAdd fallback would
//    otherwise win).
// 2. Core services:
services.AddOrkeonApplication();
services.AddOrkeonInfrastructure(configuration);
// 3. Tool suites (drive which tools crews can use):
services.AddOrkeonFileSystemTools();
services.AddOrkeonDataTools();
services.AddOrkeonWebTools();
// … the remaining AddOrkeon*Tools() suites …
// 4. VFS mounts from configuration (the web host provisions at least one mount):
services.AddOrkeonFileSystem(configuration);
// 5. Tool registry LAST, so it captures every registered IBaseTool:
services.AddSingleton<IToolRegistry, ServiceProviderToolRegistry>();

Because a web host typically runs each crew in its own DI scope (Orkeon's crew repositories are scoped), ServiceProviderToolRegistry β€” a singleton over the registered IBaseTool set β€” is shared across runs, while CrewFactory and the orchestrator resolve per scope.