Skip to content

Latest commit

 

History

2,973 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UZGRID UZGRID

Welcome to UZGRID

UZGRID is a modular server runtime for managing Electric Vehicle (EV) charging infrastructure. This repository (uzgrid-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.

UZGRID is built on CitrineOS, which it derives from and which remains licensed under Apache-2.0. See Licensing and Attribution.

Quick start

Requires Node.js 24+, pnpm, and Docker.

pnpm install
pnpm uzgrid          # builds and starts the whole stack from local source

Then open the operator UI at http://localhost:3000 and sign in with admin@uzgrid.uz / UzGrid!. The server's Swagger docs are at http://localhost:8080/docs.

There is nothing to configure first: pnpm uzgrid creates apps/operator-ui/.env.local from the committed template and generates a session secret for your install. Those demo credentials are local-only defaults — see SECURITY.md before exposing the stack to anything.

To see the dashboard with traffic on it, point some simulated chargers at the server:

node scripts/ocpp16-load.mjs --count 600 --prefix ld2-cp --pad 5 --duration 3600   # hold 600 online
node scripts/ocpp16-sim.mjs  --scenario fleet --count 40 --prefix flt-cp --minutes 25

Table of Contents

Overview

UZGRID 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
    • @AsHandler to handle incoming OCPP messages (1.6 or 2.0.1)
    • @AsMessageEndpoint to expose functions allowing sending messages to charging stations
    • @AsDataEndpoint to 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 background on the upstream project this is built from, see citrineos.github.io.

Architecture Flow

Here's a flowchart-style overview of UZGRID architecture and message flow:

┌───────────────────┐                         ┌───────────────────┐
│ Charging Stations │                         │   Operator UI     │
│  (OCPP 1.6 &      │                         │ (Next.js + Refine)│
│   2.0.1)          │                         └───┬───────────┬───┘
└────────┬──────────┘                  REST (Data │           │ GraphQL
         │ WebSocket                & Message API)│           │
         ▼                                        ▼           ▼
┌───────────────────┐                 ┌───────────────────┐ ┌──────────────┐
│  UZGRID Server │                 │  UZGRID 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

  1. Charging Stations send messages using OCPP 1.6 or OCPP 2.0.1.
  2. UZGRID Server receives and routes messages via WebSocket to the OCPP Router.
  3. The Message Broker (RabbitMQ) handles inter-module communication, enabling asynchronous processing between the OCPP Router and other server modules.
  4. Operational and configuration data are persisted in PostgreSQL (with the PostGIS extension).
  5. 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.
  6. 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:

uzgrid-core/
├── apps/
│   ├── ocpp-server/          # OCPP server entrypoint, Docker setup, migrations (@uzgrid/ocpp-server)
│   ├── ocpi-server/          # OCPI server (@uzgrid/ocpi-server)
│   └── operator-ui/     # Operator web UI — Next.js + Refine (@uzgrid/operator-ui)
├── packages/
│   ├── base/            # Shared types, interfaces, and utilities (@uzgrid/base)
│   └── core/            # Core OCPP modules and logic (@uzgrid/core)
├── scripts/
│   └── stack.mjs             # Docker stack launcher (selects compose files + profiles)
├── docker-compose.yml        # Base stack — published ghcr.io images, ui/ocpi profiles
├── docker-compose.local.yml  # Override: build server + UI from local source (--local)
├── package.json              # Root workspace scripts
└── pnpm-workspace.yaml        # pnpm workspace configuration

Each workspace member documents itself:

Prerequisites

Before you begin, make sure you have the following installed on your system:

Installation

  1. Clone the UZGRID repository to your local machine:

    git clone <uzgrid-core remote>
  2. Install all workspace dependencies from the root directory:

    pnpm install
  3. 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 the launcher at the repository root, which starts the server, the operator UI, RabbitMQ, PostgreSQL, MinIO, and Hasura together. It picks the right Compose files and profiles for you based on a few flags:

pnpm uzgrid              # ocpp-server + operator UI, built from local source
pnpm uzgrid --registry   # pull the published ghcr.io/uzgrid images instead of building
pnpm uzgrid --solo       # ocpp-server only (no operator UI)
pnpm uzgrid --ocpi       # also run the OCPI server
pnpm uzgrid --ocpi --solo   # flags combine freely
pnpm uzgrid down         # stop the stack (pass the same flags you started it with)

Building from local source is the default. The ghcr.io/uzgrid/* images referenced in docker-compose.yml are not published yet, so --registry fails until a release pipeline pushes them. Once that exists, --registry becomes the fast path.

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 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.

The stack is defined by two Compose files at the repository root, driven by the scripts/stack.mjs launcher:

  • docker-compose.yml — the base stack, referencing the published ghcr.io/uzgrid images. The operator UI (ui) and OCPI server (ocpi) are gated behind Compose profiles; the infrastructure and the OCPP server are always on.
  • docker-compose.local.yml — an override (merged on top of the base) that builds the server, UI, and OCPI server from local source. Applied by default; skipped with --registry.

You can call docker compose directly if you prefer, but the launcher saves you from remembering the file/profile matrix — e.g. pnpm uzgrid --ocpi expands to docker compose -f docker-compose.yml -f docker-compose.local.yml --profile ui --profile ocpi up -d --build.

Once a stack is running, the following services should be available:

  • UZGRID Server (service name: uzgrid)
    • 8080: webserver HTTP - Swagger
    • 8081: websocket server TCP connection without auth
    • 8082: websocket server TCP connection with basic HTTP auth
    • 8083: additional websocket server
    • 8443 / 8444: TLS websocket servers
    • 9229: Node.js debugger
  • Operator UI (service name: uzgrid-ui) — ui profile, on by default (omitted with --solo)
  • OCPI Server (service name: uzgrid-ocpi) — ocpi profile, added with --ocpi
    • 8085: OCPI HTTP API
  • RabbitMQ Broker (service name: amqp-broker)
  • 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
  • Hasura GraphQL Engine (service name: graphql-engine)

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

To run an individual app directly, use its own start script — e.g. pnpm --filter @uzgrid/ocpp-server run start, or cd apps/ocpp-server && pnpm start. See each app's README for details.

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 clean - deletes build artifacts across the workspace (dist, .next, and tsconfig.tsbuildinfo)
  • pnpm run fresh - runs clean, then also removes every node_modules and pnpm-lock.yaml and clears the pnpm cache
  • pnpm run fresh:install - convenience command that runs fresh followed by pnpm install

These operate across the whole workspace from the root, so individual packages no longer carry their own clean scripts.

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 files
  • pnpm run lint - runs the linter
  • pnpm run lint:fix - runs the linter with the --fix flag, which attempts to resolve any linting issues

Testing

  • pnpm run test - runs the test suite with Vitest
  • pnpm run test:coverage - runs the test suite with coverage

Component Documentation

  • UZGRID Server (@uzgrid/ocpp-server) — running the server, configuration, bootstrap environment variables, database migrations, OCPP interface generation, custom DataTransfer validation, auto-commissioning, Hasura metadata, and EVerest testing.
  • UZGRID Operator UI (@uzgrid/operator-ui) — running and developing the web UI, and a step-by-step guide to bringing a charging station online end-to-end.
  • UZGRID OCPI Server — running the OCPI server alongside Core, the OCPI modules and endpoints it exposes, and how it connects to the OCPP server stack.
  • Testing with EVerest — running the EVerest charger simulator against UZGRID.

Contributing

Open an issue or a pull request on this repository. For conventions inherited from upstream, the CitrineOS contribution guidelines still apply.

Security

SECURITY.md covers how to report a vulnerability, which committed material is deliberately public (the development TLS test chain and the local demo credentials) and which is never committed, plus the known gaps that must be closed before a production deployment.

Licensing and Attribution

UZGRID is a derivative work of CitrineOS, licensed under the Apache License, Version 2.0. See LICENSE for the full text.

The per-file SPDX-FileCopyrightText: … Contributors to the CitrineOS Project headers throughout this repository are the upstream copyright notices. Apache-2.0 section 4 requires that derivative works retain them, so they must not be removed or rewritten as part of any rebranding. Files authored for UZGRID carry an additional SPDX-FileCopyrightText: 2026 UZGRID line alongside the upstream one; that is the correct way to assert our copyright on new work without stripping theirs.

Support and Contact

Open an issue on this repository.

Roadmap

The upstream project's roadmap, which this fork tracks selectively, is at citrineos.github.io/docs/roadmap.html.

About

Open-source EV charging platform for Uzbekistan — OCPP 1.6/2.0.1 charge point management, OCPI 2.2.1 roaming, and an operator console. Built on CitrineOS.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages