Cratis Prologue is a tool for teams modernizing existing systems that captures signals and proposes a provisional event model for review.
Captures selected signals from an existing system — HTTP commands, database changes, and telemetry — and interprets them into a provisional event model for review.
Before the main action, a play has a prologue — the opening that recounts everything that happened before the curtain rose. Cratis Prologue is that opening act for an existing system. It stands beside an existing, running application, captures selected SQL Server and PostgreSQL changes, HTTP commands, and OpenTelemetry signals, and uses that evidence to propose an event model. The result can be reviewed and continued as a Cratis Screenplay rather than treated as recovered domain truth.
Prologue is part of the Cratis model-first layer. The event model it proposes is a starting point for building an event-sourced system with Cratis — event sourcing with Chronicle recording what happens and Arc exposing typed commands and queries.
Prologue captures structural and operational metadata rather than database row values or HTTP bodies. Some metadata can still be sensitive: HTTP observations include query strings, telemetry contains identifiers and names, and explicitly allowlisted OpenTelemetry attributes include their values. Minimize and review the capture configuration, and protect capture files and persisted observations accordingly. Observe a real system only with its owner's permission. File-based batch interpretation can run without LLM refinement; the Workbench's Discover and interpretation steps can use AI providers you configure. Before using AI features, separately authorize any capture evidence or source code sent to the configured providers.
Prologue has no dependency on Studio. The Receiver hosts the Prologue Workbench, a web interface that takes a system from a definition of its code and evidence to a provisional event model and a piece-by-piece rebuild; it stores its decisions in a Chronicle kernel and its read models in MongoDB. The Extractor and batch Interpreter need neither Chronicle nor Orleans. The Interpreter's optional resumable service mode embeds an Orleans silo for persisted session grains; consumers that only need file-based interpretation do not use that mode.
To learn the capture-to-model workflow, start with the bundled sample guide.
Once you have permission to capture your own system, follow the
Point Prologue at your system checklist.
To open the Workbench instead, you need Docker with Compose, a checkout of this repository, and available host
ports 8080 and 35000. Run this command from the repository root:
docker compose -f Source/Receiver/docker-compose.yml up --build # then open http://localhost:8080Compose builds the Receiver from the checkout and starts Chronicle's development image with bundled MongoDB.
Port 8080 serves the Receiver/Workbench; 35000 serves Chronicle. This local-development stack uses development
credentials and publishes both ports without a loopback-only binding. Restrict network access before running it;
this is not a production deployment.
The named repositories volume retains cloned repositories at /var/prologue/repos. It is not persistent storage
for observations or Chronicle/MongoDB data. Opening the Workbench starts the interface; capturing a system and
reviewing a model are separate steps.
See Running the Workbench and the walkthrough.
Three reasons, and they all line up:
- A prologue tells the backstory. It recounts what happened before the play begins. Prologue captures the system that already exists — the story so far — before you model it in Cratis.
- It comes first. Its output is an event model — the script the rest of the cast performs. Prologue always opens the show: capture and interpret, then hand the model on.
- The Cratis storytelling family. Cratis names its products after telling a story: Chronicle records new events, Arc shapes the plot, Screenplay is the script, Stage performs it, Studio storyboards it… Prologue writes the opening act from a system that predates them all. It joins the cast.
The Extractor watches an existing system from a few angles at once, correlates what it sees into captures, and the Interpreter reads those captures back into an event model:
flowchart LR
Sys["🗄️ existing system"] --> Ext{{"📜 Extractor<br/>DB · HTTP · OTel — selected metadata"}}
Ext -->|"capture .jsonl files"| Folder[["📁 mounted folder"]]
Ext -.->|"HTTP"| Rcv["📥 Receiver + Workbench"] --> Mongo[("🍃 MongoDB")]
Rcv <-.-> Chron["📚 Chronicle"]
Folder --> Interp{{"🧠 Interpreter<br/>(+ optional LLM)"}}
Interp -->|"extraction-result.json + .play"| Model["📄 provisional event model<br/>→ review · continue authoring"]
- Database change capture — watches SQL Server (CDC) and Postgres (logical replication) and records, per transaction, which tables and columns changed.
- HTTP command capture — sits in front of the system as a YARP reverse proxy and records the
POST/PUT/DELETEoperations passing through. - OpenTelemetry capture — acts as an OTLP proxy, capturing span metadata (and an allowlisted set of attributes) and forwarding the telemetry on unchanged.
The streams are correlated by a time-window heuristic and shared trace id — a command plus database transactions
committed within its window become one capture. Because timing and trace evidence can be incomplete, that
relationship is provisional. The Interpreter analyzes the captures into an ExtractionResult: candidate modules,
features, and slices with commands, events, read models, and projections.
A system made of several services can be captured as named services — each with its own databases, reverse-proxy clusters and telemetry. Observations are only correlated within a service, and each service becomes its own module. A single service needs no extra configuration; see Configuration.
| Project | Package / Image | Purpose |
|---|---|---|
Source/Contracts |
Cratis.Prologue.Contracts (NuGet) |
The capture contract — Capture, Observation, payload types, and the canonical JSON (CaptureSerialization) and capture-file (CaptureFiles) formats. |
Source/Configuration |
Cratis.Prologue.Configuration (NuGet) |
Configuration types and loading helpers for the Extractor and Interpreter's cratis-prologue.json. |
Source/Storage |
Cratis.Prologue.Storage (NuGet) |
MongoDB persistence of captures — used by the Receiver and by consumers such as Studio. |
Source/Extractor |
cratis/prologue-extractor (Docker) |
Runs next to the system being captured. Captures SQL Server CDC, Postgres logical replication, HTTP commands (reverse proxy), and OpenTelemetry — writes capture files or posts to the Receiver. |
Source/Interpreter.Contracts |
Cratis.Prologue.Interpreter.Contracts (NuGet) |
The extraction result contract — ExtractionResult and the Extracted* model tree, plus serialization helpers for the result file. |
Source/Interpretation |
Cratis.Prologue.Interpretation (NuGet) |
Deterministic heuristic model construction plus optional language-model refinement. |
Source/Screenplay |
Cratis.Prologue.Screenplay (NuGet) |
Converts an ExtractionResult into a Screenplay syntax tree and .play document. |
Source/Interpreter |
cratis/prologue-interpreter (Docker) |
Batch interpretation from capture files, or an optional Orleans-backed resumable HTTP service using MongoDB. |
Source/Receiver |
cratis/prologue-receiver (Docker) |
HTTP endpoint the Extractor can post captures to directly, instead of capturing to file. Hosts the Workbench, so it also needs a Chronicle kernel. |
Source/Workbench |
Part of the Receiver | The Workbench: its backend slices and the React frontend served by the Receiver. |
The Extractor and Interpreter use a dedicated cratis-prologue.json file. The
Cratis.Prologue.Configuration package contains their configuration types and loading helpers, so a consumer can
write the format those tools expect. The Receiver uses standard ASP.NET configuration for its Chronicle and MongoDB settings; see Receiver configuration.
{
"prologue": {
"output": { "kind": "Json", "json": { "directory": "./captures" } },
"correlation": { "windowMilliseconds": 2000 },
"sqlServer": [ { "name": "main", "connectionString": "..." } ],
"postgres": [],
"openTelemetry": { "enabled": true }
},
"llm": {
"enabled": true,
"kind": "Anthropic",
"accessToken": "sk-...",
"modelId": "claude-opus-4-6"
}
}- Everything under
prologueoutsideservicesdescribes the default service. Add named services underprologue.servicesto capture a system made of several services — each with its ownsqlServer,postgres, reverse-proxyclustersandtelemetryServiceNames. - The Extractor looks for
cratis-prologue.jsonin its working directory (override with thePROLOGUE_CONFIGenvironment variable) and binds theprologuesection. - The file is the baseline; environment variables override it. A deployed tool is configured by its host — a
container, an orchestrator, or an Aspire composition — so any setting can be supplied in the usual
double-underscore form (
Prologue__Output__Kind,Prologue__SqlServer__0__ConnectionString,ReverseProxy__Clusters__monitored__Destinations__primary__Address). UseAddPrologueConfiguration()fromCratis.Prologue.Configurationto get that precedence right in your own host. - The Interpreter reads the same file (mounted into its container at
/config/cratis-prologue.json) and binds thellmsection for optional LLM-based refinement. Supportedkindvalues:Ollama(default, native chat API),OpenAI,AzureOpenAI(themodelIdis the deployment name),OpenAICompatible(any/v1endpoint), andAnthropic— each configured with anendpointand anaccessTokenas needed; the hosted providers default to their public endpoints and models.
Extractor ──(capture .jsonl files)──▶ mounted folder ──▶ Interpreter ──▶ extraction result .json
└──────(HTTP)──▶ Receiver (Workbench) ──▶ MongoDB
└──▶ Chronicle ──▶ MongoDB (read models)
The Extractor emits one CapturedEntry per line (JSON lines), partitioned per source kind. The Interpreter
reads a folder of those files, analyzes the correlated captures, and produces an ExtractionResult and a
Screenplay .play document.
Samples/Library is a complete, ordinary ASP.NET + Entity Framework Core system — a library
with authors, members, a catalog, inventory, reservations, and lending — built with no Cratis constructs at
all, exactly the kind of system Prologue gets pointed at. Its Aspire composition wires the whole capture
pipeline around it and can generate realistic load on demand:
cd Samples/Library
aspire run # PostgreSQL
aspire run -- --database mssql # SQL ServerThen use the Simulate load command on the core resource in the Aspire dashboard and watch captures land in
MongoDB. All three capture sources are live: HTTP commands through the reverse proxy, database changes through
CDC or logical replication, and OpenTelemetry traces, metrics, and logs. See the
sample's README for the details.
dotnet build # Debug — includes inline specs
# Run each native spec executable (Debug only — Release strips inline specs):
python3 .github/scripts/run-specs.py Source/Configuration/Configuration.csproj -trait- Category=Integration
python3 .github/scripts/run-specs.py Source/Screenplay/Screenplay.csproj -trait- Category=Integration
python3 .github/scripts/run-specs.py Source/Extractor/Extractor.csproj --specifications -trait- Category=Integration
python3 .github/scripts/run-specs.py Source/Interpretation/Interpretation.csproj -trait- Category=Integration
python3 .github/scripts/run-specs.py Source/Interpreter/Interpreter.csproj --specifications -trait- Category=Integration
dotnet build -c Release # Release — warnings are errorsThe helper requires Python 3, runs the already-built Debug executable with
dotnet run --no-build --no-restore, and rejects missing, malformed, or empty xUnit XML results. The inline specs are compiled only in Debug. The Extractor and Interpreter keep their normal application entry points: pass--specificationsas the first argument in Debug to run inline specs before any application configuration or host creation. Without that argument, application startup is unchanged; Release has no spec runner.-trait- Category=Integrationexcludes integration tests. The Library sample's separate integration suite needs Docker and browsers and is not included above; see its README.
Container images are built from the repository root:
docker build -f Source/Extractor/Dockerfile -t cratis/prologue-extractor .
docker build -f Source/Interpreter/Dockerfile -t cratis/prologue-interpreter .
docker build -f Source/Receiver/Dockerfile -t cratis/prologue-receiver .dotnet build -c Release # zero warnings, zero errors
dotnet build -c Debug # compile the native spec executables
# Run all five native spec commands above, then check the wrapper:
python3 -B .github/scripts/test_run_specs.pyThis project is part of Cratis — free, MIT-licensed tools for building event-sourced and CQRS applications.
- Chronicle — event-sourcing database and runtime. Orleans-based kernel, pluggable storage (MongoDB default; PostgreSQL, SQL Server, SQLite, in-memory), language-agnostic gRPC contracts. Docs
- Chronicle clients — first-class .NET SDK, plus TypeScript, Kotlin/Java, and Elixir; Python has no client API or published package yet. AI agents connect through the Chronicle MCP server.
- Arc — opinionated CQRS framework for ASP.NET Core with commands, queries, validation, authorization, and TypeScript proxy generation. Works without event sourcing. Docs
- Components — React components aligned with Arc patterns. Docs
- CLI + Workbench — inspect and diagnose Chronicle from the terminal or the browser. Docs
- Model-first layer — Studio, Screenplay, Stage, Scene, and Prologue (this repository)
- Supporting — Fundamentals, Specifications, Synopsis, Lens, Narrator, and free AI tooling; Ensemble coming soon
- Samples — runnable event sourcing and CQRS samples for the whole stack
Everything Cratis publishes today is MIT licensed and free to use. Come talk with us on Discord.
Part of the Cratis platform · Licensed under the MIT license