Skip to content

Startup Validation

Deepstaging validates your application at startup so production failures are caught before the first request, not at 3 AM.


Development-Only Services

Most modules ship with in-memory implementations for rapid prototyping. These are marked [DevelopmentOnly]:

Service Default Implementation
IFileStore InMemoryFileStore
ISearchIndex<T> InMemorySearchIndex<T>
IVectorIndex<T> InMemoryVectorIndex<T>
ICacheStore InMemoryCacheStore
IAuditStore InMemoryAuditStore
IJobScheduler InMemoryJobScheduler
IJobStore InMemoryJobStore
INotificationChannel NoOpNotificationChannel
INotificationStore InMemoryNotificationStore
IUserContext StubUserContext

In development, these just work — register your runtime and start building. In production, the bootstrapper detects any [DevelopmentOnly] service still registered and fails at startup with a clear message:

Deepstaging: Development-only services detected in production environment.

The following services are tagged with [DevelopmentOnly] and must be replaced
with production implementations before deploying:

  - InMemoryFileStore (registered as IFileStore)
  - InMemoryAuditStore (registered as IAuditStore)

To resolve this, register production implementations
or explicitly acknowledge development services with
opts.AcknowledgeDevelopmentService<T>().

How It Works

The bootstrapper snapshots all registered services at configuration time. A BackgroundService runs at startup, checks the environment, and throws if any [DevelopmentOnly] implementation is registered in a production host.

Replacing with Production Implementations

Register a real implementation before the runtime bootstrapper — TryAdd semantics ensure the production version wins:

// Production implementation registered first
services.AddSingleton<IFileStore, AzureBlobFileStore>();
services.AddSingleton<IAuditStore, PostgresAuditStore>();

// In-memory defaults won't override
builder.AddAppRuntime();

Acknowledging Development Services

Sometimes you intentionally want a development implementation in production — for example, using InMemorySearchIndex<T> while search isn't a priority yet. Use AcknowledgeDevelopmentService<T>() to suppress the check:

builder.AddAppRuntime(options =>
{
    options.AcknowledgeDevelopmentService<InMemorySearchIndex<Product>>();
});

This is an explicit opt-in. The startup check passes, but you've documented the decision in code.


Configuration Validation

Configuration providers generate .ValidateOnStart() checks for every required property. If a config section is missing or incomplete, the app fails at startup:

Configuration section 'Smtp:SmtpSettings' is not configured.
Ensure all required values are set in deepstaging.settings.json or user-secrets.

Required properties are validated individually:

SmtpSettings.Host is required but was not configured.

These run during host startup — before any request is handled.


Secret Safety

The Auth module validates JWT configuration at startup:

  • Secret length — JWT secret must be at least 32 characters
  • Insecure patterns — known development defaults (e.g., "secret", "changeme") cause a warning in development and a startup failure in production
Insecure JWT secret detected in production.
Set a strong, unique Auth:JwtSecret value for production deployments.

Capability Validation

The generated ValidateAppRuntime() method eagerly resolves every service the runtime depends on. Call it after building the host to catch missing DI registrations immediately:

var app = builder.Build();
app.Services.ValidateAppRuntime();
app.Run();

If any capability is missing from the container, this throws before the app starts listening — not when the first handler runs.