Skip to content

Auth and compliance end to end

Security in a Cratis application is not one feature in one package. A request crosses the edge, becomes an Arc command or query, writes events through Chronicle, builds read models, and comes back to a React screen. Authentication, authorization, tenant isolation, and compliance each belong to a different part of that journey, and the stack is designed so each concern has one clear owner.

The short version: AuthProxy authenticates and resolves tenant context, Arc authorizes the operation, Chronicle stores events in the right namespace and encrypts PII by compliance subject, and Arc releases PII before query results reach the client.

Tenant-ID + identity

events + subject

Browser or API client

AuthProxy

authenticate + resolve tenant

Arc /.cratis/me

identity enrichment

Arc commands and queries

authorize operation

Chronicle

tenant namespace

Event log

PII encrypted by subject

Read models

encrypted at rest

Arc read-model release

decrypts for response

Components / React

typed result

Put AuthProxy in front of your frontend and backend services when you want one place to own the browser and API edge. It handles OpenID Connect, OAuth providers, JWT bearer authentication, provider selection, invite onboarding, tenant resolution, and routing.

AuthProxy then forwards requests with the resolved tenant and identity context. In an Arc application, the identity-enrichment endpoint is /.cratis/me: AuthProxy calls it, Arc composes the domain-specific identity payload, and the frontend can consume the resulting identity state instead of making every service rediscover the user.

Arc applies authorization where the behavior is declared: commands and queries. Use ASP.NET Core authorization attributes, Arc’s [Roles] convenience attribute, and policy-based authorization on the command record or read-model query method that represents the operation.

That matters because authorization happens before the behavior runs. If a command is not authorized, its Handle() method is not executed. If a query is not authorized, the read is not performed. The generated frontend proxy receives authorization state as part of the command or query result, so screens can react without hand-written protocol glue.

Use edge authentication for entry, then command/query authorization for business operations. Do not rely on the UI hiding a button as the only access-control layer.

Tenant context follows the request into Chronicle

Section titled “Tenant context follows the request into Chronicle”

In a multi-tenant Cratis application, the tenant resolved at the edge becomes application context in Arc. When Arc is integrated with Chronicle, that tenant context is used as the Chronicle namespace. Events, projections, reducers, and read models for one tenant stay separated from another tenant without every command manually choosing a namespace.

This is the operational boundary:

ConcernOwnerResult
Resolve the tenantAuthProxy or Arc tenancy resolversOne tenant id for the request
Carry tenant through command/query handlingArcCommands, queries, filters, identity, and generated endpoints see the same tenant context
Isolate event-sourced stateArc + Chronicle integrationChronicle uses the tenant as the event-store namespace

Chronicle compliance uses a subject to identify whose protected data an event contains. That subject is the lookup key for PII encryption material. It is often a person or customer id; it is not always the authenticated user, and it is not always the aggregate id.

For example, an order event may be appended to an OrderId event source while the protected email address in the event belongs to a CustomerId. In that case the command should provide the customer as the Chronicle Subject. If no explicit subject is provided, Chronicle falls back to the event source id.

Arc’s Chronicle integration can resolve the subject from command return values, command properties, ICanProvideSubject, or a [Subject]-marked property. The rule of thumb is simple: set the subject to the identity that owns the PII, not merely the thing being changed.

PII is protected at write and released at read

Section titled “PII is protected at write and released at read”

Chronicle detects [PII] on event properties and on ConceptAs<T> value types. At append time, those values are encrypted under the subject’s key before they are stored in the event log.

Read models keep that protection:

Read-model styleHow PII is tracked
ProjectionChronicle can infer PII lineage from mapped event properties.
ReducerThe read model must mark PII properties explicitly, because reducer logic is arbitrary C#.
Arc query responseArc releases the read model before serving it, so clients receive decrypted values when the subject key exists.

Managed read-model documents also carry Chronicle’s reserved _subject field so the correct key can be used during release. Do not model your own _subject property.

The event log can stay structurally immutable while protected values become unreadable. For GDPR-style erasure, Chronicle deletes the subject’s PII encryption key. After that, PII encrypted with that key cannot be decrypted.

For read models, finish the erasure by re-projecting or re-reducing affected models. Until then, stored read-model documents may still contain ciphertext. The key is gone, so the value is unreadable, but rebuilding the read model removes the remaining encrypted payload from the query store. If an entire event payload must be removed, combine key deletion with event redaction.

DecisionAsk thisStart here
Identity providerWhere do browser users and API clients authenticate?AuthProxy
Tenant resolutionWhich host, claim, route, or configured value selects the tenant?AuthProxy tenancy
Operation accessWhich roles or policies can run each command or query?Arc authorization
Compliance subjectWhose personal data does this event contain?Arc Chronicle subject
PII modelingWhich event properties or concepts are personal data?Chronicle compliance
ErasureWhat must be key-deleted, re-projected, or redacted?Chronicle read models and PII