OptimCE is an open-source platform for managing renewable energy communities, built for the Belgian energy-sharing context. It brings together a member CRM, energy-sharing allocation keys and simulations, invoicing, document generation, and a community news board, behind a single authenticated web application. To learn more about the project, visit www.optimce.be.
This repository is the development monorepo: it aggregates all OptimCE services as git submodules and provides the Docker Compose environment to run the full platform locally. For an example of a production deployment, see OptimCE/production.
Service code lives in the individual repositories, included here as submodules:
| Path | Repository | Description |
|---|---|---|
crm-backend/ |
OptimCE/crm-backend | CRM backend API (Node.js / TypeScript) |
crm-frontend/ |
OptimCE/crm-frontend | Web interface (Angular) |
allocation-key-generation/ |
OptimCE/allocation-key-generation | Energy-sharing allocation key generation service (Python) |
simulation-key/ |
OptimCE/allocation-key-simulation | Allocation key simulation service (Python) |
administrative-document/ |
OptimCE/administrative-document | Regulatory dossier tracking and CWaPE form generation service (Python) |
billing/ |
OptimCE/billing | Invoicing service (Python) |
document-generation/ |
OptimCE/document-generation | Document generation service (Python) |
news-board/ |
OptimCE/news-board | Community news board service (Python) |
notification-dispatch/ |
OptimCE/notification-dispatch | Outbound email delivery worker (Python) |
keycloak/kc-groupid-mapper/ |
OptimCE/kc-groupid-mapper | Keycloak mapper adding group information to tokens |
keycloak/optimce-keycloak-theme/ |
OptimCE/optimce-keycloak-theme | Keycloak login theme (Keycloakify) |
krakend/swagger2krakend/ |
OptimCE/swagger2krakend | OpenAPI → KrakenD configuration generator (Python) |
The remaining directories hold the orchestration and infrastructure configuration that belongs to this repository:
| Path | Description |
|---|---|
krakend/ |
API gateway configuration (generated krakend.json and OpenAPI sources) |
keycloak/ |
Keycloak image build, realm configuration, and providers |
nginx/ |
Reverse proxy configuration and certificates |
postgres/ |
Provisioning and verification for the unified database instance — roles, databases, grants (README) |
crm-frontend-config/ |
Generated frontend runtime configuration |
reference/ |
Shared reference data (e.g. regulators.json) |
docs/runbooks/ |
Operational procedures, e.g. the production database consolidation |
The development stack (docker-compose.dev.yml) runs the following services:
Applications
- crm-frontend: Angular user interface
- crm-backend: CRM backend API
- allocation-key-generation (+ worker): allocation key computation
- simulation-key (+ worker): energy-sharing simulations
- administrative-document (+ worker): regulatory dossiers, CWaPE deadlines and form generation
- billing (+ worker): invoicing
- document-generation: document generation worker
- notification-dispatch: outbound email delivery worker
- optimce-news-board: community news board
Databases (PostgreSQL)
- postgres: one instance, six logical databases —
crm_db,allocation_key_local,simulation_key_local,news_board_local,billing_local,administrative_document_local— each owned by its own login role. See postgres/README.md. - keycloak-db: a separate instance; Keycloak manages its own schema.
Platform
- keycloak: authentication server
- krakend: API gateway
- reverse-proxy: Nginx reverse proxy, single entry point for the app
- minio: S3-compatible object storage
- nats: messaging between services and their workers
- jaeger: distributed tracing (OpenTelemetry)
Configuration generation (init profile, run-once containers)
- swagger-doc-gen, generation-doc-gen, simulation-doc-gen, news-doc-gen, billing-doc-gen, administrative-document-doc-gen: collect each service's OpenAPI specification
- krakend-config, keycloak-config, nginx-config, crm-frontend-config: render the gateway, auth, proxy, and frontend configuration from templates
- Docker
- Docker Compose
- Git
The services are git submodules, so clone recursively:
git clone --recurse-submodules https://github.com/OptimCE/monorepo.git
cd monorepoIf you already cloned without submodules:
git submodule update --init --recursiveBefore starting the application, make sure to configure the .env.dev file
with the appropriate environment variables, particularly the passwords:
# Modify passwords in .env.dev
DB_PASSWORD=changeme_db_password
KEYCLOAK_DB_PASSWORD=changeme_keycloak_db_password
KEYCLOAK_ADMIN_PASSWORD=changeme_keycloak_admin_passwordAll application databases live in one PostgreSQL instance (the postgres
service, host port 8080), one logical database per service, each owned by its own
login role. The postgres-init sidecar provisions roles, databases, schemas,
seeds and grants on every start — see
postgres/README.md for the full picture, including the
least-privilege grant matrix that governs what each service may do in crm_db.
Each service's existing schema file is reused verbatim:
| Database | Schema applied |
|---|---|
crm_db |
crm-backend/tests/sql/init.sql (DDL + dev seed data) |
allocation_key_local |
allocation-key-generation/scripts/sql/schema.sql |
simulation_key_local |
simulation-key/scripts/sql/schema.sql |
news_board_local |
news-board/scripts/sql/schema.sql |
billing_local |
billing/scripts/sql/schema.sql |
administrative_document_local |
administrative-document/scripts/sql/schema.sql + its seeds |
crm-backend/database_script/init.sql is the pure-DDL sibling used for
production; it is not applied by the dev stack.
Keycloak keeps its own instance (keycloak-db, port 8081) and initialises a
base realm from keycloak/dev-config.json.
If you want to modify a schema, edit the corresponding SQL file and recreate the stack — a schema is applied only to an empty database, so an existing one is never re-written in place.
Some configurations are generated automatically via the init profile services
(see Architecture), for example:
swagger-doc-gen: generates./krakend/config/swagger.yamlkrakend-config: generates./krakend/config/krakend.jsoncrm-frontend-config: generates./crm-frontend-config/config.json
To only run the configuration generation:
docker compose --env-file .env.dev -f docker-compose.dev.yml --profile init up --buildWhen the init profile containers have finished, you can stop them with:
docker compose --env-file .env.dev -f docker-compose.dev.yml --profile init downThen, start the full stack normally.
A wrapper is available to control the full stack with the Docker Compose init
and dev profiles: ./docker-stack.sh (or docker-stack.bat on Windows).
If needed, make it executable:
chmod +x ./docker-stack.shMain commands:
./docker-stack.sh start
./docker-stack.sh stop
./docker-stack.sh restart
./docker-stack.sh verifyverify proves the database isolation: that no service can reach another
service's database, and that every CRM write it is granted actually lands. The
second half matters because the annexes swallow their own audit and notification
failures — a missing grant returns HTTP 200 and silently loses the row. See
postgres/README.md.
The flow automatically executes:
- the
initprofile to generate configurations - stopping the
initprofile - starting the
devprofile in detached mode
With --skip-init, the script skips the init steps and starts the dev
profile directly.
Available options for start and restart:
./docker-stack.sh start --no-pull
./docker-stack.sh start --no-build
./docker-stack.sh start --build
./docker-stack.sh start --skip-initAvailable options for start, stop, and restart:
./docker-stack.sh start -s swagger-doc-gen
./docker-stack.sh stop --service krakend
./docker-stack.sh restart -s keycloakWith -s / --service, the wrapper targets only the requested service instead
of the entire stack. For stop, this executes a docker compose stop <service>.
Wrapper behavior:
- Automatically detects
docker-composeordocker compose - Checks that the Docker service is running
- Runs the
initprofile (configuration generation), then stops it, unless--skip-initis used - Runs the
devprofile in detached mode - Uses
.env.devanddocker-compose.dev.yml
This script is the recommended method for standard start/stop/restart operations.
To start all services, use the following command:
docker compose --env-file .env.dev -f docker-compose.dev.yml --profile dev upTo run in detached mode (background):
docker compose --env-file .env.dev -f docker-compose.dev.yml --profile dev up -dTo rebuild the images before starting:
docker compose --env-file .env.dev -f docker-compose.dev.yml --profile dev up --buildTo stop all services:
docker compose --env-file .env.dev -f docker-compose.dev.yml --profile dev downTo completely reset the databases, stop and remove the containers:
docker compose --env-file .env.dev -f docker-compose.dev.yml --profile dev down
docker compose --env-file .env.dev -f docker-compose.dev.yml --profile dev upThe initialization scripts will be re-executed automatically on the next startup.
| Service | Host Port | Container Port | Protocol | Usage |
|---|---|---|---|---|
allocation-key-generation |
8002 |
8000 |
tcp |
Allocation key API |
simulation-key |
8003 |
8000 |
tcp |
Simulation API |
optimce-news-board |
8004 |
8000 |
tcp |
News board API |
billing |
8005 |
8000 |
tcp |
Billing API |
administrative-document |
8006 |
8000 |
tcp |
Administrative document API |
mailpit |
8007 |
8025 |
tcp |
Dev mail catcher (web UI) |
postgres |
8080 |
5432 |
tcp |
PostgreSQL — six logical databases (crm_db, billing_local, …) |
keycloak-db |
8081 |
5432 |
tcp |
PostgreSQL Keycloak |
keycloak |
8082 |
8080 |
tcp |
Keycloak Authentication |
jaeger |
8084 |
6831 |
udp |
Jaeger Collector |
jaeger |
8085 |
16686 |
tcp |
Jaeger UI |
krakend |
8086 |
8080 |
tcp |
API Gateway |
reverse-proxy |
8087 |
80 |
tcp |
HTTP reverse proxy |
reverse-proxy |
8088 |
443 |
tcp |
HTTPS reverse proxy |
crm-backend |
8089 |
80 |
tcp |
Backend API |
crm-frontend |
8090 |
80 |
tcp |
Frontend interface |
minio |
8091 |
9000 |
tcp |
MinIO API |
minio |
8092 |
9001 |
tcp |
MinIO Console |
nats |
8094 |
4222 |
tcp |
NATS client |
nats |
8095 |
8222 |
tcp |
NATS monitoring |
OptimCE ships in French, English, Dutch, and German. Each service keeps its own message catalog in its own repository — the frontend UI strings, and the API error messages returned by the backend and the Python services — all as nested JSON.
These catalogs are translated on Weblate, a libre
web-based continuous localization platform, which hosts OptimCE free of charge
under its Libre plan for free software. The optimce project there carries one
component per repository, and Weblate commits approved translations straight
back to each service repository.
Translate OptimCE on Weblate →
The translated versions of this README (docs/README.fr.md, docs/README.de.md,
docs/README.nl.md) are maintained by hand and are not part of the Weblate
project.
Contributions are welcome! Please read the contributing guidelines and our Code of Conduct before opening an issue or pull request.
To report a security vulnerability, please follow the security policy — do not open a public issue.
This project is licensed under the Apache License 2.0.