The complete reference for writing DevMatrix specifications. From data types and annotations to flows, state machines, and infrastructure.
Overview
DMX (DevMatrix Specification Language) is a declarative language for defining full-stack microservices. A single .dmx file describes entities, APIs, business flows, state machines, security, infrastructure, and more — then compiles deterministically to production-ready code.
DMX is not a template engine. It is a compiler IR: every construct maps to concrete backend code through deterministic passes. There is no interpolation, no scripting, and no runtime interpretation.
Lexical Structure
DMX source files use UTF-8 encoding. The language is whitespace-insensitive (indentation is conventional, not syntactic). Blocks are delimited by curly braces {}.
Comments
Line comments start with //. Block comments use /* ... */.
Comments
// This is a line comment
/* This is a
block comment */
Strings
Strings are double-quoted only. Backslash escapes are supported: \", \\, \n.
Numbers
Integer literals: 42, 0. Float literals: 3.14. No hex/octal support.
Identifiers
PascalCase for entities and types (UserProfile). snake_case for fields and keywords (created_at). UPPER_SNAKE_CASE for enum values (ACTIVE, PENDING_REVIEW).
Operators
= — assignment. -> — state transition or mapping. * — wildcard/all.
Annotations
Annotations start with @ followed by an identifier. Some accept parenthesized arguments: @default("active"), @fk(User.id).
DMX supports 18 data types that map to database column types and API schema types.
Name
Description
UUID
Universally unique identifier (v4)
String
Variable-length text, up to 255 characters
Text
Unlimited-length text (CLOB/TEXT)
Integer
Whole number (32-bit signed)
Float
IEEE 754 floating-point
Decimal
Arbitrary-precision decimal
Boolean
true or false
Date
Calendar date without time (YYYY-MM-DD)
DateTime
Date and time with timezone (ISO 8601)
Time
Time of day without date
JSON
Arbitrary JSON object or array
Bytes
Binary data (BLOB)
Array
Ordered collection of a single type
Enum
Enumerated set of named values
Char
Fixed-length character string
Point
Geographic point (latitude, longitude)
Polygon
Geographic polygon boundary
Geometry
Arbitrary geometric shape
Every entity field has a name, a type, and optional annotations.
Field declarations with types
entity User {
id UUID @pk @default($computed.uuid)
email String @unique @not_null
name String @not_null
bio Text @nullable
age Integer @check("age >= 0")
balance Decimal @default(0)
is_active Boolean @default(true)
joined_at DateTime @default($computed.now)
metadata JSON @nullable
role Enum(ADMIN, MEMBER, GUEST) @default(MEMBER)
location Point @nullable
}
Annotations
Annotations modify the behavior of fields, entities, APIs, state machines, and ClickHouse columns.
Field Annotations
Name
Description
Syntax
@pk
Primary key
@pk
@not_null
NOT NULL constraint
@not_null
@unique
Unique constraint
@unique
@nullable
Allows NULL values
@nullable
@default
Default value
@default("value") or @default($computed.now)
@fk
Foreign key reference
@fk(Entity.field)
@check
Check constraint expression
@check("age >= 18")
@required
Required in API input
@required
@sensitive
PII / secrets — excluded from logs
@sensitive
@computed
Server-computed, not in API input
@computed
@json_list
Store as JSON array column
@json_list
@immutable
Cannot be updated after creation
@immutable
@indexed
Create a database index
@indexed
@freetext
Free human text — fixes the injection security class
@freetext
@scoped
ABAC row-filter: binds this column to a principal claim (scoped / scope_match strategy)
@scoped(claim="sub")
@scope_anchor
ABAC scope anchor: when a child has N>1 @fk to the same @scoped parent, marks WHICH FK anchors the inherited transitive row-scope (else first-declared anchors)
@fk(User.id) @scope_anchor
@scope_reverse
ABAC reverse scope marker: on a junction (M2M bridge) entity's parent-@fk column, grants the parent reverse row-visibility to rows reachable through the junction (reverse-EXISTS RLS). Junction (@role(membership)) @fk columns only.
@fk(Org.id) @scope_reverse
@format
OpenAPI format hint (closed vocab: email, uuid, date-time, url, …)
@format(email)
@pattern
Explicit validation regex
@pattern("^[A-Z]{3}$")
@validation_pattern
Validation pattern type (snapshot, calculation, immutability, constraint, relationship)
@validation_pattern(calculation)
@seed_template
Seed-data value shape (email, personal_name, url, phone, description, currency_code, timestamp)
@seed_template(email)
@field_purpose
Semantic field purpose (closed allowlist: auth_failed_attempts, auth_locked_until, auth_last_login, auth_email_verified, password) — declares a capability-owned auth field (or the credential password) so emitters route on the structural role instead of name-matching
@field_purpose(auth_locked_until)
@deprecated
Marks field deprecated (since / until / replacement)
DMX specs are composed of top-level blocks. Each block defines a different concern of the service.
service
The root block that defines a microservice. Contains all other blocks.
Service definition
service UserService {
version = "1.0.0"
port = 8000
api_prefix = "/api/v1"
database = "postgresql"
// ... entities, api, flows, etc.
}
identity
Optional nested sub-block inside service { ... } that lets the spec author override canonical-derived service-identity fields. Mirrors the MicroservicesIR sub-IR hierarchy (ServiceIdentityIR / GatewayIR / ArchitectureIR). Each attribute is closed-taxonomy parse-time enforced. When the block is absent the producer derives every field canonically (service_id from the service block name, service_slug + internal_hostname from the canonical lowercase slug, internal_port from the infrastructure block, etc.). When present, every set attribute lands on the corresponding sub-IR overridden_fields frozenset sentinel for cross-build provenance.
All 12 spec-declarable attrs in a single block. Each sub-block keeps the canonical-strict closed taxonomy enforcement (GATEWAY_MODE = passthrough | auth_precheck; ARCHITECTURE_MODE = standalone | microservices | mixed; ARCHITECTURE_MODE_SOURCE = explicit | inferred | default).
Spec authors only pin the attributes they want to override; the rest canonical-derive. Here the platform pins a service version explicitly, leaving every other identity field on its canonical default.
Identity overrides — single attribute
service AuthService {
version = "1.0.0"
port = 8000
api_prefix = "/api"
identity {
service_version = "2.4.1"
}
}
entity
Defines a data model. Fields have a name, type, and optional annotations. Entities compile to database tables, ORM models, and Pydantic schemas.
Entity with annotations
entity Order @audit @soft_delete {
id UUID @pk @default($computed.uuid)
customer_id UUID @fk(Customer.id) @not_null
total Decimal @not_null @check("total >= 0")
status Enum(DRAFT, PENDING, PAID, SHIPPED, CANCELLED) @default(DRAFT)
notes Text @nullable
}
api
Defines REST, GraphQL, or WebSocket endpoints for an entity. Route-level RBAC via permissions = ["resource:action"] is enforced end-to-end — the compiler emits the permission check in the generated service (ADR-354), not advisory metadata. Each key is resource:action (2+ non-empty colon-separated segments).
REST API
api REST @entity(Order) @paginated {
// Standard CRUD endpoints are generated automatically
// Custom endpoints:
POST "/orders/{id}/cancel" @status(200) @triggers(order_cancelled)
GET "/orders/summary" @roles(ADMIN)
}
flow
Multi-step business process. Each step has an action, target entity, and field mappings. Flows compile to service-layer functions with transaction management. A flow must have either at least one step OR an intentional_stub = "reason" declaration (not both): a stepless intentional_stub declares an endpoint that is deliberately unimplemented (compiles to a declared NotImplementedError instead of a silent 501).
When the password hash lives on a separate credential entity carrying a credential-kind enum, `credential_type_field` names that enum field and `password_credential_kind` names the member that denotes a password credential. Both keys are optional; omitting them is byte-equal.
Custom error definitions with HTTP status codes, categories, and messages.
Error definitions
errors {
USER_NOT_FOUND {
status = 404
category = "not_found"
message = "User not found"
}
INVALID_CREDENTIALS {
status = 401
category = "auth"
message = "Invalid email or password"
}
}
jobs
Platform-scope scheduled jobs (ADR-260) — declared inside platform { jobs { ... } }; the per-service top-level jobs block was retired. Scheduling, retry policies, and queue routing.
Multi-service platform orchestration. Declares modules with their compile-time dependencies; the compiler derives topological build order automatically.
Platform-scope cross-service interaction registry, declared inside platform { interactions { ... } }. Each "" { ... } row declares a producer → consumer edge with its tier, trigger, and task/queue. Registry-centric (WAVE 0) enriches a producer edge with these declared attributes:
- event_schema = "Name@version" — a versioned reference into the platform event_schemas { } registry. Resolution is *lenient*: a bare Name resolves when the registry holds a single version of it; the @version suffix is required only when two or more versions of that Name are declared.
- ordering_key = "..." — advisory per-key ordering hint (also accepted inside delivery { }).
- payload_map { = } — envelope projection. Each is a closed discriminator in three kinds: a *literal* (string/number/bool), $instance. (the triggering entity), or $context. (request/tenant context). $computed and $result are not valid inside a payload_map, and $input is not yet supported here.
- delivery { ... } — delivery + reliability policy: transport ∈ {celery, redis_streams, kafka, sqs, rabbitmq}, outbox = (transactional outbox), ordering_key, plus nested retry { ... } (max_attempts · backoff · initial_delay_ms · max_delay_ms) and a baredlq { ... } block (stream_name · max_attempts_before_dlq · dlq_archive_strategy · archive_retention_days) — distinct from the saga dlq_topic / dlq_retention attributes, and reusing the platform-tier worker policy rules verbatim (M4 single-source).
The producer derives every field canonically from the registry; the compiler reads the IR directly (no name heuristics). The shapes above are declared-syntax (validator-enforced) — independent of emit behavior.
Row i-11: a producer → consumer edge with a versioned event schema, payload projection from the triggering instance, and a full delivery policy (transport + outbox + retry + DLQ).
Canonical observability surface (ADR-361) — metrics, structured logging, OTEL tracing, correlation, and health probes in one block. Carries the union of the legacy service_observability + infrastructure.observability surfaces plus tracing_enabled / tracing_exporter / log_level_env_var.
service_observability (deprecated alias)
Deprecated alias for observability (same attributes, minus tracing / log_level_env_var). Parses identically; migrate to observability.
object_storage
Platform-scope object storage topology — declares the cloud provider, credentials, and per-bucket configuration. Five providers are supported (closed taxonomy enforced parse-time): s3 / gcs / azure-blob / minio / cloudflare-r2. Each bucket carries name, region, owning_service, access_pattern (read/write/admin/shared), encryption_kind (SSE-S3/SSE-KMS/SSE-C), optional encryption_secret_ref (required for SSE-KMS+SSE-C), presigned_url_ttl_s, and lifecycle_rule blocks (transition/expiration with after_days). Cloudflare R2 rejects SSE-KMS at IR construction (R2 has no KMS integration).
permissions
Role-based and attribute-based access control definitions. The `sod_rules { ... } sub-block declares Separation-of-Duties rules; each named rule lists conflicting_permissions = [...] (the permission set that must not be co-held), an optional scope, breakglass_eligible flag, description, and an enforcement mode. enforcement is a closed taxonomy: block (the DEFAULT — omit and the rule behaves as block — records the conflict at read time and denies with 403, the fail-closed floor), warn and log_only (both record the conflict forensically and then ALLOW the request). Grant-time is always block structurally (not tunable); the enforcement` mode only relaxes read-time behavior. Provided by C-SOD-2.
sagas
Distributed transaction orchestration with compensating actions. Each `saga declares a sequence of step definitions; on failure of any step, prior completed steps run their compensation action in reverse order. Two patterns supported: orchestration (default — central coordinator drives steps) and choreography (event-driven — each step subscribes to the previous step's completion event). Persistence: outbox (default — DB-backed state machine + transactional outbox table for event publishing in the same TX as business state) or in_memory_only (opt-in for non-critical sagas where crash recovery is not required). Brokers: none (in-process — orchestration only), redis (redis-streams), kafka (aiokafka). Choreography requires a non-none broker. Per-step action_handler and compensation_handler are fully-qualified Python references (e.g. src.services.payment.charge); when omitted the compiler defaults to src.services.sagas... idempotency_key_field is the request-payload field used as the unique key for retry / replay safety. max_retries per step overrides the saga-level default. DLQ topic + retention (integer + days|hours` unit) configure the dead-letter queue for failed events that exceed retries. Provided by ADR-319 Distributed Sagas Productive Closure.
Flow Actions
Actions available in flow steps. Each action maps to a specific operation on the target entity or system.
Name
Description
Syntax
create
Create a new entity instance
action = create
update
Update an existing entity
action = update
delete
Delete an entity instance
action = delete
query
Query entities with filters
action = query
validate
Run validation rules
action = validate
extract
Extract fields from data
action = extract
calculate
Compute derived values
action = calculate
state_change
Trigger a state machine transition
action = state_change
external_call
Call an external service
action = external_call
batch
Batch multiple operations
action = batch
export
Export data to a format
action = export
infra
Infrastructure step
action = infra
Preconditions (Guards)
Structured guards the server enforces BEFORE an operation runs (fail-closed 4xx/409 on violation). A precondition { } block attaches to a command { } and to a flow step with action = validate. It is declared structurally with kind = <kind> plus that kind's attributes — the compiler lowers each kind to a real check, so a guard never ships as an un-lowerable string. The kind set is closed (17 productive + 2 markers); omitting kind defaults to field_value_compare. Every entity/field reference is spec-author-supplied — zero per-domain vocabulary (M2).
Name
Description
Syntax
field_value_compare (default)
Compare a field to a value. operator ∈ eq, not_eq, in, not_in, gt, gte, lt, lte, is_null, is_not_null. Carries error_message / error_code.
field, operator, value
role_required_single
The principal holds the single role.
role = "admin"
role_required_any_of
The principal holds ANY of roles (≥ 2).
roles = ["admin", "manager"]
permission_required
The principal holds a named permission.
permission_name = "invoice:write"
not_expired
A timestamp field is still in the future (not expired).
expiry_field = "expires_at"
workspace_present
A workspace/tenant id is present on the request.
workspace_source = jwt | header | jwt_or_header
field_present
An input field is supplied (non-null).
field = "email"
field_unique
The field value is unique across entity rows.
entity = "User", field = "email"
fk_valid
An FK field resolves to a live target_entity row.
field = "user_id", target_entity = "User"
min_list_cardinality
A list/collection field has at least count items.
field = "items", count = 1
no_conflict
No overlapping entity row exists on time_field (time-window conflict).
entity = "Booking", time_field = "slot"
token_valid
A token is valid and unrevoked.
token_source = "refresh_token"
entity_exists
An entity row identified by id_field exists.
entity = "Order", id_field = "order_id"
entity_active
The resolved entity row is active (active_field defaults to is_active).
entity = "User", active_field = "is_active"
relationship_exists
A join-table row links the subject to the object (e.g. reviewer assigned to submission).
Non-productive: flags an API/routing-layer trigger handled at the route, not as a guard. No emit.
label = "..."
session_scope_marker (marker)
Non-productive: flags an auth-service-layer session concern. No emit.
session_scope = single | global | current
kind omitted on the first block → field_value_compare. error_message / error_code apply to field_value_compare and collection_in_state; other kinds derive their fail-closed response from the kind.
Two guards on a command
command CloseInvoice {
entity = Invoice
verb = "close"
precondition { // kind omitted → field_value_compare
field = "status"
operator = eq
value = "open"
error_message = "Invoice must be open to close"
error_code = "INV_409"
}
precondition {
kind = role_required_any_of
roles = ["billing_admin", "finance_manager"]
}
}
Trigger Functions
Functions used in @triggers annotations and event routing.
Name
Description
Syntax
event()
Emit a domain event
@triggers(event("user_created"))
job()
Enqueue a background job
@triggers(job("send_welcome_email"))
state_change()
Trigger a state machine transition
@triggers(state_change("activate"))
Platform Spec
A platform spec declares the architecture of a multi-service application. It lives in a separate platform.dmx file and is never compiled directly — it provides context when compiling individual service specs.
Two file types in DMX:
- Platform spec (platform.dmx): Declares shared infrastructure, lists all services/modules, defines compilation order, cross-service interactions, Celery workers, event bus, and infrastructure config. ONE per application.
- Service spec (service_name.dmx): Defines ONE microservice — its entities, APIs, flows, security, state machines. The service "name" must match the module key in the platform's modules {} block.
The compiler compiles service specs one at a time, using the platform spec as infrastructure context.
Platform-Level Blocks
Name
Description
Syntax
version
Platform version string
version = "1.0.0"
platform_roles { }
Compile-time RBAC role definitions (optional)
modules { }
REQUIRED: service/module declarations. Each module's dependencies = [...] drive the compilation order — no explicit ordering block
ABAC row-filter scope catalog: declares scope columns once (claim → strategy). A per-entity field @scoped(claim="<key>") activates a catalog entry. Optional required_for_roles = [role, ...] makes a scope-dim role-conditional (#294-B).
DDD Strategic Design classification (closed taxonomy). Industry-agnostic. See module_category Values section for the 6 canonical keywords.
module_category = core_domain
module_category Values (DDD Strategic Design)
Closed taxonomy from Domain-Driven Design Strategic Design (Eric Evans, Domain-Driven Design, 2003; Vaughn Vernon, Implementing Domain-Driven Design, 2013). Industry-agnostic — applies to any platform regardless of business vertical. Validated parse-time by the grammar; non-canonical keywords surface a parse error inline.
Name
Description
core_domain
The strategic differentiator. Where the business invests the most because it provides competitive advantage. Custom-built, evolves with strategy.
supporting_subdomain
Necessary for the business but not differentiating. Usually built in-house when no off-the-shelf option fits; can sometimes be outsourced.
generic_subdomain
Solved problem with widely available solutions (auth, notifications, audit, document storage). Buy / adopt / standardise rather than build.
integration_layer
Bridges to external systems / third-party adapters / gateway concerns. Maps the bounded contexts of partners to the platform's ubiquitous language.
presentation_layer
User-facing surfaces (web UI, portals, dashboards, BFFs that exist only to serve the UX). Composes responses from other modules; usually no own entities.
infrastructure_layer
Platform plumbing: control planes, deploy automation, observability scaffolding, secrets / cert / identity management at the platform level (not business-domain auth).
service_type Values
Name
Description
standard
Default. Normal microservice with entities, APIs, business logic
consumer
Event consumer with no REST API (e.g., data lake ingestor)
reader
Read-only service querying external data (e.g., analytics API over ClickHouse)
stateless
No database, real-time processing (e.g., WebSocket dashboard)
aggregator
Portal API that composes responses from multiple services (no own entities)
Compilation Order
Derived automatically from each module's dependencies. There is no explicit ordering keyword — the compiler builds a topological DAG and rejects any legacy phase/ordering block.
control_plane has no deps so compiles first; identity depends on control_plane; registry and catalog depend on identity and compile in parallel; lab waits for both connectivity and preanalytic.
Canonical list-response pagination defaults. Drives the wrapper class, route Query() ge/le constraints, service signature, and BaseRepository.list defaults end-to-end (α-PAGINATION program / ADR-329). When omitted, the compiler falls back to default_limit = 100 + max_limit = 1000.
Pagination block
infrastructure {
pagination {
default_limit = 50 // default page size when client omits ?limit
max_limit = 500 // hard cap enforced via Query(le=...)
}
}
Compliance
Platform-scope compliance profiles (ADR-255 / ADR-257 / ADR-317 INV-317-1.b Op α Phase 1.B 2026-05-15). Each profile is a registered framework identifier the spec author binds to the platform per its jurisdictional scope; the compiler treats the name as opaque taxonomy and reads concrete obligations from the canonical compliance_frameworks registry + the attributes declared inside the block. The Workshop validator queries `registry_loader.is_supported_compliance_framework`; non-canonical names are rejected at validate-time as COMPLIANCE_PROFILE_UNKNOWN. The full canonical set as of 2026-05-15: CCPA / FEDRAMP / GDPR / HIPAA / HITRUST / ISO-27001 / NIST-800-53 / OWASP-API / OWASP-WEB / PCI-DSS / SOC2 / SOX + enterprise jurisdictional extensions ISO_15189 / AFIP_ARCA / FABA_AOL / LEY_25_326 / LEY_17_132 (5 entries Op α Phase 1.B added). ADR-317 E.2 extends the per-profile attribute surface so the spec is the source of truth for the full ComplianceFrameworkIR shape — when an attribute is omitted the compiler falls back to the canonical default registry shipped under `src/dsl/compliance_framework_default_registry.py`.
Name
Description
Syntax
scope
Free-form scope description (e.g. data residency rule).
Comments
//. Block comments use/* ... */.