Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- 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
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,32 @@
# Ignore node modules directories
**/node_modules
# Ignore distribution directories
**/dist
# Ignore build caches/output that are regenerated inside the image
# (operator-ui/.next can exceed 1.5GB and is the main build-context bloat)
**/.next
**/.turbo
# Common ignore files
.gitignore
**/package-lock.json
# Specific files and file types to ignore
**/*.js
**/*.cjs
**/*.md
**/*.yml
**/*.tsbuildinfo
**/*.log
**/*.tgz
**/*.Dockerfile
# Specific directories to ignore
**/Server/data
# VCS and CI/test artifacts (not needed in any image)
.git
**/playwright-report
**/test-results

View File

@@ -0,0 +1 @@
* text=auto eol=lf

View File

@@ -0,0 +1,115 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
name: REUSE License Check
on:
pull_request:
jobs:
reuse-check:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install REUSE tool
run: pip install reuse
- name: Run REUSE lint with filtering
run: |
# Run reuse lint and capture output
if ! reuse lint > reuse_output.txt 2>&1; then
echo "REUSE lint found issues. Checking against ignore list..."
# Check if .reuse/ignore-files.txt exists
if [ -f ".reuse/ignore-files.txt" ]; then
echo "Found .reuse/ignore-files.txt file. Filtering results..."
# Extract failing files from the REUSE output
sed '/SUMMARY/q' reuse_output.txt | grep "^\* " | sed 's/^\* //' > failing_files.txt
# Function to check if a file should be ignored
should_ignore_file() {
local file="$1"
local ignore_file="$2"
# Skip the reuse output file itself
if [[ "$file" == "reuse_output.txt" ]]; then
return 0 # ignore (return true)
fi
while IFS= read -r ignore_pattern; do
# Skip empty lines and comments
[[ -z "$ignore_pattern" || "$ignore_pattern" =~ ^[[:space:]]*# ]] && continue
# Remove trailing whitespace
ignore_pattern=$(echo "$ignore_pattern" | sed 's/[[:space:]]*$//')
# Check for exact match first
if [[ "$file" == "$ignore_pattern" ]]; then
return 0 # ignore
fi
# Check if it's a directory pattern (ends with /)
if [[ "$ignore_pattern" == */ ]]; then
# Check if file is in this directory
if [[ "$file" == "$ignore_pattern"* ]]; then
return 0 # ignore
fi
else
# Use shell pattern matching for globs
# Enable extended globbing for more complex patterns
shopt -s extglob nullglob
if [[ "$file" == $ignore_pattern ]]; then
return 0 # ignore
fi
shopt -u extglob nullglob
fi
done < "$ignore_file"
return 1 # don't ignore
}
# Find files that are failing but NOT in the ignore list
> unignored_failures.txt
while IFS= read -r failing_file; do
if ! should_ignore_file "$failing_file" ".reuse/ignore-files.txt"; then
echo "$failing_file" >> unignored_failures.txt
fi
done < failing_files.txt
# Check if there are any unignored failures
if [ -s unignored_failures.txt ]; then
echo "❌ REUSE compliance failed for the following files:"
cat unignored_failures.txt
echo ""
echo "These files need proper license headers or should be added to .reuse/ignore-files.txt"
echo ""
exit 1
else
echo "✅ All REUSE lint failures are accounted for in .reuse/ignore-files.txt"
ignored_count=$(wc -l < failing_files.txt)
echo "Ignored $ignored_count file(s) as specified in .reuse/ignore-files.txt"
fi
else
echo "❌ REUSE lint failed and no .reuse/ignore-files.txt file found"
echo ""
echo "Full REUSE lint output:"
cat reuse_output.txt
exit 1
fi
else
echo "✅ REUSE lint passed - all files have proper license compliance"
fi
- name: Clean up temporary files
if: always()
run: |
rm -f reuse_output.txt failing_files.txt unignored_failures.txt

View File

@@ -0,0 +1,37 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
name: Linter
on:
pull_request:
concurrency:
# Rerun check when the PR is updated, https://stackoverflow.com/a/72408109
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
linter:
name: Linter
runs-on: ubuntu-22.04 # todo: to prevent needing to run npm i we can have our own image where everything is installed
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.19.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24.16.0'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Lint
run: pnpm run lint

View File

@@ -0,0 +1,80 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
name: Publish Swagger
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
outputs:
swagger_valid: ${{ steps.validate-swagger.outputs.swagger_valid }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Build and Run Docker Compose
run: |
docker compose -f ${{ github.workspace }}/apps/Server/docker-compose.yml up -d
- name: Generate Swagger JSON
run: |
echo "Fetching Swagger JSON..."
curl --retry 3 --retry-delay 3 --retry-all-errors -o swagger.json http://localhost:8080/docs/json
- name: Validate Swagger JSON
id: validate-swagger
run: |
if jq empty swagger.json; then
echo "swagger_valid=true" >> $GITHUB_OUTPUT
else
echo "swagger_valid=false" >> $GITHUB_OUTPUT && exit 1
fi
- name: Upload Swagger JSON as Artifact
uses: actions/upload-artifact@v4
with:
name: swagger-json
path: swagger.json
- name: Clean up Docker
run: docker compose -f ${{ github.workspace }}/apps/Server/docker-compose.yml down
publish-swagger:
needs: build
if: needs.build.outputs.swagger_valid == 'true'
runs-on: ubuntu-latest
steps:
- name: Download Swagger Artifact
uses: actions/download-artifact@v4
with:
name: swagger-json
- name: Checkout citrineos.github.io
uses: actions/checkout@v4
with:
repository: citrineos/citrineos.github.io
ssh-key: ${{ secrets.CITRINEOS_GITHUB_IO_DEPLOY_KEY }}
path: citrineos.github.io
- name: Push Swagger JSON
run: |
cd citrineos.github.io
git checkout -b swagger-updates || git checkout swagger-updates
cp ../swagger.json ./docs/assets
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add ./docs/assets/swagger.json
git commit -m "Update Swagger JSON from PR"
git push -f origin swagger-updates --verbose

View File

@@ -0,0 +1,75 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
name: Push Release Tagged Operator UI
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
strategy:
matrix:
image:
- name: citrineos-operator-ui
context: ./
dockerfile: ./apps/operator-ui/Dockerfile
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24.16.0'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
install: true
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ matrix.image.name }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-${{ matrix.image.name }}-
- name: Log in to GitHub Container Registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/${{ matrix.image.name }}
tags: |
type=ref,event=tag
type=raw,value=latest
- name: Build and push operator-ui image
uses: docker/build-push-action@v5
with:
context: ${{ matrix.image.context }}
file: ${{ matrix.image.dockerfile }}
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
# Move cache to avoid ever-growing cache
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

View File

@@ -0,0 +1,77 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
name: Push Release Tagged Server
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
strategy:
matrix:
image:
- name: citrineos-server
context: ./
dockerfile: ./apps/Server/deploy.Dockerfile
config: ''
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24.16.0'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
install: true
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ matrix.image.name }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-${{ matrix.image.name }}-
- name: Log in to GitHub Container Registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/${{ matrix.image.name }}
tags: |
type=ref,event=tag
type=raw,value=latest
- name: Build and push server image
if: ${{ matrix.image.name == 'citrineos-server' }}
uses: docker/build-push-action@v5
with:
context: ${{ matrix.image.context }}
file: ${{ matrix.image.dockerfile }}
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
# Move cache to avoid ever-growing cache
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

View File

@@ -0,0 +1,111 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
name: Renew Certificates
# This workflow is to renew the certificates which are used for security profile 2&3
# It can be triggered manually or automatically every month to check if the certificates are still valid
# and renew them if needed on main branch
on:
workflow_dispatch:
schedule:
- cron: '0 0 1 * *' # Runs on the 1st of every month
env:
TARGET_BRANCH: main
jobs:
renew-certs:
runs-on: ubuntu-latest
permissions:
contents: write
defaults:
run:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ env.TARGET_BRANCH }}
- name: Set up OpenSSL + Node
uses: actions/setup-node@v4
with:
node-version: '24.16.0'
- name: Check certificate validity
id: check_cert
run: |
CERT_FILE="apps/Server/src/assets/certificates/certChain.pem"
# extract the expiry date of the leaf certificate (first cert in the chain)
EXPIRY_DATE=$(awk 'BEGIN{found=0} /BEGIN CERTIFICATE/{if (++found==1) print > "/tmp/leaf.pem"; next} found==1 {print > "/tmp/leaf.pem"} /END CERTIFICATE/{if (found==1) exit}' "$CERT_FILE")
END_DATE=$(openssl x509 -enddate -noout -in /tmp/leaf.pem | cut -d= -f2)
END_TIMESTAMP=$(date -d "$END_DATE" +%s)
NOW_TIMESTAMP=$(date +%s)
DIFF_DAYS=$(( (END_TIMESTAMP - NOW_TIMESTAMP) / 86400 ))
echo "Leaf certificate expires in $DIFF_DAYS days."
if [ "$DIFF_DAYS" -lt 31 ]; then
echo "cert_needs_renewal=true" >> "$GITHUB_OUTPUT"
else
echo "cert_needs_renewal=false" >> "$GITHUB_OUTPUT"
fi
- name: Skip if certificate is valid
if: steps.check_cert.outputs.cert_needs_renewal == 'false'
run: echo "Certificate is still valid. Skipping renewal."
- name: Generate new certificates
if: steps.check_cert.outputs.cert_needs_renewal == 'true'
run: |
CERT_DIR="apps/Server/src/assets/certificates"
CONFIG_FILE=".github/workflows/renew-certs/openssl.config"
mkdir -p "$CERT_DIR"
# Create root key and self-signed root cert
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -out "$CERT_DIR/rootKey.pem"
openssl req -x509 -new -nodes -key "$CERT_DIR/rootKey.pem" -sha256 -days 365 \
-subj "/CN=host.docker.internal root/O=S44/C=US" \
-out "$CERT_DIR/rootCertificate.pem" \
-config "$CONFIG_FILE" -extensions v3_ca
# Create sub-CA key and CSR
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -out "$CERT_DIR/subCAKey.pem"
openssl req -new -key "$CERT_DIR/subCAKey.pem" \
-subj "/CN=host.docker.internal subCA/O=S44/C=US" \
-out "$CERT_DIR/subCA.csr" \
-config "$CONFIG_FILE"
# Sign sub-CA cert with root
openssl x509 -req -in "$CERT_DIR/subCA.csr" -CA "$CERT_DIR/rootCertificate.pem" -CAkey "$CERT_DIR/rootKey.pem" -CAcreateserial \
-out "$CERT_DIR/subCA.pem" -days 365 -sha256 \
-extfile "$CONFIG_FILE" -extensions v3_subca
# Create leaf key and CSR
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -out "$CERT_DIR/leafKey.pem"
openssl req -new -key "$CERT_DIR/leafKey.pem" \
-subj "/CN=host.docker.internal/O=S44/C=US" \
-out "$CERT_DIR/leaf.csr" \
-config "$CONFIG_FILE"
# Sign leaf cert with sub-CA
openssl x509 -req -in "$CERT_DIR/leaf.csr" -CA "$CERT_DIR/subCA.pem" -CAkey "$CERT_DIR/subCAKey.pem" -CAcreateserial \
-out "$CERT_DIR/leaf.pem" -days 365 -sha256 \
-extfile "$CONFIG_FILE" -extensions v3_leaf
# Combine into certChain.pem
cat "$CERT_DIR/leaf.pem" "$CERT_DIR/subCA.pem" > "$CERT_DIR/certChain.pem"
# Clean up intermediate files
rm -f "$CERT_DIR/"*.csr "$CERT_DIR/"*.srl "$CERT_DIR/leaf.pem" "$CERT_DIR/subCA.pem"
- name: Commit and push renewed certificates
if: steps.check_cert.outputs.cert_needs_renewal == 'true'
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add apps/Server/src/assets/certificates
git commit -m "chore(cert): Renewed certificates [auto]"
git push origin $TARGET_BRANCH

View File

@@ -0,0 +1,17 @@
[ v3_ca ]
basicConstraints = critical,CA:true,pathlen:1
keyUsage = critical,digitalSignature, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
[ v3_subca ]
basicConstraints = critical,CA:true,pathlen:0
keyUsage = critical,digitalSignature, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
[ v3_leaf ]
basicConstraints = CA:false
keyUsage = critical,digitalSignature, keyEncipherment
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer

View File

@@ -0,0 +1,86 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
name: Test Server on Pull Request
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
changes:
runs-on: ubuntu-latest
outputs:
server: ${{ steps.filter.outputs.server }}
shared: ${{ steps.filter.outputs.shared }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
shared:
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'package.json'
- 'tsconfig*.json'
- '.github/workflows/**'
server:
- 'apps/Server/**'
- 'packages/**'
build:
needs: changes
if: ${{ needs.changes.outputs.server == 'true' || needs.changes.outputs.shared == 'true' }}
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24.16.0'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Build and Run Docker Compose
run: docker compose -f ${{ github.workspace }}/apps/Server/docker-compose.yml up -d
- name: Install Newman
run: |
npm install -g newman
sleep 10
- name: Integration test
run: |
RETRY_COUNT=0
MAX_RETRIES=5
RETRY_DELAY=10
until [ $RETRY_COUNT -ge $MAX_RETRIES ]
do
newman run ${{ github.workspace }}/.github/workflows/tests/CI/collection.json -e ${{ github.workspace }}/.github/workflows/tests/CI/environment.json --reporters cli && break
RETRY_COUNT=$((RETRY_COUNT+1))
echo "Retry $RETRY_COUNT/$MAX_RETRIES in $RETRY_DELAY seconds..."
sleep $RETRY_DELAY
done
shell: bash
- name: Get logs for all containers
if: failure()
run: |
docker ps -a
docker container ls -aq | xargs -I {} docker logs {}
- name: Clean up
run: docker compose -f ${{ github.workspace }}/apps/Server/docker-compose.yml down

View File

@@ -0,0 +1,44 @@
{
"info": {
"name": "API Collection",
"_postman_id": "a1234567-b89c-4d12-95a3-b4567e89c0d1",
"description": "A collection contains CI tests.",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Check Health",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('Status code is 200', function () {",
" pm.response.to.have.status(200);",
"});"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"header": [],
"body": {
"mode": "none"
},
"url": {
"raw": "{{host}}:{{port}}/health",
"protocol": "http",
"host": ["{{host}}"],
"port": "{{port}}",
"path": ["health"]
}
},
"response": []
}
],
"auth": {
"type": "noauth"
}
}

View File

@@ -0,0 +1,16 @@
{
"id": "4454509f-00c2-4b1b-837a-e45d9814dbf6",
"name": "Local Environment",
"values": [
{
"key": "host",
"value": "localhost",
"enabled": true
},
{
"key": "port",
"value": "8080",
"enabled": true
}
]
}

View File

@@ -0,0 +1,181 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
name: Tests
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
changes:
runs-on: ubuntu-latest
outputs:
base: ${{ steps.filter.outputs.base }}
core: ${{ steps.filter.outputs.core }}
server: ${{ steps.filter.outputs.server }}
operator-ui: ${{ steps.filter.outputs.operator-ui }}
shared: ${{ steps.filter.outputs.shared }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
shared:
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'package.json'
- 'tsconfig*.json'
- 'vitest.config.ts'
- 'eslint.config.base.js'
- '.github/workflows/**'
base:
- 'packages/base/**'
core:
- 'packages/core/**'
- 'packages/base/**'
server:
- 'apps/Server/**'
- 'packages/**'
operator-ui:
- 'apps/operator-ui/**'
- 'packages/base/**'
tests:
needs: changes
if: ${{ needs.changes.outputs.base == 'true' || needs.changes.outputs.core == 'true' || needs.changes.outputs.server == 'true' || needs.changes.outputs.shared == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.19.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24.16.0"
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm run build
- name: Run tests
env:
NODE_OPTIONS: "--max_old_space_size=4096"
run: pnpm test
operator-ui-e2e:
needs: changes
if: ${{ needs.changes.outputs.operator-ui == 'true' || needs.changes.outputs.shared == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.19.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24.16.0"
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Build @citrineos/operator-ui and its workspace deps
run: pnpm --filter "@citrineos/operator-ui..." run build
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('apps/operator-ui/package.json') }}
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: apps/operator-ui
run: pnpm exec playwright install --with-deps chromium
- name: Install Playwright system deps
if: steps.playwright-cache.outputs.cache-hit == 'true'
working-directory: apps/operator-ui
run: pnpm exec playwright install-deps chromium
- name: Start backend stack (Hasura + CitrineOS Core)
# Build with the default docker driver (builds straight into the daemon's
# image store) — no tarball export/import or cache round-trip, which is
# what made the build-push-action approach ~3min slower than the build itself.
run: docker compose -f ${{ github.workspace }}/apps/Server/docker-compose.yml up -d --build
- name: Wait for backend healthy
run: |
set -e
for i in $(seq 1 60); do
HASURA_OK=$(curl -fs http://localhost:8090/healthz > /dev/null && echo yes || echo no)
CORE_OK=$(curl -fs http://localhost:8080/health > /dev/null && echo yes || echo no)
if [ "$HASURA_OK" = "yes" ] && [ "$CORE_OK" = "yes" ]; then
echo "Backend healthy after ${i} attempts."
exit 0
fi
echo "Waiting for backend (attempt ${i}/60): hasura=${HASURA_OK} core=${CORE_OK}"
sleep 5
done
echo "Backend did not become healthy in time."
exit 1
- name: Run operator-ui Playwright tests
env:
CI: "true"
NODE_OPTIONS: "--max_old_space_size=4096"
NEXT_PUBLIC_ADMIN_EMAIL: admin@citrineos.com
ADMIN_PASSWORD: CitrineOS!
NEXTAUTH_URL: http://localhost:3000
NEXTAUTH_SECRET: test-secret-for-e2e-only
HASURA_ADMIN_SECRET: CitrineOS!
NEXT_PUBLIC_API_URL: http://localhost:8090/v1/graphql
NEXT_PUBLIC_WS_URL: ws://localhost:8090/v1/graphql
NEXT_PUBLIC_CITRINE_CORE_URL: http://localhost:8080
NEXT_PUBLIC_FILE_SERVER_URL: http://localhost:8050
ALLOW_IMAGE_UPLOAD: "false"
working-directory: apps/operator-ui
run: pnpm exec playwright test --project=chromium-desktop
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: operator-ui-playwright-report
path: |
apps/operator-ui/playwright-report
apps/operator-ui/test-results
apps/operator-ui/reports
retention-days: 7
- name: Dump backend logs on failure
if: failure()
run: |
docker ps -a
docker container ls -aq | xargs -I {} docker logs {} || true
- name: Tear down backend stack
if: always()
run: docker compose -f ${{ github.workspace }}/apps/Server/docker-compose.yml down -v

307
tools/citrineos-core-main/.gitignore vendored Normal file
View File

@@ -0,0 +1,307 @@
# Created by https://www.toptal.com/developers/gitignore/api/macos,node,git
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,node,git
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig
# Created by git when using merge tools for conflicts.
# Patterns like *.BASE.* are intentionally narrowed (anchored to the
# capitalized suffix forms vim/kdiff3 actually produce) because macOS's
# case-insensitive git matched the unanchored *.BASE.* against ordinary
# source files like get.base.report.modal.tsx, silently dropping them
# from commits.
*_BACKUP_*
*_BASE_*
*_LOCAL_*
*_REMOTE_*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
.vercel
next-env.d.ts
# Playwright
playwright/.auth/
playwright-report/
test-results/
reports/
tests/e2e/.cache/
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode configurations
.vscode
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
# End of https://www.toptal.com/developers/gitignore/api/macos,node,git
# Sqlite
.sqlite
# Server data
/apps/Server/data
# Created by https://www.toptal.com/developers/gitignore/api/intellij+all
# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
# Ignore everything but code style settings and run configurations
# that are supposed to be shared within teams.
.idea/*
#!.idea/codeStyles // todo potentially comment back in when vscode and intellij configs are supported
#!.idea/runConfigurations // todo potentially comment back in when vscode and intellij configs are supported
# End of https://www.toptal.com/developers/gitignore/api/intellij+all
# Generated certificate backup files
**/certificates/*.pem.backup

View File

@@ -0,0 +1 @@
engine-strict=true

View File

@@ -0,0 +1 @@
v24.16.0

View File

@@ -0,0 +1,2 @@
**/dist/**
**/node_modules/**

View File

@@ -0,0 +1,9 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": true,
"tabWidth": 2,
"bracketSpacing": true,
"arrowParens": "always"
}

View File

@@ -0,0 +1,8 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: CitrineOS Core
Upstream-Contact: Contributors to the CitrineOS Project
Source: https://github.com/citrineos/citrineos-core
Files: package.json */package.json */*/package.json 00_Base/src/ocpp/persistence/schemas/*.json 01_Data/src/interfaces/projections/schemas/*.json
Copyright: Contributors to the CitrineOS Project
License: Apache-2.0

View File

@@ -0,0 +1,154 @@
.dockerignore
.gitattributes
.gitignore
.npmrc
.nvmrc
.prettierignore
.prettierrc
apps/Server/.sequelizerc
OCPP_201_Logo_core_and_advanced_security.png
README.md
apps/Server/rbac-rules.json
apps/Server/entrypoint.sh
logo_black.png
logo_white.png
renovate.json
pnpm-lock.yaml
pnpm-workspace.yaml
apps/Server/tsconfig.migrations.json
apps/Server/src/assets/logo.png
# CI/CD files
.github/workflows/tests/CI/collection.json
.github/workflows/tests/CI/environment.json
.github/workflows/renew-certs/openssl.config
.husky/pre-commit
# Package files
packages/base/package.json
packages/core/package.json
apps/Server/package.json
package-lock.json
# TypeScript config files
packages/base/tsconfig.eslint.json
packages/base/tsconfig.json
packages/core/tsconfig.eslint.json
packages/core/tsconfig.json
apps/Server/tsconfig.eslint.json
apps/Server/tsconfig.json
tsconfig.build.json
tsconfig.eslint.json
tsconfig.json
# OCPP 1.6 Schema files
packages/base/src/ocpp/model/1.6/schemas/
# OCPP 2.0.1 Schema files
packages/base/src/ocpp/model/2.0.1/schemas/
# OCPP 2.1 Schema files
packages/base/src/ocpp/model/2.1/schemas/
# Persistence schemas
packages/base/src/ocpp/persistence/schemas/AuthorizationDataSchema.json
packages/base/src/ocpp/persistence/schemas/BootConfigSchema.json
packages/base/src/ocpp/persistence/schemas/ChargingStationTypeSchema.json
packages/base/src/ocpp/persistence/schemas/ReportDataTypeSchema.json
packages/base/src/ocpp/persistence/schemas/SetVariableResultTypeSchema.json
packages/base/src/ocpp/persistence/schemas/UpdateChargingStationPasswordRequestSchema.json
# Other schemas
packages/core/src/dal/interfaces/projections/schemas/AuthorizationRestrictionsSchema.json
packages/core/src/dal/interfaces/projections/schemas/TariffSchema.json
# Certificate files
packages/core/src/util/test/resources/ChargingStationCSRSample.pem
packages/core/src/util/test/resources/LeafCertificateSample.pem
packages/core/src/util/test/resources/LeafKeySample.pem
packages/core/src/util/test/resources/RootCertificateSample.pem
packages/core/src/util/test/resources/RootKeySample.pem
packages/core/src/util/test/resources/SubCACertificateSample.pem
packages/core/src/util/test/resources/SubCAKeySample.pem
packages/core/src/util/test/resources/V2GCACertChainSample.pem
packages/core/src/util/test/resources/V2GLeafCertificateSample.pem
apps/Server/src/assets/certificates/acme_account_key.pem
apps/Server/src/assets/certificates/certChain.pem
apps/Server/src/assets/certificates/leafKey.pem
apps/Server/src/assets/certificates/rootCertificate.pem
apps/Server/src/assets/certificates/rootKey.pem
apps/Server/src/assets/certificates/subCAKey.pem
# Documentation
packages/core/src/util/test/resources/README.md
apps/Server/everest/README.md
# Configuration files
apps/Server/nodemon.json
apps/Server/everest/
# Operator UI files
apps/operator-ui/config/countries/all-countries.json
apps/operator-ui/config/countries/country-areas.json
apps/operator-ui/config/countries/country-configs.json
apps/operator-ui/public/Citrine_Favicon_256_clear3.png
apps/operator-ui/public/gradient-dark.svg
apps/operator-ui/public/gradient.svg
apps/operator-ui/public/locales/en/common.json
apps/operator-ui/public/locales/en/authorizations.json
apps/operator-ui/public/locales/en/chargingStations.json
apps/operator-ui/public/locales/en/locations.json
apps/operator-ui/public/locales/en/tariffs.json
apps/operator-ui/public/locales/en/tenantPartners.json
apps/operator-ui/public/locales/en/transactions.json
apps/operator-ui/public/logo-black.svg
apps/operator-ui/public/logo-collapsed.svg
apps/operator-ui/public/logo-white.svg
apps/operator-ui/public/logo_black.png
apps/operator-ui/public/logo_white.png
apps/operator-ui/public/OCPP_201_Logo_core_and_advanced_security.png
apps/operator-ui/public/online.png
apps/operator-ui/src/app/_fonts/Roobert-Bold.woff2
apps/operator-ui/src/app/_fonts/Roobert-Heavy.woff2
apps/operator-ui/src/app/_fonts/Roobert-Light.woff2
apps/operator-ui/src/app/_fonts/Roobert-Medium.woff2
apps/operator-ui/src/app/_fonts/Roobert-Regular.woff2
apps/operator-ui/src/app/_fonts/Roobert-SemiBold.woff2
apps/operator-ui/src/app/not-found.tsx
apps/operator-ui/src/lib/client/components/ui/alert-dialog.tsx
apps/operator-ui/src/lib/client/components/ui/alert.tsx
apps/operator-ui/src/lib/client/components/ui/badge.tsx
apps/operator-ui/src/lib/client/components/ui/button.tsx
apps/operator-ui/src/lib/client/components/ui/calendar.tsx
apps/operator-ui/src/lib/client/components/ui/card.tsx
apps/operator-ui/src/lib/client/components/ui/chart.tsx
apps/operator-ui/src/lib/client/components/ui/checkbox.tsx
apps/operator-ui/src/lib/client/components/ui/command.tsx
apps/operator-ui/src/lib/client/components/ui/confirm.tsx
apps/operator-ui/src/lib/client/components/ui/date-time-picker.tsx
apps/operator-ui/src/lib/client/components/ui/dialog.tsx
apps/operator-ui/src/lib/client/components/ui/dropdown-menu.tsx
apps/operator-ui/src/lib/client/components/ui/error-component.tsx
apps/operator-ui/src/lib/client/components/ui/field.tsx
apps/operator-ui/src/lib/client/components/ui/input.tsx
apps/operator-ui/src/lib/client/components/ui/input-group.tsx
apps/operator-ui/src/lib/client/components/ui/label.tsx
apps/operator-ui/src/lib/client/components/ui/loader.tsx
apps/operator-ui/src/lib/client/components/ui/loading.tsx
apps/operator-ui/src/lib/client/components/ui/popover.tsx
apps/operator-ui/src/lib/client/components/ui/select.tsx
apps/operator-ui/src/lib/client/components/ui/separator.tsx
apps/operator-ui/src/lib/client/components/ui/skeleton.tsx
apps/operator-ui/src/lib/client/components/ui/sonner.tsx
apps/operator-ui/src/lib/client/components/ui/table.tsx
apps/operator-ui/src/lib/client/components/ui/tabs.tsx
apps/operator-ui/src/lib/client/components/ui/textarea.tsx
apps/operator-ui/src/lib/client/components/ui/tooltip.tsx
apps/operator-ui/src/lib/client/components/ui/undoable-notification.tsx
apps/operator-ui/src/lib/utils/cn.ts
apps/operator-ui/src/lib/utils/types.ts
apps/operator-ui/tsconfig.json
apps/operator-ui/.env.local
apps/operator-ui/.env.test

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 S44, LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,73 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

View File

@@ -0,0 +1,268 @@
![CitrineOS Logo](logo_white.png#gh-dark-mode-only)
![CitrineOS Logo](logo_black.png#gh-light-mode-only)
<div align="center">
<img src="OCPP_201_Logo_core_and_advanced_security.png" alt="CitrineOS Certification Logo" width="200" height="100" />
</div>
# 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](#repository-structure) and [Component Documentation](#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](#overview)
- [Architecture Flow](#architecture-flow)
- [Repository Structure](#repository-structure)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Running the Full Stack with Docker](#running-the-full-stack-with-docker)
- [Information on Docker Setup](#information-on-docker-setup)
- [Workspace Scripts](#workspace-scripts)
- [Component Documentation](#component-documentation)
- [Contributing](#contributing)
- [Licensing](#licensing)
- [Support and Contact](#support-and-contact)
- [Roadmap](#roadmap)
## Overview
CitrineOS is developed in TypeScript and runs on `NodeJS` with [ws](https://github.com/websockets/ws)
and [fastify](https://fastify.dev/). The operator UI is built with [Next.js](https://nextjs.org/) and
[Refine](https://refine.dev/).
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 more information on the project go to [citrineos.github.io](https://citrineos.github.io).
## Architecture Flow
Here's a **flowchart-style overview** of CitrineOS architecture and message flow:
```text
┌───────────────────┐ ┌───────────────────┐
│ 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
1. **Charging Stations** send messages using **OCPP 1.6** or **OCPP 2.0.1**.
2. **CitrineOS 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:
```
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`](./apps/Server/README.md)
- **Operator UI** — running and developing the web UI, bringing a station online end-to-end: [`apps/operator-ui/README.MD`](./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](https://nodejs.org/)
- pnpm (the workspace's package manager): [Download pnpm](https://pnpm.io/installation)
- Docker (Optional). Version >= 20.10: [Download Docker](https://docs.docker.com/get-docker/)
## Installation
1. Clone the CitrineOS repository to your local machine:
```shell
git clone https://github.com/citrineos/citrineos-core
```
1. Install all workspace dependencies from the root directory:
```shell
pnpm install
```
1. Build all packages from the root directory:
```shell
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:
```shell
docker compose up -d
```
- **With the server built from local source** — run from the repository root:
```shell
docker compose -f docker-compose.local.yml up -d
```
Once everything is up, the operator UI is available at [http://localhost:3000](http://localhost:3000) and the server's
Swagger docs at [http://localhost:8080/docs](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](./apps/Server/README.md). To develop the UI on its own, see the
[Operator UI README](./apps/operator-ui/README.MD).
## Information on Docker Setup
You need to install
[docker](https://docs.docker.com/engine/install/#server) (>= 20.10) and
[docker-compose](https://docs.docker.com/compose/install/#install-compose).
Furthermore, [Visual Studio Code](https://code.visualstudio.com/docs/setup/linux) might be handy as a common
integrated development environment.
There are three Compose files:
- `docker-compose.yml` (repository root) — full stack from published `ghcr.io` images, 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](./apps/Server/README.md)).
Once a stack is running, the following services should be available:
- **CitrineOS Server** (service name: citrine)
- `8080`: webserver HTTP - [Swagger](http://localhost:8080/docs)
- `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: citrine-ui) — only in the root-level Compose files
- `3000`: [Operator UI](http://localhost:3000)
- **RabbitMQ Broker** (service name: amqp-broker)
- `5672`: AMQP TCP connection
- `15672`: RabbitMQ [management interface](http://localhost:15672)
- **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 endpoint
- `9001`: MinIO [web console](http://localhost:9001)
- **Hasura GraphQL Engine** (service name: graphql-engine)
- `8090`: [Hasura console](http://localhost:8090)
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 packages
- `pnpm 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 all `node_modules`, `dist`, `tsbuildinfo`, and `pnpm-lock.yaml`, then clears the pnpm cache
- `pnpm run clean` - subset of `pnpm run fresh`; only deletes build artifacts (`dist` and `tsbuildinfo`)
- `pnpm run fi` - convenience command that runs `fresh` followed by `pnpm 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 files
- `pnpm run lint` - runs the linter
- `pnpm run lint-fix` - runs Prettier and 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 coverage` - runs the test suite with coverage
## Component Documentation
- [CitrineOS Server (`@citrineos/server`)](./apps/Server/README.md) — 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`)](./apps/operator-ui/README.MD) — running and developing the web UI,
and a step-by-step guide to bringing a charging station online end-to-end.
- [Testing with EVerest](./apps/Server/everest/README.md) — 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](https://github.com/citrineos/citrineos/blob/main/CONTRIBUTING.md).
## 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.
## Roadmap
[Roadmap](https://citrineos.github.io/docs/roadmap.html)

View File

@@ -0,0 +1,6 @@
const path = require('path');
module.exports = {
config: path.resolve('dist', 'config', 'sequelize.bridge.config.js'),
'migrations-path': path.resolve('dist/migrations'),
};

View File

@@ -0,0 +1,295 @@
<!--
SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
SPDX-License-Identifier: Apache-2.0
-->
![CitrineOS Logo](../../logo_white.png#gh-dark-mode-only)
![CitrineOS Logo](../../logo_black.png#gh-light-mode-only)
# CitrineOS Server (`@citrineos/server`)
This is the OCPP server application for CitrineOS — the runnable entrypoint that wires together
[`@citrineos/base`](../../packages/base) and [`@citrineos/core`](../../packages/core) into a deployable
service. It hosts the WebSocket endpoints that charging stations connect to, the OCPP message router, the
HTTP/REST Data and Message APIs, and the Sequelize database migrations.
It is one workspace member of the `citrineos-core` pnpm monorepo. For repository-wide setup (cloning,
`pnpm install`, building, the full-stack Docker Compose files, and the operator UI), see the
[root README](../../README.md).
## Table of Contents
- [Running the Server](#running-the-server)
- [With Docker (backend only)](#with-docker-backend-only)
- [Without Docker](#without-docker)
- [Attaching a Debugger](#attaching-a-debugger)
- [Server Ports](#server-ports)
- [Database Sync vs. Migration](#database-sync-vs-migration)
- [Runtime Configuration](#runtime-configuration)
- [Bootstrap Configuration Environment Variables](#bootstrap-configuration-environment-variables)
- [Generating OCPP Interfaces](#generating-ocpp-interfaces)
- [Validating Custom OCPP DataTransfer Messages](#validating-custom-ocpp-datatransfer-messages)
- [Allow Unknown Charging Stations & Auto-Commissioning](#allow-unknown-charging-stations--auto-commissioning)
- [Hasura Metadata](#hasura-metadata)
- [Testing with EVerest](#testing-with-everest)
## Running the Server
Make sure the workspace has been installed and built first (from the repository root: `pnpm install && pnpm run build`).
### With Docker (backend only)
`apps/Server/docker-compose.yml` brings up the server plus its supporting services — RabbitMQ, PostgreSQL, MinIO,
and Hasura — but **not** the operator UI. The server image is built from local source and the source tree is mounted
as volumes for live reload. Run it from this directory:
```shell
cd apps/Server
docker compose up -d
```
To run the full stack including the operator UI, use one of the root-level Compose files instead — see the
[root README](../../README.md#information-on-docker-setup).
### Without Docker
To start the server directly with pnpm, run from the repository root:
```shell
pnpm run start
```
Or from this directory:
```shell
cd apps/Server
pnpm run start
```
This launches the server via `nodemon` (see `nodemon.json`), which builds the workspace, runs database migrations,
and then starts the process with the Node.js inspector listening on port 9229.
CitrineOS requires configuration to allow your OCPP 1.6 and OCPP 2.0.1 compliant charging stations to connect.
To change the configuration used outside of Docker, adjust the configuration file at
`apps/Server/src/config/envs/local.ts`. Make sure any changes to the local configuration do not make it into your PR.
## Attaching a Debugger
Whether you run the application with Docker or locally with pnpm, you can attach a debugger to port 9229 and set
breakpoints in the TypeScript code directly from your IDE.
To make the process **wait for the debugger to attach** before executing, modify the `nodemon.json` exec command from:
```shell
pnpm run build --prefix ../../ && pnpm run migrate && node --inspect=0.0.0.0:9229 ./dist/index.js
```
to:
```shell
pnpm run build --prefix ../../ && pnpm run migrate && node --inspect-brk=0.0.0.0:9229 ./dist/index.js
```
## Server Ports
When running, the server container exposes the following ports (see `docker-compose.yml`):
- `8080`: webserver HTTP — [Swagger](http://localhost:8080/docs)
- `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
## Database Sync vs. Migration
By default, CitrineOS uses migrations to manage database schema changes. This is the recommended approach for
production environments. The `pnpm run migrate` script (run automatically on start via `nodemon.json`) applies
Sequelize migrations.
For development purposes, you can also use `sync` to automatically synchronize your database schema with the models.
Two sync scripts are available at the repository root:
- `pnpm run sync-db`: synchronizes the database schema with the models without altering existing tables. Useful for
development when you want to quickly update the schema without losing data.
- `pnpm run force-sync-db`: drops all tables and recreates them based on the models. Useful when you want to start
with a fresh database.
**Disclaimer:** Using `sync` in a production environment is not recommended as it can lead to data loss. Always use
migrations for production deployments.
## Runtime Configuration
Values from configuration files (`local.ts`, `docker.ts`, `swarm.docker.ts`) may be overridden at runtime via
environment variables. Environment variables prefixed with `citrineos_` and hierarchically separated by an
underscore will override the corresponding value. For example, the amqp URL:
```json
util: {
(...)
messageBroker: {
amqp: {
url: 'amqp://guest:guest@localhost:5672'
(...)
}
(...)
}
(...)
}
```
may be overridden by setting the environment variable `CITRINEOS_util_messageBroker_amqp_url` (case-insensitive).
## Bootstrap Configuration Environment Variables
All environment variables use the `CITRINEOS_` prefix.
Additional prefixes can be added by passing the `--env-prefix` argument to nodemon (see `start:instance1` in
`package.json`).
Here's the complete list of environment variables used in bootstrapping the application (this is not the full system
configuration):
### Basic Bootstrap Configuration
- `BOOTSTRAP_CITRINEOS_CONFIG_FILENAME` - Name of the main config file (default: `config.json`)
- `BOOTSTRAP_CITRINEOS_CONFIG_DIR` - Directory containing the config file (optional)
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE` - Type of file access: `local`, `s3`, or `gcp`
### Database Configuration
Database connection details (moved from system config to bootstrap config for better security and 12-factor compliance):
- `BOOTSTRAP_CITRINEOS_DATABASE_HOST` - Database host (default: `localhost`)
- `BOOTSTRAP_CITRINEOS_DATABASE_PORT` - Database port (default: `5432`)
- `BOOTSTRAP_CITRINEOS_DATABASE_NAME` - Database name (default: `citrine`)
- `BOOTSTRAP_CITRINEOS_DATABASE_DIALECT` - Database dialect (default: `postgres`)
- `BOOTSTRAP_CITRINEOS_DATABASE_USERNAME` - Database username (optional)
- `BOOTSTRAP_CITRINEOS_DATABASE_PASSWORD` - Database password (optional)
- `BOOTSTRAP_CITRINEOS_DATABASE_SYNC` - Enable database sync (via sequelize) (true/false, default: `false`)
- `BOOTSTRAP_CITRINEOS_DATABASE_ALTER` - Enable database alter (via sequelize) (true/false, default: `false`)
- `BOOTSTRAP_CITRINEOS_DATABASE_MAX_RETRIES` - Maximum connection retries (default: `3`)
- `BOOTSTRAP_CITRINEOS_DATABASE_RETRY_DELAY` - Retry delay in milliseconds (default: `1000`)
### Local File Access
When `BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE=local`:
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_LOCAL_DEFAULT_FILE_PATH` - Default file path (default: `/data`)
### S3 File Access
When `BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE=s3`:
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_REGION` - AWS region (optional)
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_ENDPOINT` - S3 endpoint URL (for MinIO or custom S3)
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_DEFAULT_BUCKET_NAME` - S3 bucket name (default: `citrineos-s3-bucket`)
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_FORCE_PATH_STYLE` - Force path style (true/false, default: `true`)
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_ACCESS_KEY_ID` - S3 access key ID
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_SECRET_ACCESS_KEY` - S3 secret access key
### GCP File Access
When `BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE=gcp`:
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_GCP_PROJECTID` - Project ID
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_GCP_CREDENTIALS` - GCP Credentials object (Optional, if not set will use Application Default Credentials such as the `GOOGLE_APPLICATION_CREDENTIALS` environment variable or gcloud CLI credentials)
## Generating OCPP Interfaces
All CitrineOS interfaces for OCPP 1.6, 2.0.1, and 2.1-defined schemas were procedurally generated using a processing
script. Schemas are sourced from official OCPP JSON files.
As of release 1.8.0, the schema files used by CitrineOS are not the raw output of this function; we have added
field-level validation that the official schemas lack.
## Validating Custom OCPP DataTransfer Messages
It is possible to add custom JSON schemas to validate the data fields of DataTransfer messages, which are supported by
all OCPP versions.
In the `apps/Server/src/index.ts` code, there is a function `ajvInstance()` that creates the AJV instance. Here, you
could register DataTransfer schemas:
```
import { MyDataTransferRequestSchema } from './path'
...
ajvInstance.compile(MyDataTransferRequestSchema);
```
Note: The schema's `$id` field must follow this format:
```
${protocol}-${dataTransferRequest.vendorId}${dataTransferRequest.messageId ? `-${dataTransferRequest.messageId}` : ''}
```
'Protocol' is the OCPP websocket subprotocol, i.e. "ocpp1.6", "ocpp2.0.1", or so on.
CitrineOS's validation logic assumes that the data field is a string field with JSON structure, and uses `JSON.parse`
before validation. Other approaches to custom DataTransfer message types are not supported.
## Allow Unknown Charging Stations & Auto-Commissioning
The System Configuration defines websocket servers with certain properties, one of which is 'Allow Unknown Charging
Stations', a boolean that permits charging stations which are not commissioned to connect to CitrineOS.
This triggers an auto-commissioning flow which creates the station on its first connection, and creates evses and
connectors for that station in response to StatusNotifications.
This is not recommended for production; it is exclusively for testing and is enabled by the default configuration only
on the websocket server at port 8081 — which also has no security.
Since not all information on the charger is necessarily available in the OCPP messages, commissioning may be wrong and
will be incomplete. In 1.6 in particular, multi-evse stations will not commission properly because 1.6 does not have a
concept of 'evses'. This will lead to improper behavior if a 1.6 station with multiple evses is auto-commissioned:
CitrineOS will assume each new transaction is on the same evse and will automatically mark older transactions on that
evse as inactive, leading to an inconsistent state with the charging station.
## Hasura Metadata
In order for Hasura to track the existing Citrine tables and relationships, this repository comes with Hasura metadata
already exported into the `apps/Server/hasura-metadata` folder.
Running the Docker container will automatically import this metadata and track all tables and relationships.
Unfortunately, Hasura doesn't currently support importing metadata from a JSON (which is the format if you export your
metadata from the Hasura UI or API).
Refer to this issue for more information: https://github.com/hasura/graphql-engine/issues/8423#issuecomment-1115996153.
Therefore, you must use the Hasura CLI to re-export your metadata, should something change with it. As explained in the
Hasura docs https://hasura.io/docs/2.0/migrations-metadata-seeds/auto-apply-migrations/#auto-apply-metadata,
Hasura provides an image called `hasura/graphql-engine:<version>.cli-migrations-v3` that will process and import the
metadata first before starting the server and runs the Hasura CLI internally. This is the image CitrineOS normally uses
in order to automatically load accurate metadata. However, if you want to capture the current state of your database,
you should use a normal version tag (such as `v2.40.3` instead of `v2.40.3.cli-migrations-v3`). Then proceed to the
Hasura console at `localhost:8090`, go to the data tab, use the sidebar to navigate to the database schema at
default>public, and track all of the tables, relationships, and functions you need. Then proceed with the below
instructions.
You can follow these steps to re-export your metadata via the Hasura CLI in the `graphql-engine` container:
- (if the hasura cli isn't installed):
```
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
```
- (If not yet initialized) Initialize the Hasura project in the `graphql-engine` container (you can do this via the Docker Desktop `exec` view):
```
hasura-cli init
OR
hasura init
enter any name you wish for the project (i.e. citrine)
```
- Export the metadata by executing this command in the `graphql-engine` container:
```
hasura-cli metadata export
OR
hasura metadata export
```
- Find the exported files in the `graphql-engine` container's files in the metadata filepath `<name of project i.e. citrine>/metadata` and pull that metadata backup onto your local machine
- Copy the contents of the copied `metadata` folder into the `apps/Server/hasura-metadata` folder in this repository
## Testing with EVerest
In case you don't have a charger that supports OCPP to experiment with, you can run the EVerest charger simulator
locally and point it at CitrineOS. Helper scripts (`pnpm run start-everest` and `pnpm run start-everest-16`) and full
instructions live in [`everest/README.md`](./everest/README.md).

View File

@@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
//
// SPDX-License-Identifier: Apache-2.0
import * as path from 'path';
import { promises as fs } from 'fs';
const source = process.argv[2];
const target = process.argv[3];
console.log(`Copying assets from ${source} to ${target}`);
if (!source || !target) {
console.error(
`Error: need valid source and target. Received source: ${source}, target: ${target}`,
);
process.exit(1);
}
async function copyDirectory(src: string, dest: string) {
try {
await fs.mkdir(dest, { recursive: true });
console.log(`Created "${dest}" directory`);
const entries = await fs.readdir(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
await copyDirectory(srcPath, destPath);
} else {
await fs.copyFile(srcPath, destPath);
console.log(`Copied ${srcPath} to ${destPath}`);
}
}
} catch (err) {
console.error('Error copying directory:', err);
process.exit(1);
}
}
async function copy(s: string, t: string) {
const srcDir = path.resolve(s);
const destDir = path.resolve(t);
await copyDirectory(srcDir, destDir);
console.log(`Successfully copied assets from ${srcDir} to ${destDir}`);
}
copy(source, target)
.then(() => {
console.log(`Completed copying assets from ${source} to ${target}`);
process.exit(0);
})
.catch((err) => {
console.error('Unhandled error:', err);
process.exit(1);
});

View File

@@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
# Use a specific base image with platform support
FROM --platform=${BUILDPLATFORM:-linux/amd64} node:24.16.0 AS build
RUN corepack enable
WORKDIR /usr/local/apps/citrineos
COPY . .
RUN pnpm install --frozen-lockfile
RUN pnpm --filter "@citrineos/server..." build
# The final stage, which copies built files and prepares the run environment
# Using a slim image to reduce the final image size
FROM node:24.16.0-slim
RUN corepack enable
COPY --from=build /usr/local/apps/citrineos /usr/local/apps/citrineos
WORKDIR /usr/local/apps/citrineos
RUN chmod +x /usr/local/apps/citrineos/apps/Server/entrypoint.sh
EXPOSE ${PORT}
ENTRYPOINT ["/usr/local/apps/citrineos/apps/Server/entrypoint.sh"]

View File

@@ -0,0 +1,181 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
services:
amqp-broker:
image: rabbitmq:3-management
ports:
- 15672:15672
- 5672:5672
environment:
RABBITMQ_DEFAULT_USER: "guest"
RABBITMQ_DEFAULT_PASS: "guest"
volumes:
- ./data/rabbitmq:/var/lib/rabbitmq
healthcheck:
test: rabbitmq-diagnostics -q check_port_connectivity
interval: 10s
timeout: 10s
retries: 3
ocpp-db:
image: postgis/postgis:16-3.5
platform: linux/amd64
ports:
- 5432:5432
volumes:
- ./data/postgresql/pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: citrine
POSTGRES_USER: citrine
POSTGRES_PASSWORD: "citrine"
healthcheck:
test: "pg_isready --username=citrine"
interval: 5s
timeout: 10s
retries: 5
citrine:
image: citrineos-core-citrine:latest
build:
context: ../../
dockerfile: ./apps/Server/deploy.Dockerfile
volumes:
- ../../pnpm-lock.yaml:/usr/local/apps/citrineos/pnpm-lock.yaml
- ../../package.json:/usr/local/apps/citrineos/package.json
- ../../tsconfig.json:/usr/local/apps/citrineos/tsconfig.json
- ../../tsconfig.build.json:/usr/local/apps/citrineos/tsconfig.build.json
- ./:/usr/local/apps/citrineos/apps/Server
- ../../packages/base:/usr/local/apps/citrineos/packages/base
- ../../packages/core:/usr/local/apps/citrineos/packages/core
- /usr/local/apps/citrineos/node_modules
- /usr/local/apps/citrineos/apps/Server/node_modules
- /usr/local/apps/citrineos/packages/base/node_modules
- /usr/local/apps/citrineos/packages/core/node_modules
- /usr/local/apps/citrineos/dist/
- /usr/local/apps/citrineos/apps/Server/dist/
- /usr/local/apps/citrineos/packages/base/dist/
- /usr/local/apps/citrineos/packages/core/dist/
environment:
APP_NAME: "all"
APP_ENV: "docker"
# Add AWS region (required by the SDK)
AWS_REGION: us-east-1
# Skip authentication completely - use public access
AWS_ACCESS_KEY_ID: minioadmin
AWS_SECRET_ACCESS_KEY: minioadmin
# Database Strategy
DB_STRATEGY: "migrate"
# Bootstrap Configuration Environment Variables
BOOTSTRAP_CITRINEOS_DATABASE_HOST: "ocpp-db"
BOOTSTRAP_CITRINEOS_CONFIG_FILENAME: "config.json"
# CITRINEOS_CONFIG_DIR: '/custom/config/path' # Optional - uncomment if needed
BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE: "local"
BOOTSTRAP_CITRINEOS_FILE_ACCESS_LOCAL_DEFAULT_FILE_PATH: "/data"
# No System Config file persistence respected, to avoid local conflicts across version updates - recommended to remove for production
CONFIG_CITRINEOS_WIPE_FILE_ON_START: "true"
depends_on:
ocpp-db:
condition: service_healthy
amqp-broker:
condition: service_healthy
minio-init:
condition: service_completed_successfully
ports:
- 8080:8080
- 8081:8081
- 8082:8082
- 8083:8083
- 8443:8443
- 8444:8444
- 9229:9229
healthcheck:
test: [
"CMD-SHELL",
"node -e \"const net = require(\\\"net\\\"); const client =
net.createConnection(8080, \\\"127.0.0.1\\\", () => { client.end();
process.exit(0); }); client.on(\\\"error\\\", () =>
process.exit(1)); client.setTimeout(5000, () => { client.destroy();
process.exit(1); });\"",
]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
minio:
image: minio/minio
container_name: minio
ports:
- "9000:9000"
- "9001:9001"
environment:
# Use default credentials for simplicity
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
# Enable public buckets
MINIO_BROWSER_REDIRECT_URL: http://localhost:9001
MINIO_SERVER_URL: http://localhost:9000
volumes:
- ./data/minio:/data
command: server /data --console-address ":9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 5
start_period: 20s
# Initialize MinIO
minio-init:
image: minio/mc
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c " # Enable command echoing and error reporting set -x
echo 'Setting up MinIO alias' mc alias set myminio http://minio:9000
minioadmin minioadmin || echo 'FAILED: alias setup' "
graphql-engine:
image: hasura/graphql-engine:v2.40.3.cli-migrations-v3
volumes:
- ./hasura-metadata:/hasura-metadata
ports:
- 8090:8080
restart: always
depends_on:
citrine:
# ensures hasura migrations will run only after citrine is healthy,
# meaning 01_Data/dist/layers/sequelize/util.js was executed and logged
# CitrineOS Logger:DefaultSequelizeInstance Database altered,
# which means the database is ready to be used by hasura
condition: service_healthy
environment:
## postgres database to store Hasura metadata
HASURA_GRAPHQL_DATABASE_URL: postgres://citrine:citrine@ocpp-db:5432/citrine
## enable the console served by server
HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
## enable debugging mode. It is recommended to disable this in production
HASURA_GRAPHQL_DEV_MODE: "true"
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
HASURA_GRAPHQL_ENABLE_TELEMETRY: "false"
## uncomment next line to run console offline (i.e load console assets from server instead of CDN)
# HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets
## uncomment next line to set an admin secret - warning, without configuration this will cause upstream services to fail
# HASURA_GRAPHQL_ADMIN_SECRET: CitrineOS!
HASURA_GRAPHQL_METADATA_DEFAULTS:
"{\"backend_configs\":{\"dataconnector\":{\"a\
thena\":{\"uri\":\"http://data-connector-agent:8081/api/v1/athena\"},\"\
mariadb\":{\"uri\":\"http://data-connector-agent:8081/api/v1/mariadb\"},\
\"mysql8\":{\"uri\":\"http://data-connector-agent:8081/api/v1/mysql\"},\
\"oracle\":{\"uri\":\"http://data-connector-agent:8081/api/v1/oracle\"},\
\"snowflake\":{\"uri\":\"http://data-connector-agent:8081/api/v1/snowfl\
ake\"}}}}"
healthcheck:
test: ["CMD", "curl", "-f", "http://graphql-engine:8080/healthz"]
interval: 5s
timeout: 10s
retries: 20
start_period: 5s

View File

@@ -0,0 +1,20 @@
#!/bin/sh
set -e
# Default to migrate if DB_STRATEGY is not set
DB_STRATEGY=${DB_STRATEGY:-migrate}
echo "Executing DB strategy: $DB_STRATEGY"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ "$DB_STRATEGY" = "migrate" ]; then
(cd "$SCRIPT_DIR" && pnpm run migrate)
else
echo "Unknown DB_STRATEGY: $DB_STRATEGY. Defaulting to migrate."
(cd "$SCRIPT_DIR" && pnpm run migrate)
fi
echo "Starting application..."
exec node "$SCRIPT_DIR/dist/index.js"

View File

@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
//
// SPDX-License-Identifier: Apache-2.0
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import tseslint from 'typescript-eslint';
import { sharedConfigs, sharedIgnores } from '../../eslint.config.base.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
export default tseslint.config(
...sharedConfigs,
{
languageOptions: {
parserOptions: {
project: ['./tsconfig.eslint.json'],
tsconfigRootDir: __dirname,
},
},
rules: {
'@typescript-eslint/no-floating-promises': 'error',
},
},
sharedIgnores,
);

View File

@@ -0,0 +1,11 @@
ARG EVEREST_IMAGE_TAG="2025.6.1-dt-esdp"
FROM ghcr.io/everest/everest-demo/manager:${EVEREST_IMAGE_TAG}
WORKDIR /workspace
RUN npm i -g http-server
EXPOSE 8888
COPY ./start.sh /tmp/start.sh
RUN chmod +x /tmp/start.sh
ENTRYPOINT ["/tmp/start.sh"]

View File

@@ -0,0 +1,62 @@
# Testing with EVerest
In the case you don't have a charger that supports OCPP 2.0.1 to experiment with, we can recommend using the Linux
Foundation Energy project EVerest. [See here](https://github.com/EVerest) for the repository. They have built an open source version of
charger firmware and also allow for using it as a simulator. They support OCPP 2.0.1 which makes it a great testing
opportunity with CitrineOS. For the long route of setting up EVerst you can follow their documentation and build
the project yourself. [See here for Docs](https://everest.github.io/latest/how-to-guides/getting-started/index.html)
# Running EVerest
In order to alleviate some of the complexities that may arise when starting EVerest, we have created
some helpful commands that should help in getting the EVerest charger simulator running locally and targeting
CitrineOS.
You will notice in the `apps/Server/everest` directory the files created to support running EVerest within Docker.
In addition, we created some helpful package scripts (run from the `apps/Server` directory):
- `pnpm run start-everest` — starts EVerest for OCPP 2.x (defaults to `OCPP_VERSION=2.1`)
- `pnpm run start-everest-16` — starts EVerest for OCPP 1.6
Both scripts `cd` into `everest` and trigger the `docker compose up` command (below) from within the
`apps/Server/everest` directory so that it can pick up the `Dockerfile` and the `docker-compose.yml` files.
You will notice that there are two args that are configurable:
- `EVEREST_IMAGE_TAG` - The image tag that will be used for the EVerest image (ghcr.io/everest/everest-demo/manager).
- `OCPP_VERSION` - The version of OCPP to run EVerest for. `start-everest` handles all 2.x versions for the given 2.x `OCPP_VERSION`, and `start-everest-16` is for OCPP 1.6 only.
After running `pnpm run start-everest` (or `pnpm run start-everest-16`), you should see 3 running EVerest containers
and the `manager` container should have the appropriate EVerest logs.
### EVerest UI
Now that the 3 containers are running in Docker, you should be able to navigate to `[localhost|ip]:1880/ui/` to view
the EVerest simulator UI. There, you should be able to simulate the pause/resume and plug/unplug events among others.
### EVerest NodeRed
You can also view the EVerest NodeRed UI `[localhost|ip]:1880/`, but it is not advisable to make any adjustments here
unless you have a good understanding of this configuration.
### Viewing OCPP logs in EVerest
To view the OCPP logs in EVerest, we have utilized Node `http-server`, which you will see being initialized
in the Dockerfile. We initialize a simple HTTP server on port `8888` and expose this port so that it is
mapped in the compose file allowing you to navigate to `localhost:8888`. This HTTP server is configured to
serve the contents of the `/tmp/everest_ocpp_logs` which is where EVerest stores the OCPP logs in the
Docker container. Conveniently, the logs are in HTML format, so we can easily view them in the browser.
# Running EVerest Manually
You can also use their demo repository that hosts a Docker packaged EVerest image. [See here for Github Repo](https://github.com/EVerest/everest-demo)
To get EVerest running on the side while developing and making changes, you can follow the steps below.
1. Run your CitrineOS instance locally with `docker compose up -d` in the CitrineOS repository.
1. Clone the [EVerest Demo](https://github.com/EVerest/everest-demo) repository and `cd` into the repo.
1. With CitrineOS running execute an "add charger" script at `./citrineos/add-charger.sh` This adds a charger, location and password for the charger to CitrineOS.
1. Bring up EVerest with `docker compose --project-name everest-ac-demo --file "docker-compose.ocpp201.yml" up -d`.
1. Copy over the appropriate device model with `docker cp manager/device_model_storage_citrineos_sp1.db \
everest-ac-demo-manager-1:/ext/source/build/dist/share/everest/modules/OCPP201/device_model_storage.db`.
1. Start EVerst having OCPP2.0.1 support with `docker exec everest-ac-demo-manager-1 sh /ext/source/build/run-scripts/run-sil-ocpp201.sh`.

View File

@@ -0,0 +1,56 @@
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
version: '3.6'
services:
mqtt-server:
image: ghcr.io/everest/everest-demo/mqtt-server:${EVEREST_IMAGE_TAG}
platform: linux/x86_64
logging:
driver: none
networks:
- everest_net
manager:
build:
dockerfile: Dockerfile
args:
- EVEREST_IMAGE_TAG=${EVEREST_IMAGE_TAG}
platform: linux/x86_64
ports:
- 8888:8888
deploy:
resources:
limits:
cpus: '2'
memory: '4G'
depends_on:
- mqtt-server
environment:
- MQTT_SERVER_ADDRESS=mqtt-server
- EVEREST_IMAGE_TAG=${EVEREST_IMAGE_TAG}
- OCPP_VERSION=${OCPP_VERSION}
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
extra_hosts:
- 'host.docker.internal:host-gateway'
networks:
- everest_net
nodered:
image: ghcr.io/everest/everest-demo/nodered:${EVEREST_IMAGE_TAG}
depends_on:
- mqtt-server
ports:
- 1880:1880
environment:
- MQTT_SERVER_ADDRESS=mqtt-server
- FLOWS=/config/config-sil-two-evse-flow.json
networks:
- everest_net
networks:
everest_net:
driver: bridge
enable_ipv6: true

View File

@@ -0,0 +1,75 @@
#!/bin/sh
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
_OCPP_VERSION=$OCPP_VERSION
OCPP_VERSION_ENUM="OCPP201"
EVEREST_TARGET_URL="ws://host.docker.internal:8081/cp001"
case "$_OCPP_VERSION" in
"1.6")
OCPP_VERSION_ENUM="OCPP16"
;;
"2.0.1")
OCPP_VERSION_ENUM="OCPP201"
;;
"2.1")
OCPP_VERSION_ENUM="OCPP21"
;;
*)
# NOT in the list
_OCPP_VERSION="2.0.1"
OCPP_VERSION_ENUM="OCPP201"
;;
esac
echo $OCPP_VERSION_ENUM
if [ "$_OCPP_VERSION" != "1.6" ]; then
#There are two different configs in Everest that default their settings that we need to override for Citrine.
#The first is a temp file that may or may not be used by Everest, but to be safe we change the config there.
#The second is the config used by Everest when setting up their image, and this is the primary driver for the configuration of the image.
CONFIG="$(cat <<JSON
[{"configurationSlot": 1, "connectionData": {"messageTimeout": 30, "ocppCsmsUrl": "$EVEREST_TARGET_URL", "ocppInterface": "Wired0", "ocppTransport": "JSON", "ocppVersion": "$OCPP_VERSION_ENUM", "securityProfile": 1}}, {"configurationSlot": 2, "connectionData": {"messageTimeout": 30, "ocppCsmsUrl": "$EVEREST_TARGET_URL", "ocppInterface": "Wired0", "ocppTransport": "JSON", "ocppVersion": "$OCPP_VERSION_ENUM", "securityProfile": 2}}]
JSON
)"
chmod +x /tmp/config.json
jq --argjson config "$CONFIG" '
(.[]
| select(.name == "InternalCtrlr")
| .variables.NetworkConnectionProfiles.attributes.Actual
) = $config
' "/tmp/config.json" > /tmp/config_citrine.json && mv /tmp/config_citrine.json "/tmp/config.json"
chmod -x /tmp/config.json
chmod +x /ext/dist/share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json
jq --argjson config "$CONFIG" '
(.
| .properties
| .NetworkConnectionProfiles
| .attributes[]
| select(.type == "Actual")
| .value
) = $config
' "/ext/dist/share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json" \
> /tmp/config_citrine_dist.json && mv /tmp/config_citrine_dist.json "/ext/dist/share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json"
chmod -x /ext/dist/share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json
fi
/entrypoint.sh
http-server /tmp/everest_ocpp_logs -p 8888 &
if [ "$_OCPP_VERSION" = "1.6" ]; then
chmod +x /ext/build/run-scripts/run-sil-ocpp.sh
sed -i "0,/127.0.0.1:8180\/steve\/websocket\/CentralSystemService\// s|127.0.0.1:8180/steve/websocket/CentralSystemService/|${EVEREST_TARGET_URL}|" /ext/dist/share/everest/modules/OCPP/config-docker.json
/ext/build/run-scripts/run-sil-ocpp.sh
else
#Works for all 2.x versions
rm /ext/dist/share/everest/modules/OCPP201/component_config/custom/EVSE_2.json
rm /ext/dist/share/everest/modules/OCPP201/component_config/custom/Connector_2_1.json
chmod +x /ext/build/run-scripts/run-sil-ocpp201-pnc.sh
/ext/build/run-scripts/run-sil-ocpp201-pnc.sh
fi

View File

@@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
actions: []
custom_types:
enums: []
input_objects: []
objects: []
scalars: []

View File

@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
[]

View File

@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
{}

View File

@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
{}

View File

@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
[]

View File

@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
- name: default
kind: postgres
configuration:
connection_info:
database_url:
from_env: HASURA_GRAPHQL_DATABASE_URL
isolation_level: read-committed
pool_settings:
connection_lifetime: 600
idle_timeout: 180
max_connections: 50
retries: 1
use_prepared_statements: true
tables: "!include default/tables/tables.yaml"

View File

@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: AsyncJobStatuses
schema: public
object_relationships:
- name: TenantPartner
using:
foreign_key_constraint_on: tenantPartnerId

View File

@@ -0,0 +1,83 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Authorizations
schema: public
object_relationships:
- name: GroupAuthorization
using:
foreign_key_constraint_on: groupAuthorizationId
- name: Tariff
using:
foreign_key_constraint_on: tariffId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: TenantPartner
using:
foreign_key_constraint_on: tenantPartnerId
array_relationships:
- name: Authorizations
using:
foreign_key_constraint_on:
column: groupAuthorizationId
table:
name: Authorizations
schema: public
- name: LocalListAuthorizations
using:
foreign_key_constraint_on:
column: authorizationId
table:
name: LocalListAuthorizations
schema: public
- name: Transactions
using:
foreign_key_constraint_on:
column: authorizationId
table:
name: Transactions
schema: public
- name: localListAuthorizationsByGroupauthorizationid
using:
foreign_key_constraint_on:
column: groupAuthorizationId
table:
name: LocalListAuthorizations
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,53 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Boots
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: VariableAttributes
using:
foreign_key_constraint_on:
column: bootConfigId
table:
name: VariableAttributes
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,70 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Certificates
schema: public
object_relationships:
- name: Certificate
using:
foreign_key_constraint_on: signedBy
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: Certificates
using:
foreign_key_constraint_on:
column: signedBy
table:
name: Certificates
schema: public
- name: InstallCertificateAttempts
using:
foreign_key_constraint_on:
column: certificateId
table:
name: InstallCertificateAttempts
schema: public
- name: InstalledCertificates
using:
foreign_key_constraint_on:
column: certificateId
table:
name: InstalledCertificates
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,45 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ChangeConfigurations
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ChargingNeeds
schema: public
object_relationships:
- name: Evse
using:
foreign_key_constraint_on: evseId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Transaction
using:
foreign_key_constraint_on: transactionDatabaseId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,56 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ChargingProfiles
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Transaction
using:
foreign_key_constraint_on: transactionDatabaseId
array_relationships:
- name: ChargingSchedules
using:
foreign_key_constraint_on:
column: chargingProfileDatabaseId
table:
name: ChargingSchedules
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,56 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ChargingSchedules
schema: public
object_relationships:
- name: ChargingProfile
using:
foreign_key_constraint_on: chargingProfileDatabaseId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: SalesTariffs
using:
foreign_key_constraint_on:
column: chargingScheduleDatabaseId
table:
name: SalesTariffs
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ChargingStationNetworkProfiles
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: ServerNetworkProfile
using:
foreign_key_constraint_on: websocketServerConfigId
- name: SetNetworkProfile
using:
foreign_key_constraint_on: setNetworkProfileId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ChargingStationSecurityInfos
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ChargingStationSequences
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,161 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ChargingStations
schema: public
object_relationships:
- name: Location
using:
foreign_key_constraint_on: locationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: ChargingStationNetworkProfiles
using:
foreign_key_constraint_on:
column: stationId
table:
name: ChargingStationNetworkProfiles
schema: public
- name: ChargingStationSecurityInfos
using:
foreign_key_constraint_on:
column: stationId
table:
name: ChargingStationSecurityInfos
schema: public
- name: ChargingStationSequences
using:
foreign_key_constraint_on:
column: stationId
table:
name: ChargingStationSequences
schema: public
- name: Connectors
using:
foreign_key_constraint_on:
column: stationId
table:
name: Connectors
schema: public
- name: DeleteCertificateAttempts
using:
foreign_key_constraint_on:
column: stationId
table:
name: DeleteCertificateAttempts
schema: public
- name: EventData
using:
foreign_key_constraint_on:
column: stationId
table:
name: EventData
schema: public
- name: Evses
using:
foreign_key_constraint_on:
column: stationId
table:
name: Evses
schema: public
- name: InstallCertificateAttempts
using:
foreign_key_constraint_on:
column: stationId
table:
name: InstallCertificateAttempts
schema: public
- name: InstalledCertificates
using:
foreign_key_constraint_on:
column: stationId
table:
name: InstalledCertificates
schema: public
- name: LatestStatusNotifications
using:
foreign_key_constraint_on:
column: stationId
table:
name: LatestStatusNotifications
schema: public
- name: OCPPMessages
using:
foreign_key_constraint_on:
column: stationId
table:
name: OCPPMessages
schema: public
- name: SetNetworkProfiles
using:
foreign_key_constraint_on:
column: stationId
table:
name: SetNetworkProfiles
schema: public
- name: StatusNotifications
using:
foreign_key_constraint_on:
column: stationId
table:
name: StatusNotifications
schema: public
- name: Transactions
using:
foreign_key_constraint_on:
column: stationId
table:
name: Transactions
schema: public
- name: VariableAttributes
using:
foreign_key_constraint_on:
column: stationId
table:
name: VariableAttributes
schema: public
- name: VariableMonitorings
using:
foreign_key_constraint_on:
column: stationId
table:
name: VariableMonitorings
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,51 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ComponentVariables
schema: public
object_relationships:
- name: Component
using:
foreign_key_constraint_on: componentId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Variable
using:
foreign_key_constraint_on: variableId
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,84 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Components
schema: public
object_relationships:
- name: EvseType
using:
foreign_key_constraint_on: evseDatabaseId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: ComponentVariables
using:
foreign_key_constraint_on:
column: componentId
table:
name: ComponentVariables
schema: public
- name: EventData
using:
foreign_key_constraint_on:
column: componentId
table:
name: EventData
schema: public
- name: MessageInfos
using:
foreign_key_constraint_on:
column: displayComponentId
table:
name: MessageInfos
schema: public
- name: VariableAttributes
using:
foreign_key_constraint_on:
column: componentId
table:
name: VariableAttributes
schema: public
- name: VariableMonitorings
using:
foreign_key_constraint_on:
column: componentId
table:
name: VariableMonitorings
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,45 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: CompositeSchedules
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,69 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Connectors
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Evse
using:
foreign_key_constraint_on: evseId
- name: Tariff
using:
foreign_key_constraint_on: tariffId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: StartTransactions
using:
foreign_key_constraint_on:
column: connectorDatabaseId
table:
name: StartTransactions
schema: public
- name: Transactions
using:
foreign_key_constraint_on:
column: connectorId
table:
name: Transactions
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: DeleteCertificateAttempts
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId

View File

@@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: EventData
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Component
using:
foreign_key_constraint_on: componentId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Variable
using:
foreign_key_constraint_on: variableId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,48 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: EvseTypes
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: Components
using:
foreign_key_constraint_on:
column: evseDatabaseId
table:
name: Components
schema: public
- name: Reservations
using:
foreign_key_constraint_on:
column: evseId
table:
name: Reservations
schema: public
- name: TransactionEvents
using:
foreign_key_constraint_on:
column: evseId
table:
name: TransactionEvents
schema: public
- name: VariableAttributes
using:
foreign_key_constraint_on:
column: evseDatabaseId
table:
name: VariableAttributes
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,70 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Evses
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: ChargingNeeds
using:
foreign_key_constraint_on:
column: evseId
table:
name: ChargingNeeds
schema: public
- name: Connectors
using:
foreign_key_constraint_on:
column: evseId
table:
name: Connectors
schema: public
- name: Transactions
using:
foreign_key_constraint_on:
column: evseId
table:
name: Transactions
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: InstallCertificateAttempts
schema: public
object_relationships:
- name: Certificate
using:
foreign_key_constraint_on: certificateId
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId

View File

@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: InstalledCertificates
schema: public
object_relationships:
- name: Certificate
using:
foreign_key_constraint_on: certificateId
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: LatestStatusNotifications
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: StatusNotification
using:
foreign_key_constraint_on: statusNotificationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,40 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: LocalListAuthorizations
schema: public
object_relationships:
- name: Authorization
using:
foreign_key_constraint_on: authorizationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: authorizationByGroupauthorizationid
using:
foreign_key_constraint_on: groupAuthorizationId
array_relationships:
- name: LocalListVersionAuthorizations
using:
foreign_key_constraint_on:
column: authorizationId
table:
name: LocalListVersionAuthorizations
schema: public
- name: SendLocalListAuthorizations
using:
foreign_key_constraint_on:
column: authorizationId
table:
name: SendLocalListAuthorizations
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: LocalListVersionAuthorizations
schema: public
object_relationships:
- name: LocalListAuthorization
using:
foreign_key_constraint_on: authorizationId
- name: LocalListVersion
using:
foreign_key_constraint_on: localListVersionId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: LocalListVersions
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: LocalListVersionAuthorizations
using:
foreign_key_constraint_on:
column: localListVersionId
table:
name: LocalListVersionAuthorizations
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,60 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Locations
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: ChargingStations
using:
foreign_key_constraint_on:
column: locationId
table:
name: ChargingStations
schema: public
- name: Transactions
using:
foreign_key_constraint_on:
column: locationId
table:
name: Transactions
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: MessageInfos
schema: public
object_relationships:
- name: Component
using:
foreign_key_constraint_on: displayComponentId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: MeterValues
schema: public
object_relationships:
- name: StopTransaction
using:
foreign_key_constraint_on: stopTransactionDatabaseId
- name: Tariff
using:
foreign_key_constraint_on: tariffId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Transaction
using:
foreign_key_constraint_on: transactionDatabaseId
- name: TransactionEvent
using:
foreign_key_constraint_on: transactionEventId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: OCPPMessages
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: OCPPMessage
using:
foreign_key_constraint_on: requestMessageId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: OCPPMessages
using:
foreign_key_constraint_on:
column: requestMessageId
table:
name: OCPPMessages
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Reservations
schema: public
object_relationships:
- name: EvseType
using:
foreign_key_constraint_on: evseId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: SalesTariffs
schema: public
object_relationships:
- name: ChargingSchedule
using:
foreign_key_constraint_on: chargingScheduleDatabaseId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: SecurityEvents
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: SendLocalListAuthorizations
schema: public
object_relationships:
- name: LocalListAuthorization
using:
foreign_key_constraint_on: authorizationId
- name: SendLocalList
using:
foreign_key_constraint_on: sendLocalListId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: SendLocalLists
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: SendLocalListAuthorizations
using:
foreign_key_constraint_on:
column: sendLocalListId
table:
name: SendLocalListAuthorizations
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: SequelizeMeta
schema: public

View File

@@ -0,0 +1,34 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: ServerNetworkProfiles
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: ChargingStationNetworkProfiles
using:
foreign_key_constraint_on:
column: websocketServerConfigId
table:
name: ChargingStationNetworkProfiles
schema: public
- name: SetNetworkProfiles
using:
foreign_key_constraint_on:
column: websocketServerConfigId
table:
name: SetNetworkProfiles
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: SetNetworkProfiles
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: ServerNetworkProfile
using:
foreign_key_constraint_on: websocketServerConfigId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: ChargingStationNetworkProfiles
using:
foreign_key_constraint_on:
column: setNetworkProfileId
table:
name: ChargingStationNetworkProfiles
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,51 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: StartTransactions
schema: public
object_relationships:
- name: Connector
using:
foreign_key_constraint_on: connectorDatabaseId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Transaction
using:
foreign_key_constraint_on: transactionDatabaseId
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: StatusNotifications
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: LatestStatusNotifications
using:
foreign_key_constraint_on:
column: statusNotificationId
table:
name: LatestStatusNotifications
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: StopTransactions
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Transaction
using:
foreign_key_constraint_on: transactionDatabaseId
array_relationships:
- name: MeterValues
using:
foreign_key_constraint_on:
column: stopTransactionDatabaseId
table:
name: MeterValues
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Subscriptions
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,74 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Tariffs
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: Authorizations
using:
foreign_key_constraint_on:
column: tariffId
table:
name: Authorizations
schema: public
- name: Connectors
using:
foreign_key_constraint_on:
column: tariffId
table:
name: Connectors
schema: public
- name: MeterValues
using:
foreign_key_constraint_on:
column: tariffId
table:
name: MeterValues
schema: public
- name: Transactions
using:
foreign_key_constraint_on:
column: tariffId
table:
name: Transactions
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,60 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: TenantPartners
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: AsyncJobStatuses
using:
foreign_key_constraint_on:
column: tenantPartnerId
table:
name: AsyncJobStatuses
schema: public
- name: Authorizations
using:
foreign_key_constraint_on:
column: tenantPartnerId
table:
name: Authorizations
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,366 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Tenants
schema: public
array_relationships:
- name: Authorizations
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Authorizations
schema: public
- name: Boots
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Boots
schema: public
- name: Certificates
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Certificates
schema: public
- name: ChangeConfigurations
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ChangeConfigurations
schema: public
- name: ChargingNeeds
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ChargingNeeds
schema: public
- name: ChargingProfiles
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ChargingProfiles
schema: public
- name: ChargingSchedules
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ChargingSchedules
schema: public
- name: ChargingStationNetworkProfiles
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ChargingStationNetworkProfiles
schema: public
- name: ChargingStationSecurityInfos
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ChargingStationSecurityInfos
schema: public
- name: ChargingStationSequences
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ChargingStationSequences
schema: public
- name: ChargingStations
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ChargingStations
schema: public
- name: ComponentVariables
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ComponentVariables
schema: public
- name: Components
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Components
schema: public
- name: CompositeSchedules
using:
foreign_key_constraint_on:
column: tenantId
table:
name: CompositeSchedules
schema: public
- name: Connectors
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Connectors
schema: public
- name: DeleteCertificateAttempts
using:
foreign_key_constraint_on:
column: tenantId
table:
name: DeleteCertificateAttempts
schema: public
- name: EventData
using:
foreign_key_constraint_on:
column: tenantId
table:
name: EventData
schema: public
- name: EvseTypes
using:
foreign_key_constraint_on:
column: tenantId
table:
name: EvseTypes
schema: public
- name: Evses
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Evses
schema: public
- name: InstallCertificateAttempts
using:
foreign_key_constraint_on:
column: tenantId
table:
name: InstallCertificateAttempts
schema: public
- name: InstalledCertificates
using:
foreign_key_constraint_on:
column: tenantId
table:
name: InstalledCertificates
schema: public
- name: LatestStatusNotifications
using:
foreign_key_constraint_on:
column: tenantId
table:
name: LatestStatusNotifications
schema: public
- name: LocalListAuthorizations
using:
foreign_key_constraint_on:
column: tenantId
table:
name: LocalListAuthorizations
schema: public
- name: LocalListVersionAuthorizations
using:
foreign_key_constraint_on:
column: tenantId
table:
name: LocalListVersionAuthorizations
schema: public
- name: LocalListVersions
using:
foreign_key_constraint_on:
column: tenantId
table:
name: LocalListVersions
schema: public
- name: Locations
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Locations
schema: public
- name: MessageInfos
using:
foreign_key_constraint_on:
column: tenantId
table:
name: MessageInfos
schema: public
- name: MeterValues
using:
foreign_key_constraint_on:
column: tenantId
table:
name: MeterValues
schema: public
- name: OCPPMessages
using:
foreign_key_constraint_on:
column: tenantId
table:
name: OCPPMessages
schema: public
- name: Reservations
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Reservations
schema: public
- name: SalesTariffs
using:
foreign_key_constraint_on:
column: tenantId
table:
name: SalesTariffs
schema: public
- name: SecurityEvents
using:
foreign_key_constraint_on:
column: tenantId
table:
name: SecurityEvents
schema: public
- name: SendLocalListAuthorizations
using:
foreign_key_constraint_on:
column: tenantId
table:
name: SendLocalListAuthorizations
schema: public
- name: SendLocalLists
using:
foreign_key_constraint_on:
column: tenantId
table:
name: SendLocalLists
schema: public
- name: ServerNetworkProfiles
using:
foreign_key_constraint_on:
column: tenantId
table:
name: ServerNetworkProfiles
schema: public
- name: SetNetworkProfiles
using:
foreign_key_constraint_on:
column: tenantId
table:
name: SetNetworkProfiles
schema: public
- name: StartTransactions
using:
foreign_key_constraint_on:
column: tenantId
table:
name: StartTransactions
schema: public
- name: StatusNotifications
using:
foreign_key_constraint_on:
column: tenantId
table:
name: StatusNotifications
schema: public
- name: StopTransactions
using:
foreign_key_constraint_on:
column: tenantId
table:
name: StopTransactions
schema: public
- name: Subscriptions
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Subscriptions
schema: public
- name: Tariffs
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Tariffs
schema: public
- name: TenantPartners
using:
foreign_key_constraint_on:
column: tenantId
table:
name: TenantPartners
schema: public
- name: TransactionEvents
using:
foreign_key_constraint_on:
column: tenantId
table:
name: TransactionEvents
schema: public
- name: Transactions
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Transactions
schema: public
- name: VariableAttributes
using:
foreign_key_constraint_on:
column: tenantId
table:
name: VariableAttributes
schema: public
- name: VariableCharacteristics
using:
foreign_key_constraint_on:
column: tenantId
table:
name: VariableCharacteristics
schema: public
- name: VariableMonitoringStatuses
using:
foreign_key_constraint_on:
column: tenantId
table:
name: VariableMonitoringStatuses
schema: public
- name: VariableMonitorings
using:
foreign_key_constraint_on:
column: tenantId
table:
name: VariableMonitorings
schema: public
- name: VariableStatuses
using:
foreign_key_constraint_on:
column: tenantId
table:
name: VariableStatuses
schema: public
- name: Variables
using:
foreign_key_constraint_on:
column: tenantId
table:
name: Variables
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
id:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: TransactionEvents
schema: public
object_relationships:
- name: EvseType
using:
foreign_key_constraint_on: evseId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Transaction
using:
foreign_key_constraint_on: transactionDatabaseId
array_relationships:
- name: MeterValues
using:
foreign_key_constraint_on:
column: transactionEventId
table:
name: MeterValues
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,80 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Transactions
schema: public
object_relationships:
- name: Authorization
using:
foreign_key_constraint_on: authorizationId
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Connector
using:
foreign_key_constraint_on: connectorId
- name: Evse
using:
foreign_key_constraint_on: evseId
- name: Location
using:
foreign_key_constraint_on: locationId
- name: StartTransaction
using:
foreign_key_constraint_on:
column: transactionDatabaseId
table:
name: StartTransactions
schema: public
- name: StopTransaction
using:
foreign_key_constraint_on:
column: transactionDatabaseId
table:
name: StopTransactions
schema: public
- name: Tariff
using:
foreign_key_constraint_on: tariffId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
array_relationships:
- name: ChargingNeeds
using:
foreign_key_constraint_on:
column: transactionDatabaseId
table:
name: ChargingNeeds
schema: public
- name: ChargingProfiles
using:
foreign_key_constraint_on:
column: transactionDatabaseId
table:
name: ChargingProfiles
schema: public
- name: MeterValues
using:
foreign_key_constraint_on:
column: transactionDatabaseId
table:
name: MeterValues
schema: public
- name: TransactionEvents
using:
foreign_key_constraint_on:
column: transactionDatabaseId
table:
name: TransactionEvents
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,68 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: VariableAttributes
schema: public
object_relationships:
- name: Boot
using:
foreign_key_constraint_on: bootConfigId
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Component
using:
foreign_key_constraint_on: componentId
- name: EvseType
using:
foreign_key_constraint_on: evseDatabaseId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Variable
using:
foreign_key_constraint_on: variableId
array_relationships:
- name: VariableStatuses
using:
foreign_key_constraint_on:
column: variableAttributeId
table:
name: VariableStatuses
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: VariableCharacteristics
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Variable
using:
foreign_key_constraint_on: variableId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: VariableMonitoringStatuses
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: VariableMonitoring
using:
foreign_key_constraint_on: variableMonitoringId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: VariableMonitorings
schema: public
object_relationships:
- name: ChargingStation
using:
foreign_key_constraint_on: stationId
- name: Component
using:
foreign_key_constraint_on: componentId
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: Variable
using:
foreign_key_constraint_on: variableId
array_relationships:
- name: VariableMonitoringStatuses
using:
foreign_key_constraint_on:
column: variableMonitoringId
table:
name: VariableMonitoringStatuses
schema: public
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: VariableStatuses
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: VariableAttribute
using:
foreign_key_constraint_on: variableAttributeId
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""

View File

@@ -0,0 +1,81 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: Variables
schema: public
object_relationships:
- name: Tenant
using:
foreign_key_constraint_on: tenantId
- name: VariableCharacteristic
using:
foreign_key_constraint_on:
column: variableId
table:
name: VariableCharacteristics
schema: public
array_relationships:
- name: ComponentVariables
using:
foreign_key_constraint_on:
column: variableId
table:
name: ComponentVariables
schema: public
- name: EventData
using:
foreign_key_constraint_on:
column: variableId
table:
name: EventData
schema: public
- name: VariableAttributes
using:
foreign_key_constraint_on:
column: variableId
table:
name: VariableAttributes
schema: public
- name: VariableMonitorings
using:
foreign_key_constraint_on:
column: variableId
table:
name: VariableMonitorings
schema: public
insert_permissions:
- role: user
permission:
check:
tenantId:
_eq: x-hasura-tenant-id
columns: '*'
comment: ""
select_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
allow_aggregations: true
comment: ""
update_permissions:
- role: user
permission:
columns: '*'
filter:
tenantId:
_eq: x-hasura-tenant-id
check:
tenantId:
_eq: x-hasura-tenant-id
comment: ""
delete_permissions:
- role: user
permission:
filter:
tenantId:
_eq: x-hasura-tenant-id
comment: ""

View File

@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: geography_columns
schema: public

View File

@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: geometry_columns
schema: public

View File

@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
table:
name: spatial_ref_sys
schema: public

View File

@@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
- "!include public_AsyncJobStatuses.yaml"
- "!include public_Authorizations.yaml"
- "!include public_Boots.yaml"
- "!include public_Certificates.yaml"
- "!include public_ChangeConfigurations.yaml"
- "!include public_ChargingNeeds.yaml"
- "!include public_ChargingProfiles.yaml"
- "!include public_ChargingSchedules.yaml"
- "!include public_ChargingStationNetworkProfiles.yaml"
- "!include public_ChargingStationSecurityInfos.yaml"
- "!include public_ChargingStationSequences.yaml"
- "!include public_ChargingStations.yaml"
- "!include public_ComponentVariables.yaml"
- "!include public_Components.yaml"
- "!include public_CompositeSchedules.yaml"
- "!include public_Connectors.yaml"
- "!include public_DeleteCertificateAttempts.yaml"
- "!include public_EventData.yaml"
- "!include public_EvseTypes.yaml"
- "!include public_Evses.yaml"
- "!include public_InstallCertificateAttempts.yaml"
- "!include public_InstalledCertificates.yaml"
- "!include public_LatestStatusNotifications.yaml"
- "!include public_LocalListAuthorizations.yaml"
- "!include public_LocalListVersionAuthorizations.yaml"
- "!include public_LocalListVersions.yaml"
- "!include public_Locations.yaml"
- "!include public_MessageInfos.yaml"
- "!include public_MeterValues.yaml"
- "!include public_OCPPMessages.yaml"
- "!include public_Reservations.yaml"
- "!include public_SalesTariffs.yaml"
- "!include public_SecurityEvents.yaml"
- "!include public_SendLocalListAuthorizations.yaml"
- "!include public_SendLocalLists.yaml"
- "!include public_SequelizeMeta.yaml"
- "!include public_ServerNetworkProfiles.yaml"
- "!include public_SetNetworkProfiles.yaml"
- "!include public_StartTransactions.yaml"
- "!include public_StatusNotifications.yaml"
- "!include public_StopTransactions.yaml"
- "!include public_Subscriptions.yaml"
- "!include public_Tariffs.yaml"
- "!include public_TenantPartners.yaml"
- "!include public_Tenants.yaml"
- "!include public_TransactionEvents.yaml"
- "!include public_Transactions.yaml"
- "!include public_VariableAttributes.yaml"
- "!include public_VariableCharacteristics.yaml"
- "!include public_VariableMonitoringStatuses.yaml"
- "!include public_VariableMonitorings.yaml"
- "!include public_VariableStatuses.yaml"
- "!include public_Variables.yaml"
- "!include public_geography_columns.yaml"
- "!include public_geometry_columns.yaml"
- "!include public_spatial_ref_sys.yaml"

View File

@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
#
# SPDX-License-Identifier: Apache-2.0
disabled_for_roles: []

Some files were not shown because too many files have changed in this diff Show More