Skip to content

About

Document-sourced CQRS kernel for .NET 10 - PostgreSQL for servers, SQLite for embedded/desktop, one shared model.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Papuma Kernel

Papuma Kernel

CI .NET 10 PostgreSQL 18+ SQLite embedded 2.1.0 MIT

Your documents are the truth. The change feed follows automatically.

Papuma Kernel is a small application kernel for .NET 10, built as two independent products sharing one model. You store plain C# objects as JSON documents; the kernel derives a reversible change feed in the same atomic transaction, applies your privacy policies before anything is recorded, and delivers every change to your handlers in causal order. You get what event sourcing promises — a complete, auditable history and reactive projections — without the replay obligation, the mandatory event modeling, or the GDPR headache.

This is document-sourced CQRS: the document is the source of truth, the feed is derived from it (never the other way around).

  • Papuma.Kernel — PostgreSQL ≥ 18. One primitive makes the write path work: RETURNING OLD/NEW gives the before- and after-state in a single statement, so there is no outbox to forget and no window in which the diff can lie. Multi-tenant, row-level-security-isolated, built for servers.
  • Papuma.Kernel.Local — SQLite, no server. Same documents, same reversible diffs, same change feed, same privacy policies — for single-writer desktop and mobile apps that have no business running a database server. See the design rationale for what's shared and what's deliberately different per engine.

The 1.0 reboot — rebuilt from scratch on PostgreSQL ≥ 18; no migration path from 0.x (see CHANGELOG). The marketing one-pager with diagrams and measured numbers lives in docs/factsheet.md.

Sixty seconds to running

builder.Services
    .AddPapumaKernel(o =>
    {
        o.ConnectionString = config.GetConnectionString("papuma");
        o.Model(m => m
            .Document<User>(d => d.UniqueKey(x => x.Email))
            .Event<UserLoggedIn>(e => e.Retention(TimeSpan.FromDays(90))));
    })
    .AddChangeHandler<UserProjection>();

// Write — atomic, versioned, policy-applied, feed included:
await using var session = store.OpenSession(ScopeContext.Tenant("acme"));
await session.SaveAsync(user, expectedVersion: 0);
await session.AppendAsync(new UserLoggedIn(user.Id, "web"));
await session.CommitAsync();

Schema, indexes and row-level security are created idempotently at startup. There is no migration step. There is no step two.

No server, same model — Papuma.Kernel.Local mirrors the same call shape:

builder.Services
    .AddPapumaKernelLocal(o =>
    {
        o.DbPath = Path.Combine(appDataDir, "app.db");
        o.Model(m => m
            .Document<User>(d => d.UniqueKey(x => x.Email))
            .Event<UserLoggedIn>(e => e.Retention(TimeSpan.FromDays(90))));
    })
    .AddChangeHandler<UserProjection>();

await using var session = store.OpenSession(ScopeContext.Tenant("local"));
await session.SaveAsync(user, expectedVersion: 0);
await session.CommitAsync();
dotnet build Papuma.Kernel.slnx
dotnet test  Papuma.Kernel.slnx   # Postgres suite needs Docker/Podman; SQLite suite needs nothing extra

Learn by building: docs/tutorial.md · terse API tour: docs/getting-started.md.

What ships in the box

Shared between both kernels (same code, Papuma.Kernel.Core, not two implementations pretending to agree):

  • Atomic write primitives — versioned Save/Delete, single-statement patches (Set/Remove/Increment), set-based bulk ops, append-only rollback, and a bounded counter that cannot oversell (proven under 12-way concurrency on Postgres).
  • Privacy by construction — field policies (Redact/Hash/Reference/ DoNotTrack) applied inside the write transaction, so sensitive values never reach the feed, the logs, the traces, or AI consumers. The same policies project onto masked reads (ADR-016).
  • GDPR tooling — Art.-30 data inventory from the metamodel, Art.-15/20 subject export, history redaction with a mandatory audit trail (gdpr.md).
  • Processing engine — per-handler delivery in causal order (per document by version), nothing skipped (snapshot cursor), persisted checkpoints, retry/backoff, poison handling. Projections declare themselves and rebuild when their version rises; effect handlers are never reset and can start at the feed head (ADR-024).
  • Event log — first-class facts (UserLoggedIn) beside state changes, same transaction, same policies, per-type retention.
  • Schema evolution — lazy upcasting with version guards, same Upcast(fromVersion, …) registration on either kernel.
  • Declared keys — unique and lookup keys as partial expression indexes, including composite keys over several fields (UniqueKey(x => new { x.ProjectId, x.Number }), ADR-020).

Postgres-only (Papuma.Kernel):

  • Two-layer multi-tenancy — explicit scope predicates plus PostgreSQL row-level security; fail-closed (a missing scope yields empty reads, never a leak). Your own tables in the same database join in through papuma.scope_visible/scope_writable (ADR-019).
  • Testing that exercises RLS — Papuma.Kernel.Testing runs tests as a non-superuser role and drains feeds deterministically (ADR-021).
  • Multi-instance leader failover via FOR UPDATE SKIP LOCKED — no extra infrastructure for concurrent processor instances.
  • AI-ready — an MCP server over the diagnostics and scope-bound, policy-masked reads (read-only by default).
  • Observability — BCL Meter + ActivitySource (zero vendor deps), OpenTelemetry-ready, feed-lag health check, and an embedded live dashboard (MapPapumaDashboard()).

SQLite-only (Papuma.Kernel.Local):

  • No server, no daemon, no port — one file, opens in milliseconds, single writer enforced by the OS file lock, not application code.
  • In-process feed wakeup — a SqliteChangeNotifier replaces LISTEN/NOTIFY; no network round trip, near-instant delivery.
  • WAL journal mode + busy timeout on every connection, applied through one shared factory — a background feed processor writing doesn't block the UI reading, verified empirically, not assumed.
  • Genuinely simpler where the single-writer topology allows it: no RLS machinery, no snapshot cursor — see the design rationale for exactly what's dropped and why that's safe, not a shortcut.

Packages

Package What it is
Papuma.Kernel PostgreSQL kernel — store, diff engine, policies, feeds, hosting
Papuma.Kernel.Local SQLite kernel — same model, single-writer embedded/desktop use, no server
Papuma.Kernel.AspNetCore optional ASP.NET Core integration — tenant middleware, feed-lag health check, embedded dashboard
Papuma.Kernel.Mcp optional MCP server — read-only diagnostics + masked content tools for agents
Papuma.Kernel.Testing optional integration-test support — PostgreSQL 18 test database with a non-superuser role (RLS applies), feed draining; test-framework agnostic
Papuma.Kernel.FSharp optional F# facade — Result-returning writes, an IAsyncDisposable-safe session runner; works with either kernel (details)

Papuma.Kernel.Core (diff engine, policies, model, validation) is shared internally by the two kernels; it is not independently published — its assembly ships embedded inside whichever kernel package you install.

Samples

Sample Shows
shop-minimal-api the breadth — a mini shop touching every kernel concept: approval workflows with humans in the loop, saga compensation, inventory that cannot oversell, realtime UI push, the MCP endpoint and the dashboard
event-modeled-slices the shape — one vertical slice of each Event Modeling type (Command/View/Automation) with a pure, infrastructure-free Decider test
polyglot-consumers the feed as a cross-language contract — Python (psycopg3) and Go (pgx) consumers, ~50 lines each
fsharp-local-todo Papuma.Kernel.FSharp end to end, on Papuma.Kernel.Local (SQLite — no server, no Docker)

The first three samples run against Papuma.Kernel (Postgres); fsharp-local-todo is the first sample against Papuma.Kernel.Local.

Recipes

Pattern guides on kernel primitives (docs/recipes): workflow-saga · realtime-ui-notifications · same-database-read-models (RLS on your tables) · projection-schema (create, evolve, rebuild) · external-read-models (search/vector/cache) · nats-bridge · field-level-encryption · event-modeling-slices · ai-consumers · backup-restore (backup, restore, replication).

Documentation map

Maturity, stated plainly

2.1.0 — the Postgres kernel's design is complete (13 implementation phases, every decision recorded as an ADR, every identified risk closed with a test or a measurement; the full integration suite runs against real PostgreSQL 18 on every CI build), but it has not yet carried production traffic. Best fit today: internal line-of-business systems and new products built by teams that control their PostgreSQL version. For regulated, mission-critical workloads, run a pilot first — the observability to judge it is built in.

Papuma.Kernel.Local is newer and should be read as such: full parity with the Postgres kernel's write/read/patch/GDPR/rollback/feed-processing surface, 58 tests green against the real SQLite engine (including empirically verified driver behavior, not assumed — WAL mode, busy timeouts, expression index matching), but zero hours of real application traffic yet and no performance benchmarks (only correctness). Good fit today for exactly what it was built for: local desktop/mobile storage where a server is the wrong tool. Treat it as earlier-stage than the Postgres kernel until it's proven the same way.

What it deliberately is not

  • Not an ORM or query DSL — lookups run over declared, indexed keys; anything richer is a projection or a SQL view (enforced, not just advised). True on both kernels.
  • Not event sourcing — the document is the truth, the feed is derived.
  • Not one database-agnostic abstraction pretending to support everything. Papuma.Kernel exploits PostgreSQL ≥ 18 without apology; Papuma.Kernel.Local is an independent SQLite implementation sharing the model, not a storage seam bolted under one codebase — see why that's a different (and deliberate) design.
  • Not a workflow/BPMN engine — durable state machines and timers are documented patterns on kernel primitives (with running sample code).

Contributing

Issues and pull requests are welcome — start with CONTRIBUTING.md; it covers the build prerequisites (Docker for the PostgreSQL suite), the conventions, and when a change needs an ADR. Security reports go through SECURITY.md, never a public issue.

License

MIT — see LICENSE.

About

Document-sourced CQRS kernel for .NET 10 - PostgreSQL for servers, SQLite for embedded/desktop, one shared model.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages