What this is about
Embedded means: CIAS is not a program of its own but a set of jar files inside the same process as the application. A process is one running program; CDMS and CIAS then share memory, database connection and configuration.
The application that takes CIAS in is called the host here. That is, for example, the hub backend or a project generated with the CDMS generator.
What the same setup looks like with two services is under CIAS as a separate service. The differences side by side are under Embedded and standalone compared.
The process picture
flowchart LR
F["Frontend with BFF"] -- "bearer token" --> P
subgraph P["One process"]
direction TB
FK["Filter chain<br/>(cias-authentication)"]
C["CDMS"]
CI["CIAS modules<br/>tenancy, user, authorization, …"]
FK --> C
FK --> CI
C <-->|method call| CI
end
P --> SDB[("System database<br/>CDMS tables + CIAS tables")]
C --> TDB[("Tenant databases")]
CI -- "adapter" --> K[(Keycloak)]
FK -- "keys, token exchange" --> K
classDef client fill:#475569,stroke:#475569,color:#fff
classDef cdms fill:#1976d2,stroke:#1976d2,color:#fff
classDef cias fill:#8e24aa,stroke:#8e24aa,color:#fff
classDef idp fill:#c2410c,stroke:#c2410c,color:#fff
classDef db fill:#4d7c0f,stroke:#4d7c0f,color:#fff
class F client
class C cdms
class FK,CI cias
class K idp
class SDB,TDB db
Everything inside the box is one program. Only three lines leave it: to the database, to Keycloak, and to the frontend.
Which modules a host includes
CIAS consists of many small modules, each of them its own jar. A host does not automatically take all of them. There are two ways to include them.
cias-spring-boot-starter- one dependency that brings along all the business modules:
cias-tenancy,cias-user,cias-authorization,cias-registration,cias-notification,cias-audit - wires them itself (auto-configuration), the host writes no configuration classes
- also builds the ports to the identity provider, the first start (bootstrap) and the timers
- brings its own migration run for the CIAS tables
- this is how the standalone CIAS service
cias-runtimeruns
- the host names every module one by one in its
pom.xml - it writes the wiring itself, in its own configuration classes
- it takes only what it needs – often
cias-tenancy,cias-user,cias-authorization,cias-iam-keycloak - this is how the hub backend works, and how the CDMS generator produces a project with
cias: EMBEDDED
Why without the starter at all? Because the starter brings more than a CDMS host wants: its own caller context, its own migration run, and a bean for the identity provider that fails the start when no provider is configured. A host that only wants to carry CIAS dormant therefore takes the single jars.
No CIAS module switches itself on
Every module sits behind a switch codamai.cias.<modul>.enabled, and none of these switches has a default value. That is deliberate: at startup CDMS scans all classes under com.codamai. Without these switches CIAS would spring to life in every application that happens to have the jar on its classpath.
The starter avoids the scan a second way: it registers as an auto-configuration, and auto-configurations are excluded from component scanning. So it arrives because the jar is there and the conditions match, not because somebody scanned a package name.
How CDMS calls CIAS
Embedded, all three questions between CDMS and CIAS are ordinary method calls. No HTTP, no token, no timeout.
| Question | Who asks | Who answers | How |
|---|---|---|---|
May tenant kunde-a be served? | the tenant gate in cias-authentication | LocalTenantLookupAdapter in cias-tenancy | method call |
| Which tenant-bound attribute values does this person hold here? | the attribute lookup in cias-authentication | LocalTenantBoundAttributeAdapter in cias-user | method call |
| Which roles and attributes does CDMS declare? | the reconciliation in cias-authorization | LocalModuleDeclarationAdapter reads the host’s bean | method call |
A few words on that:
- The tenant gate is the place that checks, before every request, whether the customer is served at all. Details under Admit the tenant (tenant gate).
- An attribute is a value on a person that CDMS filters rows by, for example
regionen. Some of them apply per tenant, see One value per person or per tenant. - The declaration is the list of all roles and attributes a module knows. See Modules register their roles.
- A bean is an object that Spring creates and manages at startup. “Declaring it as a bean” here means: the object is already in the process, you only have to name it.
The declaration as a bean
Embedded, CIAS does not call the endpoint GET /cias/fetch. Instead the configuration names, per module, the bean that holds the declaration:
codamai:
cias:
authorization:
declarations:
modules:
- name: cias
client: ${cias_client}
bean: ciasIdentityRegistry
- name: cdms
client: ${cias_client}
bean: roleRegistryService
Both modules live in the same process and therefore share one Keycloak client. name says whose roles are whose; without the name the reconciliation could not tell them apart. roleRegistryService is the class the CDMS generator writes from the models.
Every entry names either a bean or a URL, never both and never neither. A typo fails the start instead of surfacing weeks later during a reconciliation.
One request from front to back
sequenceDiagram
participant B as BFF
participant F as Filter chain (CIAS)
participant T as Tenant gate (CIAS)
participant U as cias-user
participant C as CDMS
participant DB as Tenant DB
B->>F: POST /api/rest/crm/customer/query + token
F->>F: check token, exchange it, resolve tenant
F->>T: may "kunde-a" be served?
Note over F,T: method call, no network
T-->>F: yes (remembered for 30 s)
F->>U: which attribute values does the person hold in "kunde-a"?
U-->>F: regionen = [nord]
F->>C: RequestContext filled
C->>DB: SELECT … (only allowed rows)
DB-->>C: rows
C-->>B: data + meta
The 30 seconds are the tenant gate’s memory (codamai.cias.tenant-gate.ttl). Embedded it only saves a database query — CIAS cannot go down on its own here, because it stands and falls with the process.
Which database CIAS uses
Embedded, CIAS has no database of its own. Its tables live in the host’s system database, next to the host’s own tables. That has to be so: cias_tenant lists every tenant of the installation and therefore cannot exist once per tenant.
Which databases there are otherwise is under Which database? The persistence target.
How the tables come to be
When: The host takes cias-spring-boot-starter and sets codamai.cias.migration.enabled: true.
The starter runs one Flyway pass per module, each with its own history table (flyway_schema_history_cias_tenancy, …_cias_user, …). That is necessary because every CIAS module is its own repository and starts its numbering at V1__ – six modules with six scripts called “version one” do not fit into one shared history.
-
1BuildThe host names the locations it migrates – one per module. A named location without scripts fails the start
-
2DatabaseWhen a module's history table is missing, the pass starts at version 0 and applies every scriptThe usual baseline “version one” would be wrong here: after the first module the schema is never empty again, and the second module would skip its own
V1__as “already applied”. -
3CIASOnly then does the persistence layer startResult: Every module has its own history and can move on by itself
When: The host includes the modules one by one, like the hub backend or a generated project.
Then there is no CIAS migration run. Instead the host registers the CIAS entities with its own persistence – a ManagedTypesContribution with scope SYSTEM – and the tables come to be the same way the host's own tables do.
Result: One schema, one path, no second migration machinery in the process.
How a CDMS project manages its schema is under Databases, pools, migration.
What the host has to supply itself
CIAS refuses to guess a few things. Without the starter the host states them in a configuration class of its own; with the starter, the starter fills in the technical parts and the security statements stay with the host.
-
1DeveloperWhich roles administer the platform (
codamai.cias.platform-administrator-roles)No default. An empty value means “nobody” and is a valid statement; a guessed default role would hand platform administration to whoever happens to hold that role. -
2DeveloperWho is calling – a
CallerContextProviderthat reads user and roles from the request contextCDMS maintains that context anyway. A request without a user is anonymous rather than an error, because system passes have no caller. -
3DeveloperRegistering the entities, so the CIAS tables belong to the system database
-
4DeveloperA transaction bracket for the writing CIAS servicesCDMS works without a central transaction manager – its boundary is the request, not the method. The host therefore builds the bracket locally and hands it to CIAS only.
-
5DeveloperWhich roles may read
GET /cias/fetch(codamai.cdms.cias.reader-roles)Result: The endpoint exists and is guarded embedded as well. The preset is the realm roledeclaration-reader.
Watch out
Next
- CIAS as a separate service: the same subject area with two services
- Embedded and standalone compared: all differences in one table
- Why both operating modes behave the same
- What is configured and SINGLE or MULTI
- The parties in a request
- CIAS as a bridge to the identity provider