Repository navigation
Configuration
Two applications are configured here, and they share no key. The cockpit server reads
business-cockpit.* and a few of Spring's own. A workflow module reads vanillabp.cockpit.* and
what stands below its own module id. This page holds both.
If you are setting a deployment up, start the application and follow what it prints. Both sides report every gap in one start, each line naming the key which fills it, so the list below is the reference rather than the first thing to read.
-
What the cockpit server needs
The database and how it stores a key with a dot, the name, and the account workflow modules report with. -
Titles, live updates and e-mails
What the user interface shows, how often it refreshes, and how a notification is sent. -
Where a workflow module writes its keys
The four levels, and which of them wins. -
Where the reports go
The cockpit server's address or the Kafka brokers, and the rest of that connection. -
What a workflow module tells the cockpit
Where the module answers, how its forms are served, and in which languages it reports. -
What one workflow or one user task says for itself
The keys a single workflow, or a single task of it, may differ from its module in.
Two values end the start when they are missing, and one more switches off the only way anything gets into the cockpit.
| Key | What it is |
|---|---|
spring.mongodb.uri |
The database. It has to be a replica set, because the live updates read its change stream. Writing the connection out in parts through spring.mongodb.host counts as well |
business-cockpit.title-short |
The short name of this cockpit, shown in the user interface and used as the realm of the login prompt |
bpms-api.username, bpms-api.password
|
The account a workflow module reports with. Without it the BPMS API is switched off and both lists stay empty |
bpms-api.realm-name |
The realm that account is asked for in, BPMS-API in the delivered application |
business-cockpit.jwt.hmacSHA256-base64 |
The key the session token is signed with. Without it a key is generated per start, which logs everybody out on a restart |
Note the spelling of the MongoDB key. Spring Boot 4 moved these settings out of spring.data.mongodb, so the key version 1 used is no longer read at all.
Four more shape the database connection, and none of them has to be written:
| Key | Default | What it is |
|---|---|---|
mongodb.use-timeout |
PT5S |
How long a write may wait for the replica set to acknowledge it |
mongodb.change-stream-max-await-time |
PT1S |
How long the server may hold an idle change stream poll |
mongodb.use-tls |
false |
TLS on the Mongo connection |
mongodb.mode |
MONGODB_4_8 |
AZURE_COSMOS_MONGO_4_2 for Cosmos DB, which delivers fewer change events than MongoDB does |
Two more say how durable a write has to be. The delivered application writes both. A cockpit which writes neither can lose a report and says so on every start:
| Key | Delivered as | What it is |
|---|---|---|
mongodb.write-concern |
majority |
How many nodes have to have a write before the cockpit is told it is stored. majority is the value which survives a failover of the primary, and 0 ends the start |
mongodb.write-concern-journal |
true |
Whether each acknowledging node has the write in its journal before it answers. Only read together with mongodb.write-concern
|
What each value costs, and why a ?w= in the connection string is not read, is
The write concern.
One more key decides what happens to a dot in the business data. A workflow module may report
details with a key like order.id, and MongoDB reads a dot in a path as a step into a nested
document. So by default such a report is not stored. Over REST it is answered with 422, and the
error in the log names the key and this property:
| Key | Default | What it is |
|---|---|---|
business-cockpit.mongodb.map-key-dot-replacement |
unset | What the cockpit stores instead of each dot in a key of details, like ~. It turns back into a dot when the record is read |
Setting it has two costs:
- Search and sorting see the stored form of the key. A column or a filter has to name it as
details.order~idfor the replacement~. The path with the dot finds nothing. - The way back is not exact. A key which holds the replacement already comes back with a dot in its place.
A value which cannot work stops the start: an empty value, a dot, a $ or the character NUL.
The lists are sorted by the paths a user interface names, a column of business data like
details.customer.name for example. Each new combination of paths gets an index in MongoDB, so
sorting stays fast on a large collection. One key limits how many of these indexes the cockpit
creates:
| Key | Default | What it is |
|---|---|---|
business-cockpit.mongodb.sort-indexes-per-collection |
30 |
How many indexes for sorting the cockpit creates per collection at most, from 0 to 60 |
Once the limit is reached, a new combination is sorted without an index of its own. The result is
the same, it only takes longer on a large collection. The log says so once per combination and
names the key. MongoDB takes 64 indexes per collection, so a higher limit has little room left.
To free a place, drop a sort index nobody uses any more. Their names start with _sort_. A value
which is no whole number from 0 to 60 stops the start.
A deployment also wants spring.threads.virtual.enabled: true. Nothing reports it as missing,
because the application runs either way, and without it every open live-update stream and every
proxied request holds a platform thread.
Reports may arrive over Kafka instead of over REST. That needs all three topics and an id telling the instances of the cockpit apart:
| Key | What it is |
|---|---|
bpms-api.kafka.topics.user-task |
The topic user task reports are consumed from |
bpms-api.kafka.topics.workflow |
The topic workflow reports are consumed from |
bpms-api.kafka.topics.workflow-module |
The topic registrations are consumed from |
bpms-api.kafka.group-id-suffix |
What makes this application's consumer group its own |
workerId |
Which instance this is, so every event is consumed once. Written as a start parameter, -DworkerId=local
|
spring.kafka.bootstrap-servers |
The brokers, read by Spring itself |
All three topics or none. Naming one or two switches the ingestion off and says so at startup.
workerId is only asked for once the topics are there, so a deployment reporting over REST never
needs it.
What the user interface shows about the application itself:
| Key | Default | What it is |
|---|---|---|
business-cockpit.title-long |
none | The full title, on the login screen and in the browser tab |
business-cockpit.application-version |
none | The version shown in the user interface |
business-cockpit.build-timestamp |
none | The build time shown next to it |
business-cockpit.application-uri |
none | Where this cockpit is reachable. A notification builds its link back from it |
business-cockpit.default-locale |
de |
The language a user without one of their own gets |
business-cockpit.additional-properties.<name> |
none | Anything else your own user interface wants to read at startup |
The session cookie:
| Key | Default | What it is |
|---|---|---|
business-cockpit.jwt.cookie.name |
bc |
The cookie the token travels in |
business-cockpit.jwt.cookie.expires-duration |
PT12H |
How long a login lasts without a request. A request in the second half renews it |
business-cockpit.jwt.cookie.max-login-duration |
P7D |
How long a login lasts at most, however often it is renewed |
business-cockpit.jwt.cookie.secure |
false |
Whether the cookie is sent over HTTPS only |
business-cockpit.jwt.cookie.same-site |
none | The SameSite attribute |
business-cockpit.jwt.cookie.domain, .path
|
/ for the path |
Where the cookie is sent |
The live updates of the two lists:
| Key | Default | What it is |
|---|---|---|
business-cockpit.gui-sse.update-interval |
500 |
Milliseconds between two flushes to one browser |
business-cockpit.gui-sse.max-items-per-update |
100 |
How many items one flush may carry |
business-cockpit.gui-sse.collecting-interval |
250 |
Milliseconds between two looks at what has been collected |
business-cockpit.gui-sse.filtering-interval |
250 |
Milliseconds between two decisions which browser a change concerns |
business-cockpit.gui-sse.max-known-ids-per-stream |
2000 |
How many ids of tasks and workflows one browser tab is remembered to show |
The collector looks at every browser once per collecting-interval. A browser gets a flush only if
its last one is longer ago than the update-interval. So the update interval is only checked in steps
of the collecting interval. Set it above the update interval and every flush comes late.
A change reaches only the browsers it concerns, and
Security says which those are. The cockpit decides that once
per filtering-interval, for all changes since the last time. It reads the changed tasks and
workflows with one query and decides for every open tab in memory. Without a change it asks
nothing. A longer interval saves a little work and makes every change arrive later.
max-known-ids-per-stream limits what one tab costs on the server, about 125 bytes per id. A tab
which goes over it loads its lists again. Set it to a positive number; any other value is replaced
by the default, and the log says so at startup.
A follow-up date which has passed is a live update too, so the cockpit has to look for those dates:
| Key | Default | What it is |
|---|---|---|
business-cockpit.follow-up.check-rate |
PT1M |
How often the cockpit looks for follow-up dates which have passed |
A task whose follow-up date passed within the last window is sent to the open browsers as a
FOLLOWUP event. Every tick asks for the window since the tick before, so a longer rate means a
later reminder and not a lost one. A restart opens a new window, so a date which passed while the
cockpit was down is not reported.
Notifications are off until an e-mail sender is configured:
| Key | Default | What it is |
|---|---|---|
business-cockpit.notification.smtp.enabled |
false |
true switches the e-mail medium on |
business-cockpit.notification.smtp.from |
none | The sender address |
spring.mail.host, spring.mail.port
|
none | The mail server, read by Spring itself |
business-cockpit.notification.interval |
PT1M |
How often pending notifications are collected and sent |
business-cockpit.notification.max-delivery-attempts |
120 |
Attempts before a notification is given up on, which is two hours at the default interval |
business-cockpit.notification.cleanup-interval |
PT1H |
How often sent notifications are cleared out |
business-cockpit.notification.cleanup-sent-older-than |
PT48H |
How long a sent one is kept |
business-cockpit.notification.templates.email |
classpath:templates/notification/email/ |
Where the e-mail templates are read from |
Two files are looked for below that path, email-header for the subject and email-body for the
text, each as .ftlh before .ftl. An .ftlh produces an HTML mail, an .ftl a plain one, and a
suffix per language works as well, email-body_de.ftl next to email-body.ftl.
The path says where the directory is, and it has to. classpath:templates/mine/ reads the templates
from the application, file:/etc/business-cockpit/templates/ from the file system of the machine the
cockpit runs on. A value without either prefix ends the boot, and the message repeats your value with
both prefixes in front of it, so the fix is to copy the one you meant.
A directory of the file system is what lets an operator change the wording of a mail without a new build. Freemarker reads a changed file again a few seconds later, so the edit takes effect while the cockpit runs. A directory which cannot be read ends the boot as well, rather than turning up as a mail nobody received.
The same rule holds for vanillabp.cockpit.template-loader-path in the workflow module, and for the
same reason: the two sides of the cockpit read one value the same way. Version 1 let that key name a
directory of the file system with no prefix at all, so an application which upgrades writes file:
in front of it.
Notifications are also off per user until that user switches them on, and a user who never logged in is never notified, because the address comes from the record written at login.
A user can switch notifications on for all workflows and make an exception for single workflows. A workflow there is a kind of business case: the workflow module and the BPMN process of the case. A user task of a process which the case started by a call activity counts for the case, because to the user it is one process, only split into several models. The cockpit takes the case from the workflow the task is filed under, so a called process which the workflow module reports as a case of its own gets an exception of its own. While the workflow of a task has not been reported yet, the task counts for its own process.
Everything from here on is configured in the application running the workflows, not in the cockpit. The keys are the ones version 1 read, in the places version 1 read them:
vanillabp.cockpit.<key>
vanillabp.workflow-modules.<moduleId>.cockpit.<key>
vanillabp.workflow-modules.<moduleId>.workflows.<workflow>.cockpit.<key>
vanillabp.workflow-modules.<moduleId>.workflows.<workflow>.user-tasks.<task>.cockpit.<key>
The most specific value wins per key. What a user task says beats what its workflow says, and what a
workflow says beats what its workflow module says. That order is not the cockpit's own. VanillaBP
walks the levels of every plug-in the same way. The cockpit hands it these sections and keeps its
own key names. The same walk also knows a place per adapter at every level
(vanillabp.adapters.<id>.cockpit.<key> and the same below a workflow module). The cockpit does not
use it yet, so nothing binds those keys today. On Quarkus no configuration mapping declares them, and
a key below vanillabp which no mapping declares ends the startup. Spring Boot does not check for
unknown keys, so there such a key is ignored without a word. What
holds for the whole application, which is the transport, the template directory and the two switches, is written once at the top and exists
nowhere else. There is no global default for what is per module: the modules answer at different
addresses, so a value written once for all of them would promise something the cockpit cannot keep.
initiator-source is the one key which stands at the top and still means something per module and
per workflow, because who answers the initiator is a question about code and not about an address.
A workflow module's own defaults file carries these keys as well, <moduleId>.yaml on the classpath
root or below a directory of the module's name, below everything the application itself writes. That
is how a module ships a template path only it knows about.
VanillaBP reads nothing below vanillabp.cockpit by itself, so the cockpit adapter binds that part
of the tree. On Quarkus that binding is also what lets the application start: a key below
vanillabp which nothing declares ends the build there, so a misspelled key is named, together with
the key it was nearest to or with what became of it. Spring Boot ignores such a key.
Exactly one transport is configured. Neither of them, or both, ends the boot with a message naming
the keys of both. These stand below vanillabp.cockpit.
| Key | What it is |
|---|---|
user-tasks-enabled |
false stops user tasks from being reported at all |
workflow-list-enabled |
false stops workflows from being reported at all |
template-loader-path |
The directory the title templates are loaded from, with classpath: or file: in front of it, described on Templates
|
initiator-source |
Who answers the initiator of a report: by-application or system. It has no default, and an application which reports to the cockpit and leaves it out does not start. Who caused it says what each value means |
rest.base-url |
The address of the cockpit server's BPMS API, and with it the choice of REST |
rest.authentication.basic |
true sends a basic authentication with every report |
rest.authentication.username, rest.authentication.password
|
The account it is sent with, which is the one the cockpit configured as bpms-api.username
|
rest.authentication.oauth.base-url |
Where tokens are issued, and with it the choice of the client credentials flow |
rest.authentication.oauth.client-id, rest.authentication.oauth.client-secret
|
The client the token is asked for |
rest.authentication.oauth.basic |
true sends the client in an Authorization header rather than in the token request |
rest.connect-timeout, rest.read-timeout
|
How long connecting and answering may take, as a number of milliseconds or written with a unit (1500ms, PT20S); 1.5 and 10 seconds by default |
rest.proxy.host, rest.proxy.port
|
The HTTP proxy the cockpit server is reached through |
rest.proxy.username, rest.proxy.password
|
The account that proxy expects, where it expects one |
rest.verify-ssl |
false switches the certificate check off altogether |
rest.ssl-truststore-filename, rest.ssl-truststore-password
|
The PKCS12 file the server's certificate is checked against |
kafka.bootstrap-servers |
The brokers, and with them the choice of Kafka |
kafka.topics.user-task |
The topic user task reports go to |
kafka.topics.workflow |
The topic workflow reports go to |
kafka.topics.workflow-module |
The topic a module registers itself on |
kafka.properties.<name> |
Any further producer setting, handed to the Kafka client unchanged |
process-engine-api.remembered-user-tasks |
How many user tasks the Process-Engine-API half holds between a delivery and its dispatch |
The client credentials flow describes its own connection: connect-timeout, read-timeout,
verify-ssl, ssl-truststore-filename, ssl-truststore-password and proxy.* below
rest.authentication.oauth say how the authorization server is reached and inherit nothing from
rest.*. An authorization server behind another proxy stays reachable because of it.
There is no key switching a request log on. The REST client logs through SLF4J, so set the logger
io.vanillabp.cockpit.bpms.api.v1_1.BpmsApi to DEBUG to see what is sent.
These stand below vanillabp.workflow-modules.<moduleId>.cockpit, because that is what they are
about.
| Key | What it is |
|---|---|
workflow-module-uri |
Where the module answers the cockpit, and where the proxy forwards to |
ui-uri-type |
Which kind of user interface the module brings, a name agreed with your cockpit user interface |
ui-uri-path |
Where the module's user interface is served from, /remoteEntry.js for a federated module and the address of a page for an external one |
i18n-languages |
The languages titles are reported in, written as a list |
bpmn-description-language |
The language the names in the BPMN files are written in; mandatory without templates |
group-hierarchy.<group> |
Which groups a group stands for, written as a list |
template-path |
The segment this module contributes to the template lookup path |
initiator-source |
Who answers this module's initiators, where it differs from the application |
A gap is reported at startup, and every gap of the application is reported in one boot. A workflow module which writes none of these keys reports nothing to the cockpit, and the log says so while the application starts.
VanillaBP passes ui-uri-type on and knows no names for it. Which names there are is agreed between
your workflow modules and your cockpit user interface, and it is written down with that user
interface. Whether a page is loaded or opened is decided by ui-uri-path. A path which starts with
http:// or https:// is an address of your own and is opened as it is, which is the way for a
system the cockpit cannot load anything from. Every other path is loaded from the workflow module
through the proxy. A details provider may replace the path per task or per case, so that the user
lands where that one task is handled.
User task forms and status sites says what each of the two means
for a module written in another framework.
Four of the module's keys mean something for a single workflow as well, and one of them for a
single user task. Both levels have a cockpit section of their own.
vanillabp:
workflow-modules:
taxi-ride:
cockpit:
i18n-languages:
- en
- de
bpmn-description-language: en
template-path: rides
workflows:
TaxiRide:
cockpit:
i18n-languages:
- fr
bpmn-description-language: fr
template-path: taxi
user-tasks:
approve:
cockpit:
template-path: approval| Key | What it is |
|---|---|
workflows.<workflow>.cockpit.i18n-languages |
The languages this workflow's titles are reported in |
workflows.<workflow>.cockpit.bpmn-description-language |
The language this workflow's BPMN names are written in |
workflows.<workflow>.cockpit.template-path |
The segment this workflow contributes to the template lookup path |
workflows.<workflow>.cockpit.initiator-source |
Who answers this workflow's initiators, where it differs from its module |
workflows.<workflow>.user-tasks.<task>.cockpit.template-path |
The segment this user task contributes |
The module still has to name i18n-languages and bpmn-description-language. A workflow which says
nothing is a workflow reported the way its module is.
What your engine adds to this, and what it reads out of vanillabp.adapters.<id>.*, is on the
Configuration page of that BPMS' own wiki.
