| name | koan-bootstrap |
| description | Auto-registration via KoanAutoRegistrar, minimal Program.cs, "Reference = Intent" pattern |
Koan Bootstrap & Auto-Registration
Core Principle
services.AddKoan() is the ONLY line needed in Program.cs. The framework discovers and registers everything through auto-registration. No manual service registration. No manual configuration. Just add package references and everything wires up automatically.
Revolutionary "Reference = Intent" Pattern
Adding a package reference automatically enables functionality:
<PackageReference Include="Koan.Data.Connector.Mongo" Version="0.6.3" />
<PackageReference Include="Koan.AI" Version="0.6.3" />
No manual registration in Program.cs. The framework handles everything.
Minimal Program.cs Template
using Koan.Core;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKoan();
var app = builder.Build();
app.Run();
That's it. 8 lines total. No manual service registration. No manual middleware configuration. The framework discovers:
- Data adapters
- AI providers
- Authentication providers
- Entity controllers
- Background services
- Message queues
- Everything else
When This Skill Applies
Invoke this skill when:
- ✅ Setting up new projects
- ✅ Debugging initialization issues
- ✅ Adding framework modules
- ✅ Troubleshooting boot failures
- ✅ Creating application-specific services
- ✅ Understanding assembly discovery
KoanAutoRegistrar Pattern
What It Is
KoanAutoRegistrar is how you register application-specific services (not framework services - those auto-register). Create one per application/module.
When to Create One
Create KoanAutoRegistrar when you have:
- Application-specific business logic services
- Custom background workers
- Domain-specific infrastructure
- Third-party service integrations
Template
using Koan.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace MyApp.Initialization;
public sealed class KoanAutoRegistrar : IKoanAutoRegistrar
{
public string ModuleName => "MyApp";
public string? ModuleVersion =>
typeof(KoanAutoRegistrar).Assembly.GetName().Version?.ToString();
public void Initialize(IServiceCollection services)
{
services.AddScoped<ITodoService, TodoService>();
services.AddScoped<IEmailService, EmailService>();
services.AddSingleton<ICacheService, RedisCacheService>();
services.AddHostedService<BackgroundCleanupWorker>();
}
public void Describe(ProvenanceModuleWriter module, IConfiguration cfg, IHostEnvironment env)
{
module.Describe(ModuleVersion, "Application services");
module.AddNote("Application services registered");
module.AddNote($"Environment: {env.EnvironmentName}");
}
}
KoanModule — Preferred Authoring Surface (ARCH-0086)
As of ARCH-0086, extend KoanModule instead of implementing IKoanAutoRegistrar directly. A KoanModule
is an IKoanAutoRegistrar (discovered + ordered identically), but gives one self-describing surface plus a
real, ordered startup lifecycle:
using Koan.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace MyApp.Initialization;
public sealed class MyAppModule : KoanModule
{
public override string Id => "MyApp";
public override void Register(IServiceCollection services)
{
services.AddScoped<ITodoService, TodoService>();
services.AddSingleton<ICacheService, RedisCacheService>();
}
public override Task Start(IServiceProvider sp, CancellationToken ct)
{
return Task.CompletedTask;
}
public override void Report(ProvenanceModuleWriter module, IConfiguration cfg, IHostEnvironment env)
=> module.Describe(Version);
}
Register ⇐ Initialize, Report ⇐ Describe, plus a new Start for one-time ordered startup work.
- Recurring periodic/pokable work stays on the
IKoanBackgroundService family. Code that registers services
or must run before the container is built belongs in Register, not Start.
- The raw
IKoanAutoRegistrar above still works via the bridge — migration is opportunistic.
Discovering "many implementers of one contract": [KoanDiscoverable]
Don't hand-roll an AppDomain assembly scan to find plug-ins. Mark the interface [KoanDiscoverable] and
read implementers from the registry:
[KoanDiscoverable]
public interface IMyPlugin { }
foreach (var t in KoanRegistry.GetDiscoveredImplementors(typeof(IMyPlugin)))
services.TryAddEnumerable(ServiceDescriptor.Scoped(typeof(IMyPlugin), t));
The source generator (build time) + RegistryManifestLoader (runtime) populate KoanRegistry, so discovery
is fast and never misses lazily-loaded assemblies. (Used by IKoanAuthEventContributor / IKoanAuthFlowHandler.)
Discovery Rules
The framework automatically discovers IKoanAutoRegistrar implementations:
- Assembly Scanning: Scans all loaded assemblies at startup
- Interface Detection: Finds types implementing
IKoanAutoRegistrar
- Instantiation: Creates instance and calls
Initialize()
- Boot Reporting: Calls
Describe() to populate boot report
You don't call it. The framework finds and executes it automatically.
What NOT to Do
❌ WRONG: Manual Service Registration in Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<ITodoRepository, TodoRepository>();
builder.Services.AddDbContext<MyDbContext>();
builder.Services.AddScoped<ITodoService, TodoService>();
builder.Services.AddKoan();
Why wrong?
- Breaks "Reference = Intent" pattern
- Creates registration order dependencies
- Duplicates framework auto-registration
- Makes Program.cs grow uncontrollably
✅ CORRECT: Use KoanAutoRegistrar
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKoan();
var app = builder.Build();
app.Run();
public sealed class KoanAutoRegistrar : IKoanAutoRegistrar
{
public void Initialize(IServiceCollection services)
{
services.AddScoped<ITodoService, TodoService>();
}
}
❌ WRONG: Multiple AddKoan() Calls
builder.Services.AddKoan();
builder.Services.AddKoanData();
builder.Services.AddKoanWeb();
builder.Services.AddKoanAI();
Why wrong? AddKoan() already discovers and registers ALL Koan modules automatically.
✅ CORRECT: Single AddKoan()
builder.Services.AddKoan();
Boot Report & Diagnostics
Viewing Boot Report
if (KoanEnv.IsDevelopment)
{
var logger = app.Services.GetRequiredService<ILogger<Program>>();
KoanEnv.DumpSnapshot(logger);
}
Output shows:
[INFO] Koan:discover postgresql: server=localhost;database=myapp... OK
[INFO] Koan:modules data→postgresql
[INFO] Koan:modules web→controllers
[INFO] Koan:modules ai→openai
[INFO] Koan:modules MyApp v1.0.0
Boot Report Structure
The Describe method receives a ProvenanceModuleWriter — a fluent writer with the following API:
| Method | Purpose |
|---|
module.Describe(version, description) | Set module version and description text |
module.AddNote(message) | Append a plain-text note (extension method) |
module.SetStatus(status, detail) | Set operational status ("ok", "degraded", "error") |
module.ClearStatus() | Reset status to default |
module.SetSetting(key, builder) | Add a structured setting entry with source tracking |
module.SetNote(key, builder) | Add a structured note entry |
module.AddTool(name, route, description) | Register an exposed tool/endpoint in the report |
public void Describe(ProvenanceModuleWriter module, IConfiguration cfg, IHostEnvironment env)
{
module.Describe(ModuleVersion, "Application services");
module.AddNote("Services registered: TodoService, EmailService");
module.AddNote($"Data source: {cfg["Koan:Data:Sources:Default:Adapter"]}");
module.SetSetting("Environment", b => b.Value(env.EnvironmentName));
if (!cfg.GetSection("Email:Smtp").Exists())
{
module.SetStatus("degraded", "Email configuration missing — notifications disabled");
}
}
Note: There is no AddWarning() or AddModule(). Use SetStatus("degraded", detail) to signal
non-fatal configuration issues. The module identity is set via module.Describe(version, description).
Environment Detection
Use KoanEnv for environment-aware logic:
public void Initialize(IServiceCollection services)
{
if (KoanEnv.IsDevelopment)
{
services.AddScoped<ISeedService, DevelopmentSeedService>();
}
if (KoanEnv.IsProduction)
{
services.AddSingleton<IEmailService, SendGridEmailService>();
}
if (KoanEnv.InContainer)
{
services.AddSingleton<IHealthCheckService, ContainerHealthCheck>();
}
if (KoanEnv.AllowMagicInProduction)
{
services.AddScoped<IAdminService, AdminService>();
}
}
Configuration Reading
Use framework configuration helpers:
public void Initialize(IServiceCollection services)
{
var sp = services.BuildServiceProvider();
var cfg = sp.GetRequiredService<IConfiguration>();
var apiKey = Configuration.Read(
cfg,
defaultValue: "dev-key",
"App:ApiKey",
"APP_API_KEY"
);
services.AddSingleton(new ExternalApiClient(apiKey));
}
Debugging Bootstrap Issues
Symptom: Service Not Found
System.InvalidOperationException: Unable to resolve service for type 'ITodoService'
Cause: KoanAutoRegistrar not discovered or not registering service
Solution:
- Verify file exists at
/Initialization/KoanAutoRegistrar.cs
- Verify class implements
IKoanAutoRegistrar
- Verify class is
public and not internal
- Check boot logs for module registration
Symptom: Provider Not Available
[ERROR] Koan:discover mongodb: connection failed
[INFO] Koan:modules data→json (fallback)
Cause: Provider package referenced but connection failed
Solution:
- Verify connection string in
appsettings.json
- Check service is running (Docker, local install)
- Verify network connectivity
- Check boot report for detailed error
Symptom: Assembly Not Loaded
[WARNING] Koan:modules MyModule not discovered
Cause: Assembly not referenced or not loaded at startup
Solution:
- Verify
<ProjectReference> or <PackageReference> exists
- Check assembly is copied to output directory
- Add explicit assembly reference if needed:
var assembly = Assembly.Load("MyModule");
Bundled Templates
templates/Program.cs.template - Minimal Program.cs
templates/KoanAutoRegistrar.cs.template - Complete registrar template
templates/KoanAutoRegistrar-with-options.cs.template - Registrar with configuration options
templates/appsettings.json.template - Koan configuration structure
Reference Documentation
- Full Guide:
docs/guides/deep-dive/bootstrap-lifecycle.md
- Troubleshooting:
docs/guides/troubleshooting/bootstrap-failures.md
- Auto-Provisioning:
docs/guides/deep-dive/auto-provisioning-system.md
- Sample:
samples/S0.ConsoleJsonRepo/Program.cs (Minimal 20-line bootstrap)
- Sample:
samples/S1.Web/Program.cs (Web bootstrap with lifecycle)
Advanced: Module Loading Order
Modules load in this order:
- Core - Foundation services
- Data - Repository abstractions
- Adapters - Concrete providers (Mongo, Postgres, etc.)
- Domain - Entity registrations
- Web - Controllers, middleware
- Application - Your
KoanAutoRegistrar
Dependencies are resolved automatically. You never need to specify order manually.
Advanced: Conditional Registration
public void Initialize(IServiceCollection services)
{
var sp = services.BuildServiceProvider();
var cfg = sp.GetRequiredService<IConfiguration>();
if (cfg.GetValue<bool>("Features:EmailNotifications"))
{
services.AddScoped<INotificationService, EmailNotificationService>();
}
else
{
services.AddScoped<INotificationService, NoOpNotificationService>();
}
var dataProvider = cfg["Koan:Data:Sources:Default:Adapter"];
if (dataProvider == "mongodb")
{
services.AddSingleton<IMongoIndexManager, MongoIndexManager>();
}
}
Framework Compliance
Bootstrap patterns are mandatory in Koan Framework:
- ✅ Use
AddKoan() for all framework registration
- ✅ Use
KoanAutoRegistrar for application services
- ✅ Keep Program.cs minimal (under 20 lines)
- ❌ Never manually register framework services
- ❌ Never duplicate framework configuration
- ❌ Never call
AddDbContext, AddControllers, etc. manually
The framework handles everything through auto-discovery.