Skip to content

Worker & Console Host

Spectra runs inside any .NET host that supports IHostedService.

In practice, that means the same Spectra setup works in:

  • background workers
  • console apps
  • ASP.NET Core apps
  • other hosted .NET processes

This page covers two common non-HTTP patterns:

  • a background worker for long-running or triggered workflows
  • a console app for one-shot runs and scripts

The Spectra registration is the same in both cases. Only the host pattern around it changes.


Background worker

Use a worker service when workflows should run continuously in response to external work.

Typical cases:

  • queue consumers
  • scheduled jobs
  • batch processing
  • event-driven orchestration without HTTP

Setup

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddSpectra(spectra =>
        {
            spectra.AddOpenRouter(c =>
            {
                c.ApiKey = Environment.GetEnvironmentVariable("OPENROUTER_API_KEY")!;
                c.DefaultModel = "openai/gpt-4o-mini";
            });

            spectra.AddWorkflowsFromDirectory("./workflows");
            spectra.AddFileCheckpoints("./checkpoints");
            spectra.AddConsoleEvents();
        });

        services.AddHostedService<DocumentProcessingWorker>();
    })
    .Build();

await host.RunAsync();

Worker example

public class DocumentProcessingWorker : BackgroundService
{
    private readonly IWorkflowRunner _runner;
    private readonly IWorkflowStore _workflowStore;

    public DocumentProcessingWorker(IWorkflowRunner runner, IWorkflowStore workflowStore)
    {
        _runner = runner;
        _workflowStore = workflowStore;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var document = await DequeueNextDocumentAsync(stoppingToken);
            if (document is null)
                continue;

            var workflow = _workflowStore.Get("process-document")!;

            var state = new WorkflowState();
            state["inputs.documentId"] = document.Id;
            state["inputs.content"] = document.Content;

            await _runner.RunAsync(workflow, state, stoppingToken);
        }
    }
}

The worker just resolves IWorkflowRunner and IWorkflowStore from DI and runs workflows as needed.

Graceful shutdown

RunAsync(...) accepts a CancellationToken.

When the host shuts down:

  • the token is cancelled
  • the current workflow execution is asked to stop cleanly
  • if checkpointing is enabled, the run can resume later

Without checkpointing, interrupted runs start over on the next launch.


Console app

Use a console app when you want a one-shot workflow run.

Typical cases:

  • scripts
  • local tools
  • development testing
  • one-time batch jobs
  • migrations or exports

Example

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddSpectra(spectra =>
        {
            spectra.AddAnthropic(c =>
            {
                c.ApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY")!;
                c.DefaultModel = "claude-sonnet-4-20250514";
            });

            spectra.AddConsoleEvents();
        });
    })
    .Build();

await host.StartAsync();

var runner = host.Services.GetRequiredService<IWorkflowRunner>();

var workflow = WorkflowBuilder.Create("summarize")
    .AddAgent("assistant", "anthropic", "claude-sonnet-4-20250514")
    .AddAgentNode("summarize", "assistant", node => node
        .WithUserPrompt("Summarize this report: {{inputs.text}}")
        .WithMaxIterations(1))
    .Build();

var state = new WorkflowState();
state["inputs.text"] = File.ReadAllText(args[0]);

var result = await runner.RunAsync(workflow, state);

Console.WriteLine(result["nodes.summarize.output.response"]);

await host.StopAsync();

Why StartAsync() matters

In console apps, call:

await host.StartAsync();

before resolving and using the runner.

That ensures SpectraHostedService has already finished startup work such as:

  • tool discovery
  • MCP connections
  • agent tool validation

Without this, some runtime features may not be ready yet.


Startup lifecycle

The startup flow is the same in worker and console hosts:

Host.StartAsync()
  -> SpectraHostedService.StartAsync()
     -> discover tools
     -> connect MCP servers
     -> validate agent tool declarations
  -> your workflow execution begins

If a required MCP server fails during startup, host startup fails.

That is usually the right behavior, because it surfaces configuration problems early instead of causing harder-to-debug failures later during workflow execution.


Which host should you use?

Background worker Console app
Lifetime Long-running One-shot
Trigger Queue, timer, external event Command line
Concurrency Often many runs Usually one run
Best for Production jobs and pipelines Scripts and local tools
Startup pattern RunAsync() StartAsync() / StopAsync()

A simple rule:

  • choose a worker when workflows are part of an ongoing service
  • choose a console app when workflows are part of a single command

A simple mental model

Spectra does not require a special host.

It plugs into the normal .NET hosting model:

  • register Spectra with DI
  • start the host
  • resolve the runner
  • execute workflows

The rest depends on whether your app is long-running or one-shot.


What's next?

  • ASP.NET Core

Expose workflows over HTTP.

ASP.NET Core

  • Checkpointing

Persist workflow state for clean resume.

Checkpointing

  • Tools Overview

Register tools and MCP servers for agents.

Tools