- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
Welcome to CitrineOS
CitrineOS is an open-source project aimed at providing a modular server runtime for managing Electric Vehicle (EV)
charging infrastructure. This repository (citrineos-core) is a pnpm monorepo containing the charging station
management logic, OCPP message routing, the related services, and the operator-facing web UI.
This README covers the repository as a whole: how it is structured, how to install and build it, and how to run the full stack. Each application and package also has its own README with deeper, component-specific documentation — see Repository Structure and Component Documentation.
All other documentation and the issue tracking can be found in our main repository here: https://github.com/citrineos/citrineos.
Table of Contents
- Overview
- Architecture Flow
- Repository Structure
- Prerequisites
- Installation
- Running the Full Stack with Docker
- Information on Docker Setup
- Workspace Scripts
- Component Documentation
- Contributing
- Licensing
- Support and Contact
- Roadmap
Overview
CitrineOS is developed in TypeScript and runs on NodeJS with ws
and fastify. The operator UI is built with Next.js and
Refine.
The system features:
- Dynamic OCPP 1.6 and 2.0.1 message schema validation, prior to transmission using
AJV - Generated OpenAPIv3 specification for easy developer access
- Configurable logical modules with decorators
@AsHandlerto handle incoming OCPP messages (1.6 or 2.0.1)@AsMessageEndpointto expose functions allowing sending messages to charging stations@AsDataEndpointto expose CRUD access to data entities
- Utilities to connect and extend various message broker and cache mechanisms
- Currently supported broker is RabbitMQ
- Currently supported caches are In Memory and Redis
- A web-based Operator UI for managing locations, stations, transactions, and authorizations
For more information on the project go to citrineos.github.io.
Architecture Flow
Here's a flowchart-style overview of CitrineOS architecture and message flow:
┌───────────────────┐ ┌───────────────────┐
│ Charging Stations │ │ Operator UI │
│ (OCPP 1.6 & │ │ (Next.js + Refine)│
│ 2.0.1) │ └───┬───────────┬───┘
└────────┬──────────┘ REST (Data │ │ GraphQL
│ WebSocket & Message API)│ │
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌──────────────┐
│ CitrineOS Server │ │ CitrineOS Server │ │ Hasura │
│ (OCPP Router + │ │ (HTTP / REST) │ │GraphQL Engine│
│ Modules) │ └───────────────────┘ └──────┬───────┘
└────────┬──────────┘ │
│ │
┌─────┴─────────┐ ┌─────────────┐ │
▼ ▼ │ File Storage│ ▼
┌─────────────┐ ┌─────────────┐ │ (S3 / GCS / │ ┌─────────────┐
│ Message │ │ PostgreSQL │ │ MinIO) │ │ PostgreSQL │
│ Broker │ │ (PostGIS) │ └─────────────┘ │ (PostGIS) │
│ (RabbitMQ) │ │ Persistence │ │ (same DB) │
└─────────────┘ └─────────────┘ └─────────────┘
Flow Overview
- Charging Stations send messages using OCPP 1.6 or OCPP 2.0.1.
- CitrineOS Server receives and routes messages via WebSocket to the OCPP Router.
- The Message Broker (RabbitMQ) handles inter-module communication, enabling asynchronous processing between the OCPP Router and other server modules.
- Operational and configuration data are persisted in PostgreSQL (with the PostGIS extension).
- Files and assets are stored in Amazon S3 or Google Cloud Storage (GCS) in supported environments. MinIO is used for local development, providing S3-compatible storage. Local development does not support a GCS-compatible storage backend.
- The Operator UI reads data through the Hasura GraphQL Engine (which queries the same PostgreSQL database) and sends commands and manages entities through the server's REST Data and Message APIs.
Repository Structure
This repository is a pnpm monorepo with the following workspace members:
citrineos-core/
├── apps/
│ ├── Server/ # OCPP server entrypoint, Docker setup, migrations (@citrineos/server)
│ └── operator-ui/ # Operator web UI — Next.js + Refine (@citrineos/operator-ui)
├── packages/
│ ├── base/ # Shared types, interfaces, and utilities (@citrineos/base)
│ └── core/ # Core OCPP modules and logic (@citrineos/core)
├── docker-compose.yml # Full stack from published ghcr.io images (server + UI)
├── docker-compose.local.yml # Full stack, server built from local source
├── package.json # Root workspace scripts
└── pnpm-workspace.yaml # pnpm workspace configuration
Each workspace member documents itself:
- Server — running the server, configuration, bootstrap env vars, migrations, OCPP interfaces, EVerest testing:
apps/Server/README.md - Operator UI — running and developing the web UI, bringing a station online end-to-end:
apps/operator-ui/README.MD
Prerequisites
Before you begin, make sure you have the following installed on your system:
- Node.js (v24.16.0 or higher): Download Node.js
- pnpm (the workspace's package manager): Download pnpm
- Docker (Optional). Version >= 20.10: Download Docker
Installation
-
Clone the CitrineOS repository to your local machine:
git clone https://github.com/citrineos/citrineos-core -
Install all workspace dependencies from the root directory:
pnpm install -
Build all packages from the root directory:
pnpm run build
Running the Full Stack with Docker
The quickest way to get a complete environment running is via the root-level Docker Compose files, which start the server, the operator UI, RabbitMQ, PostgreSQL, MinIO, and Hasura together.
-
From published images (no build required) — run from the repository root:
docker compose up -d -
With the server built from local source — run from the repository root:
docker compose -f docker-compose.local.yml up -d
Once everything is up, the operator UI is available at http://localhost:3000 and the server's Swagger docs at http://localhost:8080/docs.
To run just the backend (no operator UI), or to run the server directly with pnpm for development, see the Server README. To develop the UI on its own, see the Operator UI README.
Information on Docker Setup
You need to install docker (>= 20.10) and docker-compose. Furthermore, Visual Studio Code might be handy as a common integrated development environment.
There are three Compose files:
docker-compose.yml(repository root) — full stack from publishedghcr.ioimages, including the operator UI.docker-compose.local.yml(repository root) — full stack with the server built from local source.apps/Server/docker-compose.yml— backend only (no operator UI), server built from local source (see the Server README).
Once a stack is running, the following services should be available:
- CitrineOS Server (service name: citrine)
8080: webserver HTTP - Swagger8081: websocket server TCP connection without auth8082: websocket server TCP connection with basic HTTP auth8083: additional websocket server8443/8444: TLS websocket servers9229: Node.js debugger
- Operator UI (service name: citrine-ui) — only in the root-level Compose files
3000: Operator UI
- RabbitMQ Broker (service name: amqp-broker)
5672: AMQP TCP connection15672: RabbitMQ management interface
- PostgreSQL (service name: ocpp-db), PostGIS-enabled PostgreSQL database for persistence
5432: SQL TCP connection
- MinIO (service name: minio) for S3-compatible local file storage
9000: S3 API endpoint9001: MinIO web console
- Hasura GraphQL Engine (service name: graphql-engine)
8090: Hasura console
These services live inside the docker network with their respective ports. By default these ports are directly
accessible using localhost:8080 for example.
Workspace Scripts
These scripts are run from the repository root and operate across the whole workspace.
Building
pnpm run build- builds all packagespnpm run start- starts the CitrineOS server (delegates to@citrineos/server)
Running clean and fresh
The workspace consists of multiple pnpm packages that are loaded as dependencies when running the application. This
means packages need to be rebuilt when their files change. In some cases — in particular when switching between
branches, especially when there are changes in a package.json — the already built dist as well as the generated
pnpm-lock.yaml may become invalid.
To alleviate the above, we created the following commands (run from the root directory):
pnpm run fresh- deletes allnode_modules,dist,tsbuildinfo, andpnpm-lock.yaml, then clears the pnpm cachepnpm run clean- subset ofpnpm run fresh; only deletes build artifacts (distandtsbuildinfo)pnpm run fi- convenience command that runsfreshfollowed bypnpm install
Linting and Prettier
ESLint and Prettier have been configured to help support syntactical consistency throughout the codebase.
pnpm run prettier- runs Prettier and formats the filespnpm run lint- runs the linterpnpm run lint-fix- runs Prettier and the linter with the--fixflag, which attempts to resolve any linting issues
Testing
pnpm run test- runs the test suite with Vitestpnpm run coverage- runs the test suite with coverage
Component Documentation
- CitrineOS Server (
@citrineos/server) — running the server, configuration, bootstrap environment variables, database migrations, OCPP interface generation, custom DataTransfer validation, auto-commissioning, Hasura metadata, and EVerest testing. - CitrineOS Operator UI (
@citrineos/operator-ui) — running and developing the web UI, and a step-by-step guide to bringing a charging station online end-to-end. - Testing with EVerest — running the EVerest charger simulator against CitrineOS.
Contributing
We welcome contributions from the community. If you would like to contribute to CitrineOS, please follow our contribution guidelines.
Licensing
CitrineOS and its subprojects are licensed under the Apache License, Version 2.0. See LICENSE for the full license text.
Support and Contact
If you have any questions or need assistance, feel free to reach out to us on our community forum or create an issue on the GitHub repository.


