Welcome to the official developer documentation for Solaris Control API v1. This document covers the high-level architecture, security layer, Multiple Scoped API Keys Architecture, authentication schemes, Per-Action Precision & Granular Security Framework, RFC 7807 error format, rate-limiting, and interactive OpenAPI documentation for Solaris.
Solaris Control API v1 is an embedded, high-performance, asynchronous REST and WebSocket API server built directly into the Solaris Flutter application. It allows third-party integrations (Home Assistant, BitFocus Companion, Stream Deck, Raycast, custom scripts, and external automation services) to monitor and control monitor parameters, color temperature, circadian rhythm engines, gaming profiles, and sleep states.
dart:io HttpServer with zero third-party HTTP framework dependencies.ApiKeyEntry) with individual names, access scope configurations (ApiPermissionsConfig), and DPAPI token security.getCanonicalAction), and Clean Storage Protocol.ApiPermissionsDialog) with ExpansionTile accordions, Tri-State master checkboxes (true/false/null), click isolation in leading, and dynamic scope chips.:slug).503 Service Unavailable.safeStateMutator): State mutations are deferred via Future.microtask() to avoid Flutter widget rendering cycle conflicts (setState() or markNeedsBuild() called during build)./api/v1/docs with dynamic permission status annotations.Solaris Control API can operate in two distinct binding modes configurable via application settings:
127.0.0.1, ::1). No external LAN traffic is accepted.0.0.0.0 or :: (all network interfaces).
WindowsFirewallService with a 3-stage UAC elevation fallback (netsh direct -> powershell -Verb RunAs -> fallback).Solaris_Control_API_* are automatically pruned prior to rule updates.45321 (configurable in range 1024..65535 with automatic fallback to 45322..45330 if occupied). Modifying the port synchronizes seamlessly between Solaris Control API and Sleep Integration settings.requireLocalToken)By default, anonymous requests from loopback (127.0.0.1) are permitted if no key authentication is required. When “Require Authorization for Local Requests” (requireLocalToken = true) is enabled in the host GUI:
127.0.0.1) MUST supply a valid API key token.HTTP 401 Unauthorized.GET /api/v1/health remains 100% unrestricted under all configurations.Every HTTP and WebSocket request flows sequentially through 9 defense and isolation layers:
[ Incoming Request ]
│
▼
1. Security Headers Middleware (X-Content-Type-Options, X-Frame-Options, strict CSP for /api/v1/docs)
│
▼
2. Payload Size Guard (Rejects payloads > 64 KB with HTTP 413)
│
▼
3. Content-Type Guard (Enforces application/json on POST/PUT/DELETE)
│
▼
4. Host Header Validation Guard (30s TTL interface cache against DNS Rebinding)
│
▼
5. CORS & CSWSH / Drive-by Guard (Cross-Origin requests require valid X-API-Key)
│
▼
6. Modular Subsystem Routing Guard (Rejects requests to disabled subsystems with HTTP 503 RFC 7807)
│
▼
7. Constant-Time Auth & requireLocalToken Guard (Constant-time SHA-256 token lookup via constantTimeEquals)
│
▼
8. LruCache Rate Limiter (Per-IP token bucket rate limiting, default 120 req/min)
│
▼
9. Granular ACL & Data Privacy Guard (ApiPermissionsChecker & ApiPermissionsFilter per-action scoping)
│
▼
[ Route Handler / Controller ]
Solaris Control API v1 incorporates an enterprise-grade Zero-Trust access control system (ApiPermissionsConfig) configured locally per-key via the Flutter GUI (ApiKeysManagementDialog & ApiPermissionsDialog).
Controls visibility of telemetry and subsystem data in GET /api/v1/status, queries, and WebSocket streaming:
allowReadMonitors: Physical monitor metadata, brightness & color temperature readings.allowReadSolar: Solar elevation, azimuth, zenith angle, day phase, and twilight events.allowReadWeather: Weather condition, temperature, and intensity adjustments.allowReadSleep: Asleep/awake status, last session end, and historical sleep records.allowReadCircadian: Smart Circadian phase tracking and auto-adjustment parameters.[!NOTE] Granular Masking (
ApiPermissionsFilter): Disabling a read flag automatically redacts the corresponding root section and performs key-level masking on composite objects (automation,smart_circadian,presets). For example, whenallowReadSleep = false, sleep metrics (sleep_pressure,sleep_debt) are automatically pruned fromsmart_circadianwithout stripping circadian day-phase info.
In addition to 7 high-level categories (allowedCategories), permissions can be constrained to 28 fine-grained canonical actions (allowedActions set):
Category (ApiActionCategory) |
Actions Count | Canonical Action Keys (allowedActions) |
|---|---|---|
monitors |
3 | set_brightness, set_temperature, set_monitor_offset |
presets |
7 | set_brightness_preset, set_temperature_preset, set_user_preset, cycle_preset, get_app_overrides, manage_app_overrides, reset_builtin_app_overrides |
circadian |
4 | set_auto_brightness, set_auto_temperature, set_smart_circadian, set_smart_circadian_submodules |
gaming |
3 | set_game_mode, set_game_mode_brightness, manage_game_mode_whitelist |
environment |
6 | set_weather_adjustment, set_weather_temperature_adjustment, set_weather_intensity, set_manual_location, set_weather_provider, trigger_sun_sync |
sleep |
1 | push_sleep_status |
system |
4 | manage_webhooks, set_map_animations, on_system_resume, on_hardware_error |
getCanonicalAction)Incoming requests (REST API, WebSocket commands, or internal triggers) undergo automatic alias canonicalization before permission evaluation:
| Incoming Action / Alias | Canonical Action Key | Parent Category |
|---|---|---|
set_monitor_brightness |
set_brightness |
monitors |
set_monitor_temperature |
set_temperature |
monitors |
brightest, bright, dim, dimmest |
set_brightness_preset |
presets |
coolest, cool, warm, warmest |
set_temperature_preset |
presets |
toggle_auto_brightness |
set_auto_brightness |
circadian |
toggle_auto_temperature, set_color_temperature_enabled |
set_auto_temperature |
circadian |
openmeteo, weatherapi, auto |
set_weather_provider |
environment |
clear_failed_webhooks |
manage_webhooks |
system |
[!NOTE] Action Execution Routing:
on_system_resumeandon_hardware_errorare system/hardware-triggered event signals (categorysystem), and per-app override management actions (get_app_overrides,manage_app_overrides,reset_builtin_app_overrides) are handled via dedicated/api/v1/app-overridesREST endpoints rather thanPOST /api/v1/control. Executing non-control actions onPOST /api/v1/controlreturnsHTTP 422 Unprocessable Entity.
[!TIP] Extended Brightness Range (-100% to +100%): When Extra Dark Dimming is enabled in application settings, the
set_brightnessaction accepts values down to-100.0. In this negative range, physical monitor backlights are kept at 0% DDC/CI while a click-through Win32 DWM overlay darkens the display down to an 85% opacity safety floor. If Extra Dark Dimming is disabled, values below 0.0 are rejected with RFC 7807400 Bad Request.
[!TIP] Extended Color Temperature Range (1000K to 6500K): The
set_temperatureaction accepts color temperatures across an ultra-wide range from1000to6500Kelvin (from Daylight down to Candlelight Ember), utilizing a mathematically grounded 111-point Planckian Blackbody spectrum LUT via Win32 GDI & WCS APIs. Per-monitor targeting (monitor_idor:slugroute) allows independent color temperatures per display. Values outside the1000..6500Kelvin range are rejected with RFC 7807400 Bad Request.
To maintain 100% backward compatibility and minimal storage footprint:
allowedActions contains all 28 canonical actions, toJson() automatically omits allowedActions (allowedActions = null).fromJson() automatically converts a full 28-action set back into null.allowedActions == null represents full standard permission for all actions within enabled categories.The Flutter GUI (ApiPermissionsDialog) renders permissions using a 2-level Accordion UI:
ExpansionTile): Each category header features a Tri-State master checkbox:
true (Checked 🟢): All canonical actions of this category are explicitly granted.false (Unchecked ⚪): Category is disabled (allowedCategories does not contain category).null (Indeterminate / Dash 🟠): Category enabled, allowedActions == null (inherit default full access).leading: Tapping the master checkbox in leading is wrapped in GestureDetector/InkWell to prevent unintended expansion/collapsing of the accordion body.apiKeysGranularActionChip): In ApiKeysManagementDialog, keys display green "7 of 7 categories" chips when allowedActions == null, or orange "Allowed N of 28 actions" chips when a granular subset ($N < 28$) is configured.app_en.arb, app_ru.arb, and app_uk.arb.isReadOnly)When isReadOnly = true is enabled for a specific key or globally:
POST /api/v1/control, POST /api/v1/monitors/:slug/*, POST /api/v1/app-overrides*, POST /api/v1/webhooks*, POST /api/v1/sleep/*, POST /api/sleep/*) are immediately rejected with HTTP 403 Forbidden (Read-Only Mode Enabled).GET /api/v1/health remains 100% accessible.To prevent external API clients or WebSocket payloads from tampering with permission configurations, ApiControlHandler enforces an immediate rejection rule:
apiPermissions, permissions, apiKeys, or apiKey is rejected with HTTP 403 Forbidden (Privilege Escalation Prohibited).ApiPermissionsDialog.Solaris manages access tokens through a Multiple Scoped API Keys System (ApiKeyEntry). Each key possesses an isolated security context and granular scope.
ApiKeyEntry)id: Unique string identifier (e.g. key_1721800000123).name: Custom human-readable label (e.g. "Home Assistant Key", "Stream Deck"). Max 50 chars, auto-trimmed.token: Cryptographically secure random token starting with prefix sol_sec_ (72 characters total: 8 char prefix + 64 hex characters from 32 bytes).createdAt: UTC timestamp of creation.lastUsedAt: UTC timestamp of most recent successful request execution.permissions: Independent ApiPermissionsConfig instance controlling read flags and mutation categories.To prevent accidental total lockout, Solaris enforces a strict Single Key Protection Guard:
removeApiKey return false, and the delete icon 🗑️ is disabled in the GUI (ApiKeysManagementDialog).crypt32.dll) via win32 API bindings.ApiSettingsCard) until acknowledged by the user.Tokens can be passed using any of the following standard methods:
X-API-Key: sol_sec_ae1302d9e99a8b6aad30264417a64cec8ac1b17c20f5abc5cea38b5dea368eae01234567
or standard HTTP Bearer token:
Authorization: Bearer sol_sec_ae1302d9e99a8b6aad30264417a64cec8ac1b17c20f5abc5cea38b5dea368eae01234567
GET /api/v1/ws?token=sol_sec_ae1302d9e99a8b6aad30264417a64cec8ac1b17c20f5abc5cea38b5dea368eae01234567 HTTP/1.1
Sec-WebSocket-Protocol: bearer.sol_sec_ae1302d9e99a8b6aad30264417a64cec8ac1b17c20f5abc5cea38b5dea368eae01234567
[!IMPORTANT] Constant-Time Verification: Auth checks perform a SHA-256
constantTimeEqualshash comparison across all stored keys to protect against timing side-channel attacks. Rejections triggerHTTP 401 Unauthorizedresponses.
When WebSocket sessions end, Solaris transmits explicit status codes:
| Code | Reason String | Trigger Condition |
|---|---|---|
1000 |
Normal Closure | Client gracefully disconnected. |
1001 |
Solaris API Server Stopping / PC Entering Sleep Mode |
Host user or OS stopped Control API server or PC entered sleep mode. |
1001 |
Heartbeat failed |
Socket heartbeat timeout (ping/pong failure). |
1008 |
Slow Consumer: Pending buffer limit exceeded 512 KB |
Unconsumed client frame buffer exceeded 512 KB threshold. |
4001 |
Key Revoked |
Host user revoked/deleted the key associated with this connection. |
4001 |
Token Regenerated |
Host user regenerated the secret token for this key. |
4001 |
Local Auth Required |
Host user enabled requireLocalToken = true while an anonymous socket was open. |
All non-2xx direct REST HTTP responses from Solaris Control API return application/problem+json formatted strictly according to RFC 7807 (Problem Details for HTTP APIs). (Note: Batch action item errors and WebSocket handshake rejection frames use simplified JSON error objects).
{
"type": "https://solaris.local/errors/access-denied",
"title": "Action Category Prohibited",
"status": 403,
"detail": "Action category \"gaming\" is disabled in API permissions settings.",
"instance": "/api/v1/control",
"timestamp": "2026-07-25T14:30:00.000Z"
}
| HTTP Status | Error Type URI | Description |
|---|---|---|
400 Bad Request |
https://solaris.local/errors/control-error / monitors-error / app-overrides-error |
Invalid JSON syntax, missing required field, or invalid parameter. |
401 Unauthorized |
https://solaris.local/errors/unauthorized |
Missing, empty, or invalid API access token. |
403 Forbidden |
https://solaris.local/errors/access-denied |
Mutation disabled by Read-Only mode, category prohibited, or action denied in ACL. |
403 Forbidden |
https://solaris.local/errors/drive-by-blocked |
Untrusted Origin/Referer header without valid API token. |
403 Forbidden |
https://solaris.local/errors/untrusted-host |
Untrusted Host header detected (Host header validation guard). |
404 Not Found |
https://solaris.local/errors/monitors-error / not-found |
Unknown endpoint or requested monitor slug/ID not found. |
413 Payload Too Large |
https://solaris.local/errors/payload-too-large |
Request body exceeds 64 KB (65,536 bytes). |
415 Unsupported Media |
https://solaris.local/errors/unsupported-media-type |
Mutating request sent without Content-Type: application/json. |
422 Unprocessable |
https://solaris.local/errors/unprocessable-entity / control-error |
Invalid parameter values or unknown action command name. |
429 Too Many Requests |
https://solaris.local/errors/too-many-requests |
Rate limit quota exceeded (per-IP limit). |
500 Internal Error |
about:blank |
Unexpected internal server exception. |
503 Service Unavailable |
https://solaris.local/errors/service-disabled |
Requested subsystem (Solaris Control API or Sleep Integration API) is disabled in application settings. |
Solaris Control API embeds an interactive RapiDoc HTML endpoint (<rapi-doc>) and an OpenAPI 3.0.3 JSON Spec generator.
GET http://localhost:45321/api/v1/docsGET http://localhost:45321/api/v1/openapi.jsonThe interactive RapiDoc documentation allows developers to inspect models, view request schemas, and execute API calls directly from their browser.