Write your domain's ubiquitous language once, in
.koifiles. Koine compiles it to idiomatic, self-contained C# — value objects, entities, aggregates, invariants, the whole Domain-Driven Design toolkit.
Domain-Driven Design gives you a precise vocabulary — value objects, entities, aggregates, invariants, domain events, state machines — but in C# every one of those is a pile of mechanical boilerplate: validating constructors, value equality, identity equality, defensive copies, guard clauses, repository contracts. You write it by hand for every type. Then the model drifts from the glossary on the wiki, the "ubiquitous language" stops being ubiquitous, and the rules you cared about get buried in plumbing.
Koine is a small, readable DSL for DDD. You describe a bounded context using the same words your domain experts use, and the compiler emits the tactical code for you — correct, idiomatic, and with no runtime to reference. The model is the ubiquitous language: there is no second copy to keep in sync, and the rules stay front and centre instead of drowning in boilerplate.
The name evokes Koine Greek, the common language that became a lingua franca. The goal is to
compile one domain model to many targets. C# is the primary, most complete target; a
TypeScript emitter ships (--target typescript), a Python emitter ships (--target python →
dependency-free Python 3.11+, mypy --strict-clean; the tactical core and the strategic/CQRS layer
— read models, queries, policies, state machines, context maps/ACL), a PHP 8.1
emitter ships (--target php → dependency-free PHP 8.1, typed properties, readonly promoted properties;
the tactical core and the strategic/CQRS layer — read models, query handler seams, application
services/use cases/operations, specifications, policies, context-map ACL translators and
integration-event subscriber seams), a Rust emitter ships (--target rust → an idiomatic crate: value
objects as structs with smart constructors returning Result<_, DomainError>, smart enums as Rust
enums matched exhaustively (with Match/Switch/from_name/from_value lookups), entities and
aggregates with invariant-checked behaviors, factories that mint identities, domain events raised into
a Vec-friendly DomainEvent collection, query DTOs and read-model projections, and repositories as
traits; multi-context models compile end-to-end via crate::<module> qualification — a type
owned by two contexts and referenced from a third resolves to a deterministic canonical owner
(shared with the Java target) and raises a KOI1419 warning naming the chosen owner; depends only
on rust_decimal for money and regex for matches, plus uuid when a model uses a factory), a
Java 17 emitter ships (--target java → dependency-free, stdlib-only Java 17: value objects and
domain events as validating records with compact constructors, smart enums as Java enums (with
per-constant associated data), entities and aggregates as classes with identity equals/hashCode and
invariant-guarded behaviors, generated identities as branded records minting java.util.UUID, domain
events grouped under a per-context sealed interface DomainEvent, and repositories as interfaces —
one public type per .java file, koine.runtime.DomainException for invariant failures), a
Kotlin 2.x emitter ships (--target kotlin → idiomatic, dependency-free Kotlin/JVM: value objects
and domain events as data classes (invariants in an init block), generated identities as
@JvmInline value classes minting java.util.UUID, smart enums as enum classes (constructor val
accessors + neutral-key fromKey/tryFromKey), entities and aggregates as classes with var … private set state, identity equals/hashCode, and invariant-guarded behaviors, events under a per-context
sealed interface DomainEvent, and repositories as interfaces — optionality lives in the type system
as T? (never Optional), koine.runtime.DomainException for invariant failures), a
docs target emits living
documentation (--target docs → Markdown + Mermaid diagrams) straight from the model, an
AsyncAPI 3.0 target emits a single event-API document (--target asyncapi → channels, messages,
JSON-Schema payloads, and send/receive operations derived from the integration-event + context-map
graph), an OpenAPI target emits an API contract (--target openapi → a deterministic OpenAPI 3.1
YAML document per bounded context: value objects / read models / enums become components/schemas,
commands become POST operations and queries become GET operations, and static value-object
invariants lower to JSON-Schema validation keywords), and the parser and semantic model are kept
strictly target-agnostic so further emitters can be added without touching them.
The Koine compiler is itself compiled to WebAssembly, so you can write a model and watch it become C# without installing anything.
Koine Studio — your .koi model (left) and the C# it compiles to (right), live in the browser.
The hero above is the editor split — but Studio is a full IDE, and a single shot can't show it. Each surface below is a click away in the live Studio:
- Koine Studio — the full web IDE (every
view in the tour above), running the real compiler in your browser. (Also ships as a native
Tauri desktop app — same UI, see
tooling/koine-studio.) - Playground — a lightweight, zero-install editor that recompiles to C#/TypeScript the moment you stop typing. Great for a quick taste or for following along with the tutorial.
Both run the same parser, validator, and emitters as the
koineCLI — what you see in the browser is exactly what the build produces.
Concept Colors — one DDD concept, one color, everywhere. An aggregate is the same indigo, a value object the same blue, an enum the same amber in the explorer, on the canvas, in the code editor, in the playground, and in VS Code — driven by one palette and the language server's concept-kind signal, so the association carries from the tree straight into the source. See the Concept Colors guide.
📖 Full docs → https://atypical-consulting.github.io/Koine/ — getting started, a six-part
tutorial, the complete language reference, the feature catalogue, and the CLI. (Source in
website/; run locally with cd website && npm install && npm run dev.)
A .koi file declares one or more bounded contexts. Inside a context you declare value objects,
entities, aggregates, and enums:
context Billing {
value Money {
amount: Decimal
currency: Currency
invariant amount >= 0 "a monetary amount cannot be negative"
}
enum Currency { EUR, USD, GBP }
value Email {
raw: String
invariant raw matches /^[^@]+@[^@]+$/ "invalid email address"
}
entity Customer identified by CustomerId {
name: String
email: Email
}
aggregate Order root Order {
enum OrderStatus { Draft, Placed, Shipped, Cancelled }
value OrderLine {
product: ProductId
quantity: Int
unitPrice: Money
subtotal: Money = unitPrice * quantity // derived (computed) field
}
entity Order identified by OrderId {
customer: CustomerId
lines: List<OrderLine>
status: OrderStatus = Draft // default value
invariant status == Draft when lines.isEmpty
}
}
}
That compiles to plain C# records and classes with validating constructors, value/identity equality,
a generated OrderId/CustomerId, an IOrderRepository contract, and the Money * int operator
needed for subtotal — nothing for you to write, and nothing external to reference.
- One source of truth. The model is the ubiquitous language — no drift between the glossary and the code.
- Idiomatic, dependency-free output. Generated C# is plain, readable, and self-contained; the
Koine.Runtimemarkers are emitted alongside it, so there's nothing to install. - The whole tactical and strategic toolkit. Value objects, entities, aggregates, smart enums, invariants, commands, domain events, state machines, factories, specifications, services, policies, repositories, optimistic concurrency, the application layer (UoW, read models, CQRS), multi-file modules, context maps, integration events, and model versioning — all shipped.
- Enforced DDD reference discipline. The compiler keeps your building blocks honest: a value
object can't embed an entity or aggregate, commands and domain events carry data and identities
rather than live references, one aggregate references another only by its id, and an entity holds
domain state — never an event, read model, or query — violations are hard errors
(
KOI1601–KOI1605), not lint. - A green build proves the domain. Every construct is snapshot-tested and compiled and executed through an in-memory Roslyn meta-test, so a passing build means the generated C# is correct and usable — not just that it parses.
Requires .NET 10.
Install the CLI as a .NET global tool from NuGet, then invoke it as koine:
dotnet tool install --global Koine.Cli
koine --version
koine build templates/starters/billing/billing.koi --target csharp --out ./generatedOr run it without installing via dnx
(bundled with the .NET 10 SDK — it fetches the tool from NuGet and runs it in one shot, like npx):
dnx Koine.Cli build templates/starters/billing/billing.koi --target csharp --out ./generatedOr run it straight from a clone of this repo with dotnet run --project src/Koine.Cli -- …:
# Build everything and run the tests
./scripts/build/build.sh # or: dotnet build && dotnet test
# Compile a domain model to C#
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target csharp --out ./generated
# Add a runnable EF Core infrastructure layer (DbContext, repositories, unit of work, outbox, DI)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target csharp --out ./generated --layers domain,infrastructure
# Emit to TypeScript instead
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target typescript --out ./generated
# Add the runnable, dependency-light TypeScript infrastructure layer (in-memory repositories, unit of
# work, transactional outbox, validation/transaction behaviors, a composition-root factory)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target typescript --out ./generated --layers domain,infrastructure
# Or to Python (tactical core + strategic/CQRS: read models, queries, policies, state machines, ACL)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target python --out ./generated_py
# Add the runnable, dependency-free Python infrastructure layer (in-memory repositories, unit of work,
# transactional outbox, validation/transaction behaviors, a provider/composition helper)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target python --out ./generated_py --layers domain,infrastructure
# Or to PHP 8.1 (tactical core + strategic/CQRS: read models, queries, services, policies, ACL)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target php --out ./generated_php
# Or to Rust (multi-context + CQRS — an idiomatic crate; `cargo build` proves it compiles)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target rust --out ./generated_rs
# Or to Java (dependency-free Java 17 — records, sealed events, invariant-guarded classes; `javac --release 17` proves it compiles)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target java --out ./generated_java
# Or to Kotlin (idiomatic Kotlin 2.x/JVM — data classes, @JvmInline value-class IDs, sealed events, T? optionality; `kotlinc` proves it compiles)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target kotlin --out ./generated_kt
# Emit the opt-in C# Application layer alongside the domain (handlers, validators, query handlers, DI)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target csharp --layers domain,application --out ./generated
# Generate living documentation (Markdown + Mermaid state/class/context-map diagrams)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target docs --out ./docs
# Emit an AsyncAPI 3.0 document from the integration-event + context-map graph
dotnet run --project src/Koine.Cli -- build templates/pizzeria --target asyncapi --out ./events
# Emit an OpenAPI 3.1 spec (one <Context>/openapi.yaml per bounded context: schemas, paths, parameters)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi --target openapi --out ./api
# Just check a model parses & validates (no output)
dotnet run --project src/Koine.Cli -- build templates/starters/billing/billing.koi
# Version
dotnet run --project src/Koine.Cli -- --versionThe generated C# in ./generated is self-contained and compiles on its own. A path argument may be a
single .koi file or a directory — directory mode compiles every .koi underneath as one model,
so cross-file imports, context maps, and integration events resolve.
The C# target emits in composable layers, selected with --layers (or targets.csharp.layers in
koine.config):
| Layer | What it emits |
|---|---|
domain (default) |
The Domain model + the application/CQRS contracts — value objects, entities, aggregates, invariants, smart enums, events, the persistence-ignorant IRepository/IUnitOfWork interfaces, etc. Byte-identical to the historical output. |
infrastructure |
A runnable EF Core realization of those contracts, per bounded context: a DbContext with a DbSet per aggregate root, IEntityTypeConfiguration mappings (value objects → owned types, the versioned token → IsRowVersion, smart enums → HasConversion, strongly-typed IDs → key converters), a concrete Repository + UnitOfWork, a transactional OutboxMessage + IntegrationEventDispatcher (for a publishing context), and an Add<Context>Infrastructure(this IServiceCollection, Action<DbContextOptionsBuilder>) DI extension. Implies domain. |
# Domain contracts only (default — omit --layers for the same result)
koine build ./Models --target csharp --out ./generated --layers domain
# Domain + a regenerated EF Core infrastructure layer
koine build ./Models --target csharp --out ./generated --layers domain,infrastructureThe infrastructure is regenerated from the model on every build, so it can never silently drift
from the ubiquitous language. The provider (SQL Server, Postgres, …) is supplied by the caller through
the Action<DbContextOptionsBuilder>, so the emitter stays provider-agnostic. EF Core only in v1.
The same --layers infrastructure selector now applies to the TypeScript and Python targets
(issue #241), keeping the "write the ubiquitous language once, get a runnable stack" promise across all
three primary targets. Rather than a bundled ORM, each emits a dependency-light realization of the
domain contracts, per bounded context with at least one entity-rooted aggregate:
- a concrete repository over an injectable
AggregateStorewith a zero-dependency in-memory default (runnable in tests out of the box; swap in a persistent store to productionize) — declarative finders compile to concrete lookups; - a concrete unit of work realizing the per-context contract;
- a transactional outbox (
OutboxMessage+ a drainableIntegrationEventDispatcher) for a publishing context, so the publisher stays decoupled from its subscribers; - validation + transaction pipeline behaviors (the idiomatic analogue of the C# MediatR decorators); and
- a composition-root factory (TypeScript) / provider helper (Python) — the analogue of C#'s
Add<Context>Infrastructure.
The shared primitives live once in an emitted infrastructure-runtime.ts / koine_infrastructure.py.
The layer is off by default, so an unconfigured emit is byte-identical to the historical output; the
generated TypeScript is tsc --strict-clean and the Python is mypy --strict-clean.
Every persisted aggregate round-trips — not just collection owners. A scalar-only root, a root that
owns a scalar value object (OwnsOne), a versioned aggregate, and nested value objects all insert and
re-query correctly: each persisted root (and any value object that owns a value object) gets a private
parameterless persistence constructor EF Core materializes through, and plain scalar properties are
mapped explicitly so EF persists the read-only auto-property via its backing field. A value-object
collection (list of <ValueObject>) round-trips too: it is mapped with EF Core OwnsMany and
backed by a mutable private List<T> (exposed read-only as IReadOnlyList<T>), with
PropertyAccessMode.Field so EF can materialize owned children into it and a single-column surrogate key
so the rows persist on every provider. Scalar (String/Int/…) collections are left to EF Core's
primitive-collection convention.
Other CLI commands: check (model-versioning compatibility against a --baseline), coverage (proves
declared == emitted and doubles as a CI gate), fmt (canonical formatter), init (scaffold a
project), watch (rebuild on change), lsp (language server over stdio), and mcp (the MCP server —
stdio by default, or --http to serve it over HTTP). See the
CLI reference.
By default --target csharp stops at the application boundary: it emits the contracts —
IUnitOfWork, the I<Service> use-case interfaces, read-model projections, query objects and the
IQueryHandler<,> runtime type — but no implementations. Pass --layers domain,application to also
emit the Application layer that fills those in:
| Construct | Emitted application code |
|---|---|
| aggregate command | a <Entity><Command>Request record + a handler that loads the aggregate via its IUnitOfWork repository, invokes the behavior, and SaveChangesAsync. |
| aggregate factory | a <Entity><Factory>Request record + a handler that creates the aggregate, adds it via the repository, and commits. |
| value-object / command invariant | a FluentValidation AbstractValidator<TRequest> rule (RuleFor(...).Must(...).WithMessage(...)) rendered from the same invariant the domain enforces — not re-derived by hand. |
| query | a concrete IQueryHandler<,>; a single result keyed by the root's identity loads + projects via the To<ReadModel> mapper, other shapes throw until wired to your read store. |
| DI | an Add<Context>Application(this IServiceCollection) extension registering every handler, validator and query handler. |
Plain handlers (no third-party runtime dependency beyond FluentValidation) are the default.
Two opt-in sub-options, also settable via koine.config (targets.csharp.application.mediatr,
targets.csharp.application.mapping):
--app-mediatr— emit the MediatR shape instead:IRequest/IRequest<T>requests,IRequestHandler<,>handlers, and validation + transactionIPipelineBehavior<,>s.--app-mapping plain|mapperly— DTO/read-model mapping strategy (plainhand-rolled mappers by default;mapperlyis reserved for source-generated mapping).
With the layer off (the default), the emitted C# is byte-identical to before. Koine usecase
declarations carry no binding to a specific aggregate behavior, so the generated I<Service>
implementation throws NotImplementedException until wired — the generated command/factory handlers
are the real entry points. MediatR/FluentValidation/Mapperly are C#-emitter concerns and never
leak into the target-agnostic model.
| Construct | Emits |
|---|---|
value X { … } |
sealed record with get-only properties, a validating constructor, value equality |
entity X identified by XId { … } |
sealed class with identity-only equality + a generated XId value object (Guid by default; as natural(String|Int) or as sequence selects the strategy). A create factory either mints the id (Guid: XId.New() / XId::generate()) or, for a natural/sequence key, takes it as an explicit identity-typed parameter (create register(id: XId, …)) — KOI0808 only when a non-Guid factory provides neither. |
aggregate A root R { … } |
nested types in the <Context> namespace; the root R implements IAggregateRoot, and an I<R>Repository contract is emitted for it |
aggregate A root R versioned { … } |
the root additionally gains a get-only Version token; ConcurrencyConflictException is emitted into Koine.Runtime |
repository { operations: … ; find name(p): List<R>|R } |
tunes the root's repository — its mutating method set plus intention-revealing async finders |
service S { usecase U(p: T): R } |
an application-service interface IS with one async method per use case (Task/Task<R>); a context with aggregates also gets an IUnitOfWork |
readmodel M from Src { id; total: Int = … } |
a flat, value-equal DTO record + a static ToM(this Src src) projection mapper |
query Q(criteria): List<M>|M |
a query DTO record handled via the shared generic IQueryHandler<TQuery,TResult> |
enum E { … } |
a self-contained smart enum (sealed class: static instances, Name/Value, All, FromName/FromValue, value equality, ==/!=) |
name: Type |
a typed property + constructor parameter |
name: Type = const |
a constructor parameter with a default value |
name: Type = expr (refs siblings) |
a derived, get-only computed property (not in the constructor) |
invariant <expr> "msg" |
a constructor guard that throws DomainInvariantViolationException |
invariant <expr> matches /re/ … |
a regex guard (Regex.IsMatch) |
invariant <body> when <cond> |
a conditional guard (if (cond && !body) throw) |
The full construct set (commands, domain events, state machines, factories, specs, services, policies, context maps, integration events, model versioning) is mapped construct-by-construct to the C# it emits in the feature catalogue.
Small and pure (no statements, no I/O): comparisons (== != < <= > >=), arithmetic (+ - * /),
logical (&& || !), member access (lines.isEmpty), regex matches /…/, a when guard,
identifiers, and literals.
| Koine | C# | Notes |
|---|---|---|
String |
string |
|
Int |
int |
|
Decimal |
decimal |
money / quantities |
Bool |
bool |
|
Instant |
DateTimeOffset |
|
List<T> |
IReadOnlyList<T> |
defensively copied in the constructor |
<XId> |
generated ID value object | a record wrapping a Guid |
- Soft keywords. Most Koine keywords (
context,value,entity,aggregate,enum,command,service,policy,repository,readmodel,query,import,module, …) may be used as field names, and declaration keywords additionally as type names and in expressions. Onlymatchesandinvariantremain reserved; keywords are not usable in the few hard-Identifierpositions (a type/command/state/enum-member name). Because<-and->are atomic operators, a comparison against a negative operand needs a space (x < -1, notx<-1). - Reserved type names.
List,Set,Map, andRangeare built-in generics; a user type may not take one of these names.
The pipeline is strictly layered so backends are pluggable:
.koi source
→ Lexer/Parser (ANTLR, generated from Grammar/KoineLexer.g4 + KoineParser.g4)
→ KoineModelBuilderVisitor → semantic model (Ast/, target-agnostic)
→ SemanticValidator (Semantics/) → diagnostics with line/column
→ IEmitter (Koine.Emit.CSharp, .TypeScript, .Python, .Php, .Rust, …) → source files
Koine.slnx
├── src/
│ ├── Koine.Compiler/ # parser, Ast/, semantics + emit CONTRACTS (IEmitter, EmitterRegistry, …)
│ │ ├── Grammar/ # KoineLexer.g4, KoineParser.g4
│ │ ├── Ast/ # semantic model + ModelIndex (NO target-specific concepts)
│ │ ├── Parsing/ # KoineModelBuilderVisitor, SyntaxErrorListener
│ │ ├── Semantics/ # SemanticValidator (+ focused validators)
│ │ ├── Emit/ # IEmitter, IEmitterProvider, EmitterOptions, EmitterRegistry, EmitterLoader
│ │ ├── Diagnostics/ # Diagnostic
│ │ └── Services/ # KoineCompiler (orchestrator) + LSP/tooling backend
│ ├── Koine.Emit.Common/ # shared emitter helpers (FactoryIdBinding, MarkdownDoc, OperatorNeedsAnalyzer)
│ ├── Koine.Emit.CSharp/ # CSharpEmitter (primary target) ── each emitter its own assembly (#861)
│ ├── Koine.Emit.TypeScript/ # TypeScriptEmitter
│ ├── Koine.Emit.Python/ # PythonEmitter (tactical core + strategic/CQRS layer)
│ ├── Koine.Emit.Php/ # PhpEmitter (tactical core + strategic/CQRS layer, PHP 8.1)
│ ├── Koine.Emit.Rust/ # RustEmitter (multi-context + CQRS read side)
│ ├── Koine.Emit.Glossary/ # ubiquitous-language glossary
│ ├── Koine.Emit.Docs/ # living documentation (Markdown + Mermaid diagrams)
│ ├── Koine.Emit.AsyncApi/ # AsyncApiEmitter (AsyncAPI 3.0 doc from integration events)
│ ├── Koine.Emit.OpenApi/ # OpenApiEmitter (OpenAPI 3.1 spec per bounded context)
│ ├── Koine.Emit.All/ # aggregator: BuiltInEmitterProviders.All — one reference, all targets
│ ├── Koine.Cli/ # `koine` command-line tool
│ ├── Koine.Wasm/ # the compiler as a WebAssembly module (Playground + Studio web)
│ └── Koine.Mcp/ # MCP server for AI agents
└── tests/
└── Koine.Compiler.Tests/ # parsing, semantic, snapshot (Verify), Roslyn compile meta-tests
The grammar is split into a separate lexer grammar so that matches /regex/ can use a lexer mode —
this lets a regex literal be read as a single token without colliding with the / division operator.
The single most important invariant: no C#-specific concept lives in Ast/ — that is what keeps
multiple emitters possible.
Koine.Wasm compiles the whole compiler to WebAssembly for the
Playground and
Studio. The deployed bundle is
AOT-compiled (opt-in KoineWasmAot MSBuild property, switched on by the docs-deploy job); a bare
dotnet build/publish and the dev inner loop stay on the fast interpreter build, so only the
deployed/CI path pays the slower AOT publish. Measured trade-off (pizzeria template via
node src/Koine.Wasm/smoke-test.mjs --bench, best/median of 10 warm runs):
| Bundle | pizzeria compile (best / median) | _framework raw / gzip |
publish (warm) |
|---|---|---|---|
| Interpreter (dev default) | 30 / 39 ms | 6.6 MB / 2.3 MB | ~4–10 s |
| AOT (deployed) | 7 / 8 ms | 20.3 MB / ~5.9 MB | ~24–32 s |
≈5× faster per-keystroke compile for a ~3× larger (browser-cached, +3.6 MB gzipped) one-time
download — worth it for the in-browser compiler, and the AOT-vs-interpreter lever
#219 needs to pick a mobile fallback.
WasmStripILAfterAOT is a no-op here (the compiler + ANTLR are rooted whole and reflect, so their IL
metadata is retained either way). Full rationale: the comment block in src/Koine.Wasm/Koine.Wasm.csproj
(issue #327).
Koine.Compiler ships as a NuGet library with a frozen, contract-gated public API (guarded by
Microsoft.CodeAnalysis.PublicApiAnalyzers, so unintended public surface can never ship silently).
You can embed the compiler, write your own analyzers, and ship your own emitters.
Embed the compiler. Koine.Compiler carries the parser, semantic model, and emit contracts; the
built-in emitters ship in Koine.Emit.All (the BuiltInEmitterProviders.All aggregator). Add both to
resolve a target like csharp, then compile a model in process:
dotnet add package Koine.Compiler
dotnet add package Koine.Emit.Allusing Koine.Compiler; // BuiltInEmitterProviders (from Koine.Emit.All)
using Koine.Compiler.Emit; // EmitterRegistry, EmitterOptions, EmittedFile
using Koine.Compiler.Services; // KoineCompiler
var registry = new EmitterRegistry(BuiltInEmitterProviders.All); // built-in providers (csharp, typescript, …)
registry.TryCreate("csharp", EmitterOptions.Empty, out var emitter);
var result = new KoineCompiler().Compile(source, emitter); // string source or IReadOnlyList<SourceFile>
if (result.Success)
foreach (EmittedFile file in result.Files)
Console.WriteLine($"{file.RelativePath}\n{file.Contents}");
else
foreach (var d in result.Diagnostics)
Console.Error.WriteLine(d);Write an analyzer. Implement IModelAnalyzer — a target-agnostic check over the resolved
semantic model that reports Diagnostics:
using Koine.Compiler.Diagnostics;
using Koine.Compiler.Semantics;
public sealed class NoLowercaseTypeNames : IModelAnalyzer
{
public string Id => "acme.no-lowercase-type-names";
public void Analyze(AnalyzerContext context)
{
foreach (var ctx in context.Model.Contexts)
foreach (var type in ctx.Types)
if (char.IsLower(type.Name[0]))
context.Report(Diagnostic.Warning("ACME001",
$"type '{type.Name}' should be PascalCase", type.Span));
}
}Wire it in code via new KoineCompiler([new NoLowercaseTypeNames()]), or let the CLI discover it:
point the analyzers key in koine.config at the assembly that contains it (a comma-separated list of
paths) — any public parameterless-constructible IModelAnalyzer is loaded and run after the built-ins.
# koine.config
analyzers = ./build/Acme.KoineAnalyzers.dllShip an emitter. Implement IEmitterProvider (returning an IEmitter for your target) to add a
brand-new backend without forking the compiler:
using Koine.Compiler.Ast;
using Koine.Compiler.Emit;
public sealed class GoEmitterProvider : IEmitterProvider
{
public string Target => "go";
public IEmitter Create(EmitterOptions options) => new GoEmitter(options);
}
public sealed class GoEmitter : IEmitter
{
public IReadOnlyList<EmittedFile> Emit(KoineModel model) => /* … */;
}The CLI loads external emitters from the emitters key in koine.config (same discovery rules as
analyzers), so koine build Models/ --target go resolves your provider through the same
EmitterRegistry the built-in targets use.
# koine.config
emitters = ./build/Acme.GoEmitter.dll-
Web IDE. Koine Studio and the Playground run the compiler in the browser (WebAssembly) — see See it run above. Studio also ships as a native desktop app (
tooling/koine-studio). Its built-in AI copilot makes the.koiit generates valid by construction: a grammar-capable local model is constrained to Koine's grammar (GBNF token masking), hosted APIs fall back to bounded parse-and-repair against the real parser, and Apply to editor stays disabled until the model parses. In a folder workspace it can also edit across files in one turn — the assistant reads the workspace and stages full-file changes (new files land under the folder root) that you review as a per-file diff and apply together; nothing touches disk until you accept. See the Assistant guide. -
Editor support.
tooling/koine-textmateis a TextMate grammar for.koithat works in JetBrains Rider and VS Code. For live error squiggles, completion, hover docs, and go-to-definition, run the bundled language server (koine lsp) — it reuses the compiler's own parser + validator, so editor diagnostics matchkoine build, and hover/navigation resolve across every.koifile in the workspace. Setup intooling/README.md. -
AI agents (MCP server).
src/Koine.Mcpis an MCP server (koine-mcp) that lets an AI agent author a complete domain in.koi: tools tokoine_validate,koine_compile(csharp/typescript/python/php/glossary/docs), andkoine_format, pluskoine_referenceandkoine_examplesso the agent learns the language. Install withdotnet tool install -g Koine.Mcp, then register it over stdio (the default, for editor-spawned clients like Claude Desktop):{ "mcpServers": { "koine": { "command": "koine-mcp" } } }Or serve it over HTTP (Streamable HTTP/SSE) so any client connects by URL — no DLL paths.
koine mcp --http(orkoine-mcp --http) binds loopback on an OS-assigned port and prints the endpoint; an LM Studiomcp.jsonthen collapses to one line (use a tool-capable model):{ "mcpServers": { "koine": { "url": "http://127.0.0.1:PORT/mcp" } } }On the desktop, Koine Studio launches that HTTP server for you and shows the ready-to-paste
mcp.jsonunder Settings → Assistant (a "Copy mcp.json" button). From a checkout,./scripts/install-mcp/install-mcp.sh(or.ps1/.cmd) packs, installs, and registers the stdio server with Claude Desktop in one step. Full tool list + the HTTP recipe in the MCP guide.
In spec-driven development, Koine is the deterministic implementation step. An AI agent authors a
small .koi model (over the MCP server above) and a human reviews that model — not thousands of
lines of generated code. From there, koine build compiles the reviewed spec to idiomatic C# with no
AI in the loop, so the implementation is a milliseconds-long, zero-token compile instead of a
generate-test-fix loop. Completeness isn't hoped-for: the compiler emits every declared type, and
koine coverage proves declared == emitted (exiting non-zero if anything is missing, so it doubles
as a CI gate). See the
Model-as-spec guide.
- .NET 10, C#
- ANTLR 4 via
Antlr4BuildTasks+Antlr4.Runtime.Standard(visitor, not listener) - Tests: xUnit, Verify snapshots, and an in-memory Roslyn meta-test that compiles and executes the emitted C#.
Koine ships the full tactical and strategic DDD toolkit and nine emitter targets alongside C# —
six languages (TypeScript, Python, PHP 8.1, Rust, Java 17, Kotlin 2.x) plus three
spec/doc targets (docs — Markdown + Mermaid, AsyncAPI 3.0, and OpenAPI 3.1) — the editor
tooling (TextMate grammar, koine lsp language server, and the fmt / init / watch commands), and
model-as-spec coverage (koine coverage proves declared == emitted and doubles as a CI gate, also
exposed as the koine_coverage MCP tool). The
feature catalogue maps every
construct to the C# it emits. The current version is shown by the NuGet badge above.
Recently landed: Java 17 and Kotlin 2.x emitters (dependency-free, compile-verified via
javac/kotlinc); DDD-kind metadata surfaced across the TypeScript/Python/PHP emitters
(#1170); and the CLI is now published to
NuGet.org via Trusted Publishing — dotnet tool install --global Koine.Cli
(#1179). The full roadmap lives in
USER-STORIES.md.
templates/ is the single, CI-validated source of truth for Koine's example domains.
A template is a folder holding one or more .koi files plus a template.json
manifest describing it. Every template is compiled green and every manifest is schema-validated on each
build (TemplatesValidationTests), and the same set powers three places: the demo (below),
Koine Studio's template gallery, and the Playground
sample picker.
Templates come in four difficulty levels — starter, beginner, intermediate, advanced (the schema reserves all four; beginner is unused so far):
| Template | Difficulty | What it models |
|---|---|---|
starters/billing |
starter | Money, orders, and invariants — the canonical Koine starter |
starters/ordering |
starter | An aggregate with a state machine — renders as a diagram |
starters/contextmap |
starter | Two bounded contexts and the relationship between them |
starters/values |
starter | Smart enums with data, quantities, ranges, and derived fields |
ticketing |
intermediate | A help-desk workflow with a ticket lifecycle and a cross-context SLA policy |
pizzeria |
intermediate | A six-context pizza shop (menu, ordering, kitchen, delivery, payment, promotions) + an external Gateway |
library |
intermediate | A lending library across five contexts — Book vs BookCopy, loans, reservations, fines |
saas-subscription |
advanced | Multi-tenant subscriptions with trials, metered quotas, dunning, and a payment-provider ACL |
Each template folder carries a template.json validated against
templates/template.schema.json:
| Field | Meaning |
|---|---|
id |
Stable identifier — must equal the folder name |
name |
Human-readable display name |
tagline |
One-line summary shown in listings |
description |
A paragraph describing what the template models |
difficulty |
starter · beginner · intermediate · advanced — used to order and badge templates |
tags |
Free-form keywords for search and filtering |
contexts |
The bounded contexts the template defines |
coreAggregate |
The headline aggregate that anchors the template |
entryFile |
The primary .koi file to open first — must name a file in the folder |
teaches |
The Koine concepts / DDD patterns a learner picks up |
icon |
An icon identifier (emoji or icon name) for the template card |
demo/ consumes the generated C# from a real .NET project. It compiles straight from the
templates/pizzeria template — a pizzeria domain across six bounded contexts
(plus an external card Gateway) tied together by a context map — so dotnet build demo/Pizzeria.Domain
regenerates and compiles it, which makes building the demo the end-to-end proof that the pizzeria
template emits compiling, runnable C#. Between the .koi template and Samples.cs it exercises the
full shipped feature set — see demo/README.md for the feature-to-location map.
Contributions are welcome! See CONTRIBUTING.md for how to build, test, and submit
a change, and please follow our Code of Conduct. Security issues should be reported
privately — see SECURITY.md. Notable changes are tracked in CHANGELOG.md.
Koine is licensed under the Apache License 2.0 — see LICENSE. Copyright © 2026 Atypical
Consulting / Philippe Matray.




