This is the API service for the PixelIt project, this provides the bitmaps for the Pixelit.
Use Docker Compose. The production image runs on Node.js 24 as the unprivileged
node user and installs only production dependencies from package-lock.json.
services:
pixelit_api:
restart: unless-stopped
container_name: pixelit_api
image: ghcr.io/pixelit-project/pixelit.api:latest
ports:
- "8080:8080"
volumes:
- /etc/localtime:/etc/localtime:ro
environment:
PORT: 8080
TELEMETRY_USER_CHECK: "true"
# Set to the actual reverse proxy IP(s) or CIDR(s); empty disables proxy trust.
# Example for a proxy container with a fixed IP: "172.20.0.10"
TRUSTED_PROXIES: ""
MYSQL_HOST: host
MYSQL_DATABASE: database
MYSQL_USER: user
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
# Optional admin access (HTTPS origin, no trailing slash):
# ADMIN_ORIGIN: https://pixelit.bastelbunker.de
# ADMIN_GITHUB_CLIENT_ID: ${PIXELIT_GITHUB_CLIENT_ID}
# ADMIN_GITHUB_CLIENT_SECRET: ${PIXELIT_GITHUB_CLIENT_SECRET}
# ADMIN_GITHUB_OWNER_ID: "123456"
# Optional Telegram moderation notifications (see docs/telegram.md):
# TELEGRAM_BOT_TOKEN: ${PIXELIT_TELEGRAM_BOT_TOKEN}
# TELEGRAM_BOT_USERNAME: YourPixelItBot
# TELEGRAM_WEBHOOK_SECRET: ${PIXELIT_TELEGRAM_WEBHOOK_SECRET}
GITHUB_TOKEN: ${GITHUB_TOKEN}
API_GLOBAL_LIMIT_WINDOW_MS: 300000
API_GLOBAL_LIMIT_MAX: 100
API_TELEMETRY_LIMIT_WINDOW_MS: 900000
API_TELEMETRY_LIMIT_MAX: 10
API_SAVEBITMAP_LIMIT_WINDOW_MS: 900000
API_SAVEBITMAP_LIMIT_MAX: 10
API_GLOBAL_LIMIT_EXCLUDE: "192.168.0.1, 192.168.2.1, 192.168.3.1, ::1"
API_TELEMETRY_LIMIT_EXCLUDE: "192.168.0.1, 192.168.2.1, 192.168.3.1, ::1"
API_SAVEBITMAP_LIMIT_EXCLUDE: "192.168.0.1, 192.168.2.1, 192.168.3.1, ::1"
SEQ_SERVER: http://seqserver:5341
SEQ_APIKEY: ${SEQ_APIKEY}Set these values in the Docker Compose environment section or in a .env
file when running the API locally. Defaults below apply when a variable is not
set; the Compose example explicitly enables TELEMETRY_USER_CHECK.
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Port on which the API listens inside the container. If changed, update the container side of the port mapping, e.g. 8080:9090 for PORT: 9090. |
TELEMETRY_USER_CHECK |
false |
Set to "true" to restrict /api/Statistics, /api/UserMap, and /api/UserMapData to clients whose uuid query parameter matches a telemetry record updated within the last 30 days. Otherwise the response contains error: { telemetryUser: false }. This does not disable telemetry collection. The check is cached for 30 seconds. |
TRUSTED_PROXIES |
Empty (no proxy trust) | Comma-separated trusted proxy IPs or CIDRs used to resolve the client IP from X-Forwarded-For. Leave empty for direct access. See the proxy configuration notes below. |
MYSQL_HOST |
Not set by the API | MySQL server hostname or IP, reachable from the API container. |
MYSQL_DATABASE |
Not set by the API | Name of the existing PixelIt database. |
MYSQL_USER |
Not set by the API | MySQL user with access to the PixelIt tables. |
MYSQL_PASSWORD |
Not set by the API | Password for the MySQL user. |
TELEGRAM_BOT_TOKEN |
Not set (notifications disabled) | Optional bot token for moderation notifications. Requires GitHub admin login and the other Telegram settings; see setup. |
TELEGRAM_BOT_USERNAME |
Not set | Bot username without @. |
TELEGRAM_WEBHOOK_SECRET |
Not set | Random 32–256 character webhook authentication secret (A-Z, a-z, 0-9, _, -). |
GITHUB_TOKEN |
Not set (unauthenticated requests) | Optional token sent with GitHub API requests for PixelIt release information. |
API_GLOBAL_LIMIT_WINDOW_MS |
300000 (5 minutes) |
Rate-limit window in milliseconds for all /api/ routes. |
API_GLOBAL_LIMIT_MAX |
100 |
Maximum requests per client in the global window. |
API_GLOBAL_LIMIT_EXCLUDE |
Empty | Comma-separated exact client IPs exempt from the global limiter only. |
API_TELEMETRY_LIMIT_WINDOW_MS |
900000 (15 minutes) |
Additional rate-limit window in milliseconds for POST /api/Telemetry. |
API_TELEMETRY_LIMIT_MAX |
10 |
Maximum telemetry requests per client in the telemetry window. |
API_TELEMETRY_LIMIT_EXCLUDE |
Empty | Comma-separated exact client IPs exempt from the telemetry limiter only. |
API_SAVEBITMAP_LIMIT_WINDOW_MS |
900000 (15 minutes) |
Additional rate-limit window in milliseconds for POST /api/SaveBitmap. |
API_SAVEBITMAP_LIMIT_MAX |
10 |
Maximum bitmap uploads per client in the bitmap window. |
API_SAVEBITMAP_LIMIT_EXCLUDE |
Empty | Comma-separated exact client IPs exempt from the bitmap limiter only. |
SEQ_SERVER |
Not set (Seq logging disabled) | Optional Seq server URL, e.g. http://seqserver:5341. Console logging remains enabled. |
SEQ_APIKEY |
Not set | API key for the Seq server, if required by that server. |
Use positive integers for rate-limit windows and request counts. A value of 0
falls back to the default; it does not disable the limiter. Exceeding a limit
returns HTTP 429. Telemetry and bitmap requests are subject to both the global
and their endpoint-specific limiter. Excluding an IP from one limiter does not
exempt it from the others. Rate-limit counters are stored in memory per API
process and reset on restart.
By default the API uses the direct peer IP and ignores X-Forwarded-For.
When running behind a reverse proxy, set TRUSTED_PROXIES to a comma-separated
list of trusted proxy IPs or CIDRs, for example 127.0.0.1,::1 for a local proxy.
For a reverse proxy in another Docker container, use its address on the shared
Docker network; 127.0.0.1 refers to the API container itself. Keep the value
empty when accessing the API directly.
Use the actual proxy addresses for your deployment, restrict direct access to
the API, and ensure the proxy overwrites incoming forwarding headers.
Do not trust all addresses or a shared network containing untrusted clients.
Rate limits group IPv6 clients by /56 subnet. The existing *_LIMIT_EXCLUDE
settings match exact resolved client IPs, including normalized IPv4-mapped IPv6.
Use Node.js 24.15.0 or newer within the Node.js 24 series, or Node.js 26+.
Install the locked dependencies with npm ci and run the dev server with
npm run dev.
SaveBitmap and Telemetry return HTTP 200 only after the database write
completes, HTTP 400 for invalid input, and HTTP 500 for persistence failures.
The JSON body limit remains 100 KB (HTTP 413 when exceeded).
Bitmap uploads require nonempty name and userName strings (up to 255
characters), integer sizeX/sizeY dimensions from 1 to 256, a boolean
animated, and a string rgb565array. Each frame must contain exactly
sizeX * sizeY integer RGB565 values from 0 to 65535. Static data uses
[0,65535]; animated data uses [0,65535],[65535,0], without an outer array.
Telemetry requires a 40-character hexadecimal uuid, nonempty version and
type strings, a matrix object with positive integer type, nonempty name,
and integer width/height from 1 to 256, plus a sensors array of at most
32 nonempty strings. Text fields are limited to 255 characters. buildSection
is optional and may be empty. GeoIP data is resolved by the server.
All asynchronous API routes forward errors to the central error handler. Database failures return HTTP 500 instead of successful empty data. GitHub requests time out after 10 seconds; failures return HTTP 502 and timeouts return HTTP 504. Failed upstream requests are not cached as empty release lists.
An empty database returns valid empty statistics and bitmap lists.
LastVersion and Releases accept ?includePrereleases=true to include published
prereleases alongside stable releases, retaining their existing bounded response
formats and shared cache. Releases returns up to four stable releases plus up to
four prereleases when opted in. All other values preserve stable-only behavior. Deploy
this API support before enabling beta searches in firmware/WebUI.
LastVersion and LastRelease return {} when no stable release is available.
OfficialReleases and Releases exclude prereleases; draft releases are ignored.
/api/Releases and /api/LastRelease expose fwdownloads entries with the existing
name (display label) and downloads fields, plus fileName (original asset
filename) and downloadURL (GitHub browser download URL). Clients should match the
exact hardware filename and exclude .full-upgrade.bin images for OTA uploads.
Only show a direct download when a matching asset and its URL are present; older
API deployments may omit these fields. Deploy this API update before the WebUI
that uses direct firmware downloads. Existing release caching (600 seconds) also
covers this metadata; no extra upstream request is made. /api/LastVersion
continues to omit fwdownloads, keeping device version checks small.
Run the regression tests with npm test. HTTP and Seq logging tests use temporary
loopback servers; database, GitHub, and GeoIP calls are stubbed, so no credentials
are needed. Run npm audit --omit=dev to check production dependencies.
Build and smoke-test the production image locally:
docker build --file dockerfile --tag pixelit-api:test .
docker run --rm --network none \
--mount "type=bind,src=${PWD}/scripts/container-smoke.js,dst=/tmp/container-smoke.js,readonly" \
pixelit-api:test node /tmp/container-smoke.jsThe smoke test checks application startup, unprivileged execution, excluded
development files, input validation, and controlled database connection errors.
The Docker build context includes only production source, package files, and the license;
local .env files, credentials, and node_modules are excluded. CI runs the
regression tests and dependency audit before building and smoke-testing Docker.
The API includes a dark-mode admin page at /admin/ for reviewing icon reports,
hiding or restoring icons, and viewing aggregate device telemetry statistics.
The API creates the moderation tables automatically before accepting requests.
The MySQL account needs CREATE permission in addition to its existing access.
Enable GitHub login with ADMIN_ORIGIN (public HTTPS origin, no trailing slash),
ADMIN_GITHUB_CLIENT_ID, ADMIN_GITHUB_CLIENT_SECRET and ADMIN_GITHUB_OWNER_ID
(your numeric GitHub ID). The owner manages up to 100 moderators by GitHub username in the admin UI.
Separate audit views show moderation decisions and owner-only membership changes.
Without GitHub configuration, existing ADMIN_PASSWORD login remains supported.
See moderation setup and API reference for setup, deployment requirements and endpoints. Admins and moderators can optionally connect Telegram for bundled notifications about new reports. Webhook registration is automatic at startup; see Telegram setup.
- (o0shojo0o) Refresh bitmap caches after successful uploads so newly saved icons appear immediately.
- (o0shojo0o) Finish GitHub sign-in through a same-site completion page to avoid returning to the login screen after cross-site redirects with Strict session cookies, including iOS Safari.
- (o0shojo0o) Use English throughout the admin UI, sign-in flow and Telegram notifications, including accessibility labels and date/number formatting.
- (o0shojo0o) Add optional prerelease selection to LastVersion and Releases; stable-only remains the default.
-
(o0shojo0o) Refresh the dark admin workspace with consistent navigation icons, clearer cards and account settings, and scrollable mobile navigation.
-
(o0shojo0o) Add separate moderation and owner-only administrative audit views; record moderator membership changes transactionally and allow adding moderators by GitHub username while retaining stable account IDs.
-
(o0shojo0o) Confirm successful Telegram account linking once in the bot's private chat.
-
(o0shojo0o) Register the Telegram webhook automatically at startup while preserving queued updates; retry failed registrations with increasing delays without preventing API startup.
-
(o0shojo0o) Add optional per-account Telegram notifications for new icon reports, with expiring account links, bundled delivery, unsubscribe controls and automatic removal with moderator access.
-
(o0shojo0o) Add GitHub OAuth login with an owner role, UI-managed moderators, membership revocation and individual moderation audit identities; retain password login for installations without GitHub configuration.
-
(o0shojo0o) Show open reports by default in the admin UI.
-
(o0shojo0o) Validate matrix names against supported firmware names in admin and public statistics; group other names as
Customwithout changing stored telemetry.
- (o0shojo0o) Serve a minimal login page before authentication; protect dashboard HTML and JavaScript with the admin session.
- (o0shojo0o) Return to the login page after logout or session expiry and verify dashboard access is revoked in regression tests.
- (o0shojo0o) Add public icon reports and a dark-mode admin UI for reviewing reports, hiding icons and restoring them with a transactional audit history.
- (o0shojo0o) Add aggregate telemetry statistics for recent device activity, firmware versions, hardware types and matrix configurations.
- (o0shojo0o) Configure admin access through
ADMIN_PASSWORDandADMIN_ORIGIN, with internal password hashing, expiring sessions and CSRF protection. - (o0shojo0o) Run database migrations automatically before startup and include migration and route files in Docker images.
- (o0shojo0o) Add configurable reporting, login, admin-read and admin-action rate limits, operational logging and bitmap cache invalidation.
- (o0shojo0o) Group existing endpoints into route modules while preserving their URLs and responses; document code-style conventions in
AGENTS.md. - (o0shojo0o) Add regression coverage for admin access, moderation, rate limits, log redaction, telemetry aggregates and migrations.
- (o0shojo0o) Add original asset filenames and browser download URLs to firmware release metadata while preserving existing labels and download counts. Document client compatibility and add regression coverage.
- (o0shojo0o) Upgrade to Express 5 and body-parser 2; update dotenv, mysql2, express-rate-limit, and CodeQL.
- (o0shojo0o) Require successful regression tests, dependency auditing, Docker build, and container smoke tests before eligible Dependabot updates can be automatically merged.
- (o0shojo0o) Switch Docker to Node.js 24, install locked production dependencies, run as an unprivileged user, and exclude local files from the build context.
- (o0shojo0o) Update vulnerable dependencies within existing version ranges and declare supported Node.js versions.
- (o0shojo0o) Fix Seq logging initialization for the ES module used by seq-logging 3.
- (o0shojo0o) Forward asynchronous route errors and return proper error responses for database and GitHub failures.
- (o0shojo0o) Add a 10-second GitHub request timeout and handle empty statistics, missing release metadata, and prerelease-only release lists.
- (o0shojo0o) Preserve full release data when serving LastVersion and exclude prereleases from OfficialReleases.
- (o0shojo0o) Extend regression tests and CI with dependency auditing and production container smoke tests.
- (o0shojo0o) Document all environment variables, defaults, telemetry access checks, and rate-limit configuration.
- (o0shojo0o) Trust forwarded client IPs only from proxies configured via
TRUSTED_PROXIES. - (o0shojo0o) Normalize client IPs and group IPv6 rate limits by /56 subnet.
- (o0shojo0o) Validate bitmap and telemetry payloads before writing to the database.
- (o0shojo0o) Return HTTP 200 only after successful persistence and HTTP 500 on write failures.
- (o0shojo0o) Add regression tests and a CI workflow for API validation, persistence errors, and rate limits.
- (o0shojo0o) added new api endpoint
getBMPByID_v2for better handling of animated bitmaps
- (o0shojo0o) add uuid to each request
- (o0shojo0o) change node from 20 to 21
- (o0shojo0o) update dependencies
- (o0shojo0o) Build statistics revised
- (o0shojo0o) Version statistics revised
- (o0shojo0o) small fixes
- (o0shojo0o) only active telemetry users can see statistics
- (o0shojo0o) added ENV param
TELEMETRY_USER_CHECK
- (o0shojo0o) small fixes
- (o0shojo0o) added more statistics to endpoint
Statistics
- (o0shojo0o) added new api endpoint
Statistics
- (o0shojo0o) added
buildSectionfor endpointTelemetry
- (o0shojo0o) added ENV param
API_SAVEBITMAP_LIMIT_EXCLUDE
- (o0shojo0o) fix endpoint
SaveBitmap
- (o0shojo0o) small fixes
- (o0shojo0o) added new api endpoint
SaveBitmap
- (o0shojo0o) added environment variable
API_GLOBAL_LIMIT_EXCLUDE - (o0shojo0o) added environment variable
API_TELEMETRY_LIMIT_EXCLUDE
- (o0shojo0o) fix rate limit key
- (o0shojo0o) added environment variable
API_GLOBAL_LIMIT_WINDOW_MS - (o0shojo0o) added environment variable
API_GLOBAL_LIMIT_MAX - (o0shojo0o) added environment variable
API_TELEMETRY_LIMIT_WINDOW_MS - (o0shojo0o) added environment variable
API_TELEMETRY_LIMIT_MAX - (o0shojo0o) added environment variable
API_TELEMETRY_LIMIT_MAX - (o0shojo0o) enrich log with rate limit data
- (o0shojo0o) added .env file support
- (o0shojo0o) added rate limits
- (o0shojo0o) no prereleases for releases statistics, information, etc. not considered.
- (o0shojo0o) fix id check
- (o0shojo0o) inital commit