1
2
3
4
5
6
7
8
9
10
grimoire-api/
βββ src/
β βββ Grimoire.Core β Domain layer
β βββ Grimoire.Infrastructure β Data + services layer
β βββ Grimoire.Api β HTTP API layer
β βββ Grimoire.Consumer β Client library
βββ tests/
βββ Grimoire.Tests β Unit tests
βββ Grimoire.IntegrationTests β Integration tests
βββ Grimoire.E2eTests β End-to-end tests
1
2
3
4
5
6
Grimoire.Api
βββ Grimoire.Core (entities, interfaces)
βββ Grimoire.Infrastructure (EF Core, encryption, repositories)
βββ Grimoire.Core
Grimoire.Consumer (standalone β no dependency on Api or Infrastructure)
Grimoire.Consumer is intentionally isolated. It communicates with the API over HTTP and has no compile-time coupling to server-side code.
The innermost layer. Contains only:
Application, AppEnvironment, Secret, SecretVersion, ConfigurationEntryIEncryptionService and four repository interfaces (IApplicationRepository, IEnvironmentRepository, ISecretRepository, IConfigurationRepository)No framework references. No EF Core. No dependency injection.
Implements the Core interfaces:
GrimoireDbContext β EF Core DbContext with Fluent API entity configurations, global query filters for soft-delete, and automatic migration on startupApplicationRepository, EnvironmentRepository, SecretRepository, ConfigurationRepositoryAesGcmEncryptionService β AES-256-GCM encryption with HKDF key derivationSlugService β Converts application names to URL-safe slugsGrimoireDbContextFactory β Design-time EF Core factory for running migrations from the CLIThe ASP.NET Core 10 host:
Management/* β full CRUD behind Bearer token authConsumer/* β read-only behind API key authAdminApiKeyMiddleware and ConsumerApiKeyMiddlewaremanagement and consumer)A standalone .NET library for consuming the API:
GrimoireSecretClient β typed HTTP client for individual secret readsGrimoireConfigurationClient β implements IConfigurationProvider + IConfigurationSourceGrimoireConfigurationExtensions β AddGrimoire() extension on IConfigurationBuilderEvery HTTP request flows through:
1
2
3
4
5
6
7
8
9
10
11
12
13
Request
β
βββΊ Serilog request logging
βββΊ Exception handler (ProblemDetails)
βββΊ CORS
βββΊ AdminApiKeyMiddleware
β (validates Bearer token for /api/management/*)
βββΊ ConsumerApiKeyMiddleware
β (validates X-Api-Key for /api/consumer/*)
β (stores matched Application in HttpContext.Items)
βββΊ Controllers
βββΊ Management controllers (FluentValidation, repository calls)
βββΊ Consumer controllers (repository + decryption)
Both middleware components return 401 Unauthorized immediately if:
They do not call next() in the rejection path, so no controller code runs.
ConsumerApiKeyMiddleware resolves the Application entity from the database, verifies the API key hash, and stores the entity in HttpContext.Items["GrimoireApplication"]. Consumer controllers read it from there rather than re-querying the database.
EF Core migrations are stored in src/Grimoire.Infrastructure/Persistence/Migrations/.
On startup, Program.cs runs:
1
2
3
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<GrimoireDbContext>();
db.Database.Migrate();
This applies any pending migrations automatically. The SQLite file is created on first run.
The API exposes two separate Swagger documents at runtime:
| Doc | URL | Audience |
|---|---|---|
| Management | /swagger/management/swagger.json |
Administrators |
| Consumer | /swagger/consumer/swagger.json |
Application developers |
The Swagger UI is available at /swagger in Development environment only.