Building Blocks
Cohesive. Infra
Cohesive.Infra is the typed semantic infrastructure model for compute, runtimes, external services, capabilities, bindings, legalization, and projection targets.
Core Idea
Cohesive.Infra is the realization IR for Cohesive systems. It defines the compute, runtime, external service, network, configuration, capability, and binding model used to host a distributed application.
It is not only an IaC wrapper. It is a typed semantic infrastructure model.
The framework lets a system declare:
- What compute exists?
- What runtime components execute application code?
- What external services exist?
- What capabilities do those components provide?
- What capabilities does the application require?
- How are semantic constructs bound to those capabilities?
- Which realization graph satisfies the requirements?
- Which backend projects the result into Pulumi, Terraform, Aspire, Kubernetes, Docker, or another target?
The core invariant is:
- Application semantics declare requirements.
- Infrastructure/runtime components declare capabilities.
- Bindings compose capabilities into realized behavior.
- The legalizer verifies or rewrites the realization graph until requirements are satisfied.
Layer Model
Cohesive.Infra models a realization layer below the semantic model, then projects that realization onto concrete deployment systems.
The compute layer provides the physical or virtual place where processes run.
The application/runtime layer receives activity, demultiplexes it, routes it, controls concurrency, and dispatches code.
The external service layer contains distributed systems accessed by runtimes through clients, drivers, protocols, or RPC.
Core Concepts
Infra System
The top-level model is an immutable graph.
public sealed record InfraSystemDefinition
{
public InfraSystemId Id { get; init; }
public string? Version { get; init; }
public ImmutableArray<ComputeComponentDefinition> Compute { get; init; }
public ImmutableArray<RuntimeComponentDefinition> Runtimes { get; init; }
public ImmutableArray<ServiceComponentDefinition> Services { get; init; }
public ImmutableArray<NetworkSurfaceDefinition> Network { get; init; }
public ImmutableArray<CapabilityProviderDefinition> Providers { get; init; }
public ImmutableArray<RequirementDefinition> Requirements { get; init; }
public ImmutableArray<BindingDefinition> Bindings { get; init; }
public ImmutableArray<LegalizationRuleDefinition> LegalizationRules { get; init; }
public ImmutableArray<ConfigurationBindingDefinition> Configuration { get; init; }
public ImmutableDictionary<AnnotationKey, AnnotationValue> Annotations { get; init; }
}The model is not a deployment artifact. It is the source of truth for realization.
Projection adapters compile it into deployment artifacts, local development hosts, integration-test fixtures, or provider-specific IaC.
Component Kinds
Compute Components
A compute component is a place where processes or containers run.
Examples:
Typical capabilities:
Example definition:
var apiCompute = infra.Compute.KubernetesDeployment("freight-api", deployment =>
{
deployment.Image("ghcr.io/cohesive/freight-api:1.8.0");
deployment.Replicas(min: 3, max: 20);
deployment.Resources(cpu: Cpu.Cores(2), memory: Memory.Gibibytes(4));
deployment.ExposePort("http", 8080);
deployment.ProviderSettings(new KubernetesWorkloadSettings
{
Namespace = "freight-prod",
TopologySpread = TopologySpread.ByZone,
RollingUpdate = new RollingUpdateSettings(maxUnavailable: 1, maxSurge: 2)
});
});Runtime Components
A runtime component executes or dispatches application code inside a process or worker.
Examples:
Typical capabilities:
Runtime components are hosted on compute components.
var aspNet = infra.Runtime.AspNet("public-http", runtime =>
{
runtime.HostedOn(apiCompute);
runtime.Listen(Http.Public(port: 443));
runtime.Provides(Http.RequestReply());
runtime.Provides(Http.RouteDispatch());
runtime.ProviderSettings(new AspNetRuntimeSettings
{
RequestBodyLimitBytes = 50 * 1024 * 1024,
ForwardedHeaders = true,
UseHttp2 = true
});
});External Service Components
An external service component is a runtime outside the application process, accessed through a client, driver, protocol, or RPC.
Examples:
Typical capabilities:
Example:
var cosmos = infra.Service.Cosmos("primary-cosmos", service =>
{
service.Account("freight-prod-cosmos");
service.Database("freight");
service.Container("orders", container =>
{
container.PartitionKey("/tenantId");
container.Provides(Storage.DocumentStore());
container.Provides(Storage.OptimisticConcurrency("etag"));
container.Provides(Storage.TransactionalBatch(scope: TransactionScope.SinglePartition));
container.Provides(Events.ChangeFeed());
});
service.ProviderSettings(new CosmosProviderSettings
{
Consistency = CosmosConsistency.Session,
DefaultTimeToLive = null,
RegionPriority = ["westus2", "eastus2"],
Throughput = CosmosThroughput.AutoScale(maxRuPerSecond: 50_000)
});
});Provider-Specific Settings
Every provider exposes its own settings without polluting the portable Cohesive capability model.
The typical split is:
Portable contract
common component kind, common capabilities, common constraints
Provider settings
product-specific options, deployment knobs, version-specific features
Projection settings
target-specific artifact behavior, such as Pulumi stack names or Terraform modulesProvider settings are strongly typed when supported and extensible when not.
public abstract record ProviderSettings
{
public string ProviderName { get; init; }
public string? ProviderVersion { get; init; }
public ImmutableDictionary<string, ObservationValue> Extensions { get; init; }
}Each provider adapter maps product-specific settings to provided capabilities.
Cosmos settings
partition key, consistency, transactional batch, change feed, RU model
-> capabilities and constraints
Kubernetes settings
replicas, probes, resources, affinity, topology spread
-> compute capabilities and constraints
Orleans settings
cluster id, silo membership, placement strategy, grain storage
-> actor runtime capabilities and constraintsUnknown provider settings round-trip. Cohesive may not understand every product option, but it preserves them and allows adapters to consume them.
Capability Model
Capabilities are first-class typed semantic facts about resources, runtimes, and composed behavior.
A capability is not just a label. It has parameters, constraints, scope, and sometimes proof obligations.
public sealed record CapabilityDefinition
{
public CapabilityId Id { get; init; }
public string Name { get; init; }
public CapabilityKind Kind { get; init; }
public ImmutableArray<CapabilityProperty> Properties { get; init; }
public ImmutableArray<CapabilityConstraint> Constraints { get; init; }
public ImmutableArray<CapabilityObligation> Obligations { get; init; }
}The capability kinds are:
public enum CapabilityKind
{
Compute,
Network,
Ingress,
Dispatch,
ExecutionDiscipline,
ConcurrencyControl,
Durability,
Storage,
Eventing,
Search,
Security,
Observability,
Configuration,
Composite
}Examples:
Execution.SerializedByIdentity(scope: "OrderId")
Execution.InlineSerializedBeforeResponse(scope: "ShipmentId", response: "SynchronousHttp")
Execution.DurableWorkflowHistory()
Execution.DurableTimer()
Storage.DocumentStore()
Storage.TransactionalBatch(scope: TransactionScope.SinglePartition)
Storage.OptimisticConcurrency(token: "etag")
Storage.RelationalTransaction(isolation: IsolationLevel.Serializable)
Events.DurableStream()
Events.AtLeastOnceDelivery()
Events.OrderedWithinPartition(key: "OrderId")
Events.ConsumerGroups()
Search.FullTextIndex()
Search.VectorIndex(dimensions: 1536)
Search.EventuallyConsistentProjection()Primitive vs Composed Capabilities
Cohesive.Infra distinguishes primitive resources from realized behavior.
A lock provides a primitive:
MutualExclusionPrimitiveLeaseFencingToken(if supported)Renewal(if supported)
It does not by itself provide serialized execution.
Serialized execution is provided by a composed runtime binding:
LockGuardedInlineExecutionA partitioned queue provides a primitive:
PartitionedOrderedDeliveryOffsetCheckpointConsumerGroup
It does not by itself provide serialized processing.
Serialized processing is provided by a composed consumer binding:
SerializedAsyncProcessingThis distinction is mandatory. Capabilities attach to realized behavior graphs, not merely to named resources.
Requirement Model
Application and semantic layers declare requirements without hardcoding products.
public sealed record RequirementDefinition
{
public RequirementId Id { get; init; }
public SemanticSubjectRef Subject { get; init; }
public RequiredCapability RequiredCapability { get; init; }
public RequirementStrength Strength { get; init; }
public ImmutableArray<RequirementConstraint> Constraints { get; init; }
}Example requirements:
OrderRepositoryrequiresDurableDocumentStorage.OrderRepositoryrequiresOptimisticConcurrency.OrderTransitionRuntimerequiresSerializedExecutionbyOrderId.OrderTransitionRuntimerequiresAtomicStateWriteAndOutboxAppend.FulfillmentFlowrequiresDurableWorkflowExecution.FulfillmentFlowrequiresDurableTimersandExternalSignals.PublicApirequiresSynchronousHttpRequestReply.DomainEventsrequiresDurableAtLeastOncePartitionedReplayableStream.OrderSearchProjectionrequiresFullTextSearchIndex.AS2IngressrequiresInlineSerializedExecutionbyShipmentIdbeforeSynchronousHttpResponse.
The domain layer states what it needs. It does not need to know whether the implementation is Cosmos, PostgreSQL, Orleans, Akka, Event Hubs, Redis, Kubernetes, or Temporal.
Binding Model
A binding connects a semantic subject to a provider graph.
public sealed record BindingDefinition
{
public BindingId Id { get; init; }
public SemanticSubjectRef Subject { get; init; }
public ImmutableArray<ComponentRef> Providers { get; init; }
public ImmutableArray<CapabilityMapping> CapabilityMappings { get; init; }
public ImmutableArray<IdentityMapping> IdentityMappings { get; init; }
public ImmutableArray<BoundaryMapping> BoundaryMappings { get; init; }
public ImmutableArray<ConfigurationBindingDefinition> Configuration { get; init; }
}Common binding types:
Repository Binding
app.Entity<Order>().Repository.BindTo(cosmos.Container("orders"), binding =>
{
binding.Requires(Storage.DocumentStore());
binding.Requires(Storage.OptimisticConcurrency("etag"));
binding.MapPartitionKey(order => order.TenantId);
binding.MapId(order => order.OrderId);
});Transition Runtime Binding
app.Entity<Order>().Transitions.BindTo(orleans, binding =>
{
binding.Requires(Execution.SerializedByIdentity("OrderId"));
binding.MapIdentity("OrderId", RuntimeIdentity.GrainId);
binding.BindState(app.Entity<Order>().Repository);
});Process Runtime Binding
app.Flow("FulfillmentFlow").BindTo(temporal, binding =>
{
binding.Requires(Execution.DurableWorkflowHistory());
binding.Requires(Execution.DurableTimer());
binding.Requires(Execution.ExternalSignal());
binding.MapIdentity("ShipmentId", RuntimeIdentity.WorkflowId);
binding.TaskQueue("fulfillment");
});API Binding
app.Api("PublicFreightApi").BindTo(aspNet, binding =>
{
binding.Requires(Http.RequestReply());
binding.Requires(Http.RouteDispatch());
binding.ExposeTransition<Order>("AcceptTender", route: "POST /orders/{id}/accept");
});Lock-Guarded Transition Runtime Binding
app.Entity<Shipment>().Transitions.BindTo(aspNet, binding =>
{
binding.Requires(Execution.InlineSerializedBeforeResponse("ShipmentId"));
binding.UseLockGuard(redisLock, guard =>
{
guard.MapIdentity("ShipmentId", key: "shipment:{ShipmentId}");
guard.GuardCriticalSection("ReceiveAndRespond");
guard.RequireLeaseRenewal();
guard.RequireFencingToken();
guard.ReleaseAfter(Boundary.HttpResponseDecision);
});
});Legalization Model
Legalization is the compiler-like process that turns semantic requirements into valid runtime/infrastructure realizations.
Requirement graph
+ available providers
+ selected bindings
+ legalization rules
-> realization graph
-> proof / diagnostics
-> projection artifactsThe core relation is:
ProviderGraph ⊨ RequiredCapabilityBut this is not a flat lookup. It may require composition.
Legalization Results
public enum LegalizationStatus
{
SatisfiedDirectly,
SatisfiedByComposition,
SatisfiedAfterRewrite,
RequiresUserChoice,
Unsatisfied,
Invalid
}A result includes a proof chain.
public sealed record LegalizationResult
{
public LegalizationStatus Status { get; init; }
public RealizationGraph Realization { get; init; }
public ImmutableArray<CapabilityProofStep> Proof { get; init; }
public ImmutableArray<InfraDiagnostic> Diagnostics { get; init; }
}Legalization Rule Examples
Actor dispatch satisfies serialized execution
Required:
SerializedExecution(scope = X)
Provided:
ActorRuntime.SerializedPerIdentity
Legal if:
X maps to actor identity
all entry points for the subject dispatch through that actor
execution boundary is compatible with the caller's response boundaryLock guard satisfies inline serialized execution
Required:
InlineSerializedExecution(scope = X, boundary = BeforeHttpResponse)
Provided:
HttpRuntime.RequestReply
LockService.Lease
RuntimeBinding.LockGuardedCriticalSection
Legal if:
X maps to lock key
lock is acquired before critical section
critical section includes every response-relevant operation
lock is held until response decision is complete
lease renewal/fencing semantics satisfy configured risk policy
all relevant entry points use the same guardOptimistic concurrency does not satisfy serialized execution
Required:
SerializedExecution(scope = X)
Provided:
Storage.OptimisticConcurrency
Illegal because:
optimistic concurrency detects conflict at commit time
it does not prevent concurrent execution from entering the critical sectionQueue partitioning satisfies only async serialization
Required:
InlineSerializedExecution(scope = X, boundary = BeforeHttpResponse)
Provided:
PartitionedQueue.OrderedWithinPartition
ConsumerRuntime.SingleActiveHandlerPerPartition
Illegal because:
serialization occurs after enqueue and outside the synchronous response boundary
Legal for:
SerializedAsyncContinuation(scope = X)Change feed to event hub rewrite preserves logical stream
Required:
LogicalEventStream(
durable,
atLeastOnce,
partitioned,
orderedWithin = OrderId,
replayable,
consumerGroups)
Initial realization:
CosmosChangeFeed
Scaled realization:
CosmosChangeFeed -> Bridge -> EventHubs
Legal if:
envelope preserved
idempotency key preserved
partition key mapping preserves required ordering
consumer checkpoint semantics satisfy replay requirement
retention is adequateRealization Graph
The legalizer produces a realization graph. This graph is lower-level than the semantic model and higher-level than provider-specific deployment artifacts.
Example: AS2 ingress with actor serialization.
AS2IngressRuntime
requires HTTP synchronous response
requires inline serialized execution by ShipmentId
Realization:
ASP.NET route /as2/receive
-> Orleans grain As2ShipmentGrain(ShipmentId)
-> durable receive store
-> outbox append
-> MDN responseExample: AS2 ingress with lock-guarded ASP.NET.
AS2IngressRuntime
requires HTTP synchronous response
requires inline serialized execution by ShipmentId
Realization:
ASP.NET route /as2/receive
-> acquire distributed lock shipment:{ShipmentId}
-> receive critical section
-> durable receive store
-> outbox append
-> MDN response decision
-> release lockExample: outbox stream scaling rewrite.
Application binding:
DomainEvents logical stream
v1 realization:
Cosmos outbox container
-> Cosmos change feed consumers
v2 realization:
Cosmos outbox container
-> ChangeFeedToEventHubBridge
-> Event Hub domain-events
-> consumersThe semantic binding remains stable. The infra graph changes.
Configuration Integration
Cohesive.Infra integrates with Cohesive.Configuration. Infra does not store raw configuration values as untyped strings.
Each component exposes a configuration surface:
Inputs
values required to provision or run the component
Outputs
values produced by provisioning, discovery, or runtime allocation
Bindings
wiring from outputs to inputs, secrets, environment variables, appsettings, files, or provider-specific mechanismsConfiguration values are typed, scoped, and provenance-aware.
public sealed record ConfigurationSlot<T>
{
public ConfigurationKey Key { get; init; }
public ConfigurationValueKind Kind { get; init; }
public bool IsSecret { get; init; }
public T? DefaultValue { get; init; }
public ImmutableArray<ConfigurationConstraint> Constraints { get; init; }
}Value kinds:
LiteralEnvironmentVariableSecretReferenceProviderOutputGeneratedValueComputedExpressionDeploymentParameterTestRuntimeValue
Example:
infra.Configuration.Bind(cosmos.Outputs.ConnectionString)
.To(apiCompute.EnvironmentVariable("ConnectionStrings__Cosmos"))
.AsSecret();
infra.Configuration.Bind(eventHub.Outputs.FullyQualifiedNamespace)
.To(worker.Configuration("Eventing:Namespace"));
infra.Configuration.Bind(redis.Outputs.Endpoint)
.To(lockGuard.Configuration("Lock:Endpoint"));Cohesive.Configuration owns:
- Environment overlays
- Secret provenance
- Validation
- Late-bound provider outputs
- Deployment-time vs runtime-time values
- Local/test/prod substitution
- Typed quantities, durations, resource names, and endpoints
Cohesive.Infra consumes those semantics and attaches them to runtime/service bindings.
Projection Targets
Pulumi
Use for cloud resource provisioning and application deployment.
- Resource graph
- Provider credentials
- Stack outputs
- Secret handling
- Cloud-specific resources
- Kubernetes deployments
- Managed databases
- Managed queues
- Managed search services
A single Cohesive.Infra model may produce different projections for different environments.
Semantic Binding Examples
Entity Repository to Storage Infrastructure
OrderRepository requires:
durable document storagepoint lookup by OrderIdquery by statusoptimistic concurrencytransactional state write+outbox append within partition
Binding:
app.Entity<Order>().Repository.BindTo(cosmos.Container("orders"), binding =>
{
binding.MapPartitionKey(order => order.TenantId);
binding.MapId(order => order.OrderId);
binding.Requires(Storage.DocumentStore());
binding.Requires(Storage.OptimisticConcurrency("etag"));
binding.Requires(Storage.TransactionalBatch(TransactionScope.SinglePartition));
});Verifier diagnostics might include:
Valid:
Cosmos container provides document storage and ETag optimistic concurrency.
Conditional:
Transactional batch is valid only when Order state and Outbox event share the same partition key.Distributed Lock Embedded into Transition Runtime
Shipment AS2 ingress transition requires:
synchronous HTTP responseserialized decision by ShipmentId before responsedurable receive record before response
Binding:
app.Edi.As2Ingress("ShipmentAs2Receive").BindTo(aspNet, binding =>
{
binding.Route("POST /edi/as2/shipments");
binding.Requires(Http.SynchronousResponse());
binding.Requires(Execution.InlineSerializedBeforeResponse("ShipmentId"));
binding.UseLockGuard(redisLock, guard =>
{
guard.MapIdentity("ShipmentId", "shipment:{ShipmentId}");
guard.AcquireBefore("ReceiveDecision");
guard.ReleaseAfter(Boundary.HttpResponseDecision);
guard.RequireFencingToken();
});
});The lock alone is not the provider. The provider is the composed runtime behavior:
Actor Runtime Alternative
app.Edi.As2Ingress("ShipmentAs2Receive").BindTo(aspNet, binding =>
{
binding.Route("POST /edi/as2/shipments");
binding.Requires(Http.SynchronousResponse());
binding.Requires(Execution.InlineSerializedBeforeResponse("ShipmentId"));
binding.DispatchTo(orleans.Grain("ShipmentAs2IngressGrain"), dispatch =>
{
dispatch.MapIdentity("ShipmentId", RuntimeIdentity.GrainId);
dispatch.AwaitBeforeResponse();
});
});Event Stream Rewrite
Initial realization:
var domainEvents = infra.EventStream("DomainEvents", stream =>
{
stream.Requires(Events.Durable());
stream.Requires(Events.AtLeastOnce());
stream.Requires(Events.OrderedWithin("AggregateId"));
stream.Requires(Events.Replayable());
});
domainEvents.RealizeWith(cosmos.Container("outbox").ChangeFeed());Scaled realization:
var eventHub = infra.Service.EventHub("domain-events", hub =>
{
hub.PartitionBy("AggregateId");
hub.Retention(TimeSpan.FromDays(7));
hub.Provides(Events.DurableStream());
hub.Provides(Events.ConsumerGroups());
});
infra.Pipe("outbox-to-eventhub", pipe =>
{
pipe.From(cosmos.Container("outbox").ChangeFeed());
pipe.To(eventHub);
pipe.PreserveEnvelope();
pipe.PreserveIdempotencyKey("EventId");
pipe.PreservePartitionKey("AggregateId");
});
domainEvents.RealizeWith(eventHub);Publishers and consumers remain bound to DomainEvents.
Compiler Pipeline
Cohesive.Infra behaves like a compiler backend for system realization.
- Parse / construct
InfraSystemDefinition. - Normalize provider-specific settings.
- Discover provided capabilities.
- Collect requirements from semantic models and explicit infra definitions.
- Build initial binding graph.
- Run legalization passes.
- Emit diagnostics and proof graph.
- Resolve configuration surfaces with Cohesive.Configuration.
- Produce
RealizationGraph. - Project to Pulumi, Aspire, TestContainers, Docker Compose, Terraform, etc.
Diagnostics
Diagnostics are precise and semantic.
Example:
Error cohesive.infra.execution.serialized.unsatisfied:
AS2Ingress 'ShipmentAs2Receive' requires InlineSerializedExecution by ShipmentId before SynchronousHttpResponse.
Current binding:
ASP.NET route 'POST /edi/as2/shipments'
Kubernetes deployment replicas = 4
Cosmos ETag optimistic concurrency
Problem:
ASP.NET does not serialize requests by ShipmentId.
Cosmos ETag detects conflicts at commit time, not before the response-bound critical section.
Legalization options:
1. Dispatch to Orleans/Akka identity keyed by ShipmentId and await before response.
2. Insert lock-guarded critical section keyed by ShipmentId with lease renewal and fencing.
3. Restrict to single replica. Not recommended for production scale.Environment Model
Infra definitions are environment-parameterized without duplicating semantic intent.
Environments override values and provider settings, but not semantic requirements unless explicitly allowed.
infra.Environment("local", env =>
{
env.ProjectWith(Aspire.Projector);
env.Override(cosmos, service => service.UseEmulator());
env.Override(eventHub, service => service.UseContainerizedKafka());
});
infra.Environment("prod", env =>
{
env.ProjectWith(Pulumi.Projector);
env.RequireNoCapabilityDowngrades();
env.RequireSecretProvider("AzureKeyVault");
});Design Rules
- Infrastructure resources are not passive; they provide typed capabilities.
- Capabilities may be primitive or composed.
- Semantic constructs bind to capabilities, not products.
- Provider-specific settings are allowed but isolated from the portable capability contract.
- Legalization preserves requirements or rejects the realization.
- Optimistic concurrency is not serialized execution.
- A queue is not serialized processing unless the consumer runtime enforces it.
- A lock is not serialized execution unless a runtime acquires, renews, fences, and releases it around the required critical section.
- Response boundaries are first-class. Some requirements are satisfied before an external response is committed.
- Configuration is typed, scoped, secret-aware, and integrated through Cohesive.Configuration.
- Projection backends are targets, not sources of semantic truth.
- The realization graph is inspectable and diagnosable.