What this is about
CIAS is not an application you start. It is a set of jars. A jar is a library that sits on an application’s classpath. Whether a CIAS module actually works inside that application is decided by the configuration alone. The jar being there is not enough.
Why no module switches itself on
Spring normally finds classes through a component scan: it searches packages for classes carrying markers such as @Service or @Component and wires them up. CDMS scans everything under com.codamai — and CIAS lives under com.codamai.
If a CIAS class carried such a marker, every CDMS application would pick it up as soon as the jar landed on the classpath somehow, for example as a dependency of a dependency. It would then be wired against objects that do not exist there.
So instead:
-
1Buildputs the jar on the classpathNo class in it carries
@Service,@Repositoryor@Component. The component scan finds nothing. -
2CIASone configuration class per module registers the objects explicitlyThe starter additionally registers its configurations as auto-configurations. Spring deliberately excludes auto-configurations from the scan — that is protection against accidental wiring too.
-
3CIASreads the module's switch
-
4CIASthe switch is not
true→ the module does not work -
5CIASthe switch is
true→ the module worksResult: A module is on because somebody wrote it down, never because a jar came along.
The switches
The picture shows where the switches sit. On the left, what is always there once the filter chain runs; on the right, the modules that are switched on one by one.
flowchart LR
subgraph P["One process"]
direction TB
A["cias-authentication<br/>filter chain, tenant gate<br/><i>no switch: present or absent</i>"]
L{"codamai.cias.tenancy.lookup"}
A --> L
L -->|local| T["cias-tenancy<br/>codamai.cias.tenancy.enabled"]
L -->|remote| C["cias-tenancy-client<br/>+ client.base-url, client.credentials"]
A -.-> U["cias-user<br/>codamai.cias.user.enabled"]
A -.-> Z["cias-authorization<br/>codamai.cias.authorization.enabled"]
A -.-> R["cias-registration<br/>codamai.cias.registration.enabled"]
A -.-> N["cias-notification<br/>codamai.cias.notification.enabled"]
A -.-> AU["cias-audit<br/>codamai.cias.audit.enabled"]
end
IP{"codamai.cias.iam-provider"}
Z --> IP
U --> IP
IP -->|keycloak| K[(Keycloak)]
The complete list:
| Switch | What for | Values |
|---|---|---|
codamai.cias.tenancy.enabled | keeping tenants: create, suspend, close | true |
codamai.cias.tenancy.lookup | where the tenant gate gets its answer from | local or remote |
codamai.cias.tenancy.client.base-url | where CIAS is reachable | only with lookup: remote |
codamai.cias.tenancy.client.credentials.* | the service’s own client at the IAM, used to fetch the service token (token-uri, client-id, client-secret); without it a fixed .token | only with lookup: remote |
codamai.cias.tenancy.rest | the administrative API /cias/admin/tenants | true / false |
codamai.cias.tenancy.lookup-rest | the endpoint other services ask | true / false; true in the standalone CIAS service |
codamai.cias.tenancy.lookup-roles | who may ask that endpoint | role names |
codamai.cias.user.enabled, .rest | the user record and its API | true / false |
codamai.cias.user.lookup-rest, .lookup-roles | per-tenant attribute values for other services | true / false (true in the standalone CIAS service), role names |
codamai.cias.authorization.enabled, .rest | role catalog, grants, reconciliation with Keycloak | true / false |
codamai.cias.registration.enabled, .rest | registration and invitation | true / false |
codamai.cias.notification.enabled, .rest | mail templates and editing them | true / false |
codamai.cias.notification.editor-roles | who may change every mail template, also via CIAS_NOTIFICATION_EDITOR_ROLES | default platform-admin,mail-template-admin |
codamai.cias.notification.mail, .mail.from | how mails leave the house | smtp or log |
codamai.cias.audit.enabled | the audit trail | true / false |
codamai.cias.<module>.persistence | how the module reaches the database | jpa |
codamai.cias.iam-provider | which identity provider CIAS talks to | keycloak or memory |
codamai.cias.migration.enabled | creates and upgrades the CIAS tables | true / false |
codamai.cias.platform-administrator-roles | which roles administer the platform | role names, empty means nobody |
codamai.cdms.cias.reader-roles | who may read GET /cias/fetch in CDMS | role names |
codamai.persistence.tenant.mode | the operating mode of the data storage | SINGLE or MULTI |
cias-authentication has no switch of its own. The filter chain checks every token, and it works as soon as the jar is there. What it needs is not a switch but an answer: codamai.cias.tenancy.lookup tells it whom to ask about the tenant.
The three variants
When: The switch is true.
-
1CIASwires up the module's objects
-
2CIASthe module's endpoints answer, its flows run
Result: The module works.
When: The switch is absent or false.
"Off" looks different per module, and both shapes are deliberate. With tenancy and audit the switch decides whether the objects exist at all: they are simply not there. With user, authorization, registration and notification the objects always exist, and the switch is read where it decides something: the flow is then an implementation that refuses every call, and the endpoints answer 404 before a controller is ever reached.
Result: In both cases nothing happens in the subject area. From the outside the only difference is whether an endpoint says 404 or does not exist at all.
When: A value is missing that CIAS would have to guess.
-
1CIASdoes not find the value; there is deliberately no default
-
2CIASaborts the start, the message names the setting or the type
Result: The application does not come up. That is better than one that comes up generously.
Which case applies is not a matter of taste. It depends on what a wrong default would do:
| What is missing | Consequence |
|---|---|
codamai.cias.tenancy.lookup | Start fails. Without it nobody answers the tenant gate — and a gate that let everybody through for lack of an answer would be a hole opened by a forgotten line. |
client.base-url with lookup: remote | Start fails. An invented address would be worse than none. |
codamai.cias.iam-provider where the modules need it | Start fails. A CIAS without an identity provider would accept registrations it can never carry out. |
codamai.persistence.tenant.mode where there is CDMS persistence | Start fails. A default would silently switch off a tenant isolation or invent one. |
codamai.cias.notification.mail while the mail module is on | Start fails. A default of log would report every mail as sent and deliver none. |
codamai.cias.user.enabled | No error. The module is off, and an installation without user management is a valid state. |
codamai.cias.tenant-gate.ttl and other durations | No error. They have defaults, because a wrong value makes the application slower but opens nothing. |
The whole rule behind this is under Reject when in doubt, together with the list of all mandatory values.
What the application itself has to supply
Some values are not a line in a file but beans. A bean is an object Spring wires up at start.
| Bean | What it decides | Does CIAS ship one? |
|---|---|---|
PlatformAdministrators | which roles administer the platform | no, and it stays that way: a library must not invent an administrator role |
RegistrationRoles | which roles a registration grants | no |
MailTemplateEditPolicy | which mails tenant administrators may rewrite; it reads the editor roles from CIAS_NOTIFICATION_EDITOR_ROLES | no |
ManagedTypesContribution | that the CIAS tables are known to the host’s persistence | no, only the host knows that |
CallerContextProvider | how CIAS learns who is acting right now | the starter ships one that looks at the RequestContext; without the starter you write it yourself |
Clock | the time source | as above |
If one of them is missing where it is needed, the start fails with the name of the type in the message.
What is decided at build time
Part of the decision is made earlier, when the project scaffold is produced. In the hub, a system carries a field cias with three possible values. From it the generator decides which jars go into the application and which configuration classes it writes.
cias field on the system| NONE | REMOTE | EMBEDDED | |
|---|---|---|---|
| What it means | The application does not authenticate anybody through CIAS. | CIAS is a separate service. | CIAS runs in this process. |
| Which jars are added | none | cias-tenancy-client | cias-tenancy, cias-user, cias-authorization, cias-iam-keycloak |
| What the generator writes | none of it | only the entry in .env | the configuration classes and the whole codamai.cias block of application.yaml |
| Which environment variables are added | none | CODAMAI_CIAS_TENANCY_CLIENT_BASE_URL | CIAS_IAM_PROVIDER, CIAS_USER, CIAS_AUTHORIZATION and the credentials of the administration client |
| Who answers the tenant gate | nobody — there is no CIAS filter chain | the CIAS service, over HTTP | cias-tenancy in the same process, as a method call |
Two things here are easy to miss:
REMOTEis the value that applies when nobody says anything. As soon as an application authenticates through CIAS it needs an answer to the tenant gate’s question, or it does not start. So the generator never leaves that question open.REMOTEis the more restrained choice: one jar and two settings.EMBEDDEDpulls tenants, users and the role catalog into the application, and that is a decision about the installation, not a default.- With
EMBEDDEDthe identity half comes along dormant. The jars for users, the role catalog and Keycloak access sit on the classpath, butCIAS_IAM_PROVIDER,CIAS_USERandCIAS_AUTHORIZATIONstart out empty orfalse. Only setting them gives you those modules. That is exactly the point of this page: configuration decides what runs, not the classpath.
A system that does not authenticate through CIAS at all always gets NONE — even if the field says otherwise. Without a filter chain there is no gate that would need an answer.