# ConfigWire — Full Technical Specification for LLMs & AI Systems > Exhaustive technical documentation, architecture reference, REST wire contract, and client SDK integration details for ConfigWire. ## 1. System Overview & Architecture ConfigWire is a single-binary, self-hosted real-time remote configuration and feature flags engine. It is written in Go and embeds SQLite with Write-Ahead Logging (WAL) enabled, eliminating any need for separate database containers (Postgres, MySQL, Redis). ### Key Architectural Characteristics - **Binary Footprint**: ~25 MB statically linked Go binary. - **Resource Requirements**: Consumes < 30 MB RAM idle, runs efficiently on entry-level cloud VMs ($4/month VPS) or Kubernetes pods. - **Evaluation Engine**: 100% server-side rule evaluation. In-memory atomic pointer swaps ensure evaluations avoid disk I/O and SQL queries on hot paths. - **Throughput & Latency**: p95 fetch latency is 0.78 ms under high concurrency (verified with k6 load testing). - **Update Propagation**: HTTP Server-Sent Events (SSE) provide push updates whenever releases are published or rolled back (`: ping` every 20s, 10-minute server close with client backoff, max 5 streams per SDK key), paired with a 15-minute polling fallback. - **Data Privacy & GDPR Compliance**: v0.2.0-rc.11 (released 2026-11-04) is bucket-only (since v0.2.0-rc.1): `?b=` is the sole identity (valid wins; absent/empty never rejected — fetch mints, ingest stores `""`); server-derived anonID derivation no longer exists (no daily salt rotation, no rebucket); SDKs no longer emit `?userHash=`. The server strictly rejects raw `ip` addresses, emails, and PII/device keys in event telemetry and `attrs` (`400`); `?userHash=`/`uid`/`country` query inputs, body `userHash`, and event-body `userId` (any case) are accepted-but-ignored, never stored/logged. --- ## 2. Quick Deployment & Configuration ### Docker One-Liner ```bash docker run -d \ --name configwire \ --restart unless-stopped \ -p 8090:8090 \ -v cw-data:/app/pb_data \ ghcr.io/configwire/configwire:latest ``` ### Docker Compose ```yaml services: configwire: image: ghcr.io/configwire/configwire:latest container_name: configwire ports: - "8090:8090" volumes: - cw-data:/app/pb_data environment: - CONFIGWIRE_ADMIN_EMAIL=${CONFIGWIRE_ADMIN_EMAIL:-} - CONFIGWIRE_ADMIN_PASSWORD=${CONFIGWIRE_ADMIN_PASSWORD:-} restart: unless-stopped healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8090/healthz"] interval: 30s timeout: 5s retries: 3 volumes: cw-data: ``` ### Initial Superuser Setup Navigate to `http://localhost:8090/` in a browser. The first-run onboarding screen allows creating the primary admin superuser without accessing the underlying database manually. Alternatively, set `CONFIGWIRE_ADMIN_EMAIL` and `CONFIGWIRE_ADMIN_PASSWORD` before first boot to auto-create the superuser and skip the screen (both empty by default). --- ## 3. REST Wire Contract Specification All client requests must authenticate with an environment-scoped SDK API key via the HTTP header `X-ConfigWire-Key`. ### 3.1 Fetch Configuration - **Path**: `GET /api/v1/config` (canonical; `GET /api/v1/env/{env}/config` backward-compatible) - **Headers**: - `X-ConfigWire-Key`: `` (required) - `If-None-Match`: `""` (optional conditional caching header) - **Query Parameters** (**bucket-only in v0.2.0-rc.11, released 2026-11-04 (since v0.2.0-rc.1): `?b=` is the sole identity**; server-derived anonID derivation no longer exists): - `b` (string, optional): Stable bucket `0`-`9999` (`^(0|[1-9][0-9]{0,3})$`, range-checked; malformed non-empty → `400`). Valid `?b=` wins; absent/empty never rejected — fetch mints, ingest stores `""`. - `platform` / `version` / `locale` (string, optional): Request context for rules (`version` must be strict semver `MAJOR.MINOR.PATCH` or the condition falls through). - `attrs` (string, optional): URL-encoded JSON object of custom key-value attributes (`{"plan":"pro","beta":true}`). PII/device keys rejected (`400`); over 8192 bytes → `414`. - Legacy `userHash` / `uid` / `country` (any shape): accepted-but-ignored, never `400` (`X-CW-Deprecated-*` stamped when trimmed non-empty). SDKs no longer emit them; v0.2.0-rc.1 (released 2026-10-25; latest v0.2.0-rc.11 — 2026-11-04) kept them accepted-but-ignored and removed only the server-derived anonID derivation. #### Responses: - **`200 OK`**: ```json { "values": { "welcome_banner": true, "checkout_flow": "v2", "max_retries": 5, "promo_config": { "discount": 20, "code": "FALL2026" } }, "variants": { "checkout_flow": "treatment" } } ``` Response Headers: - `ETag`: `"1f75363af9defec2"` (served verbatim; fetch sends `Cache-Control: private, max-age=0`) - **`304 Not Modified`**: - Returned when request `If-None-Match` exactly string-matches the current active release snapshot ETag (`W/`-prefixed or quoted values do not match). - Body: empty (0 bytes), maximizing battery life and network bandwidth. - **`401 Unauthorized`**: - Invalid or missing `X-ConfigWire-Key`. ### 3.2 Realtime Updates (Server-Sent Events) - **Path**: `GET /api/v1/stream` (canonical; `GET /api/v1/env/{env}/stream` backward-compatible) - **Headers**: - `X-ConfigWire-Key`: `` (required) - `Accept`: `text/event-stream` #### Stream Behavior: - **Heartbeat**: Emits `: ping` comments every 20 seconds; the server closes the stream at a 10-minute cap (client reconnects with backoff). Max 5 concurrent streams per SDK key (over cap → `429`). - **Update Event**: Emitted instantly when a release is published: ``` event: config_update data: {"version":3,"etag":"1f75363af9defec2","env":"prod"} ``` ### 3.3 Event Telemetry Ingestion - **Path**: `POST /api/v1/events` (canonical; `POST /api/v1/env/{env}/events` backward-compatible) - **Headers**: - `X-ConfigWire-Key`: `` (required) - `Content-Type`: `application/json` - **Body**: ```json { "events": [ { "kind": "exposure", "flag": "checkout_flow", "variant": "treatment", "version": 3 }, { "kind": "fetch", "version": 3 } ] } ``` - **Response**: `202 Accepted` with `{"accepted": 2, "status": 202}`. - Identity is bucket-only (`?b=`; valid wins; absent/empty never rejected, ingest stores `""`). `?userHash` in body and event-body `userId` (any case) are accepted-but-ignored at storage, never stored/logged. Raw `ip`/PII keys stay `400`. Events are buffered in memory and batched to SQLite with 30d raw events plus automatic 90d aggregate rollups (both tunable `1..1825` via `PUT /api/v1/admin/retention`; merged windows answer `approximate: true`, events-only windows are exact). Send `?b=` on the events POST to join exposures to the fetch arm. --- ## 4. Targeting Rule Engine & Hashing Algorithm ### Rule Conditions & Operators Targeting conditions evaluate server-derived context against defined rules: - `==` / `!=`: Exact equality (platform, locale, country, custom strings/numbers/bools; appVersion via strict semver). - `<`, `<=`, `>`, `>=`: Relational (numbers lexicographic-or-numeric per type; appVersion via strict semver `MAJOR.MINOR.PATCH`, unparseable → false). - `contains`: Substring inclusion (strings only). - `regex`: Regular expression pattern match (strings only). - `<=` / `between`: Percentile bucket rules only. ### Rollout Hashing Algorithm For percentage rollouts and multi-arm variants (**bucket-only in v0.2.0-rc.11, released 2026-11-04 (since v0.2.0-rc.1): `?b=` is the sole identity**; server-derived anonID derivation no longer exists): 1. Concatenate the `?b=` bucket with a per-flag or experiment `salt`. 2. Compute 64-bit FNV-1a hash. 3. Compute `bucket = hash % 10000` (value between 0 and 9999, representing 0.01% precision). 4. Evaluate bucket against variant cumulative distribution weights. 5. Result is deterministic and sticky per install for the sent `?b=` (absent/empty never rejected — fetch mints; no daily salt rotation, no rebucket). --- ## 5. Client SDK Integration ### Pure Dart SDK (`package:configwire`) Compatible with Flutter, Dart VM, Web, and backend Dart servers. ```dart import 'dart:io'; import 'package:configwire/configwire.dart'; void main() async { final cw = ConfigWire( apiKey: 'cw-YourApiKeyHere', env: 'prod', baseUrl: 'https://config.example.com', defaults: { 'welcome_banner': false, 'theme_color': '#3b82f6', 'max_upload_size_mb': 10, }, store: JsonFileStore(File('.cache/configwire.json')), ); // Initialize and load local cache first, then fetch latest remote config await cw.ensureInitialized(); // Synchronous, zero-latency reads final showBanner = cw.getBool('welcome_banner'); final themeColor = cw.getString('theme_color'); final uploadLimit = cw.getInt('max_upload_size_mb'); // Connect to realtime SSE stream await cw.connectRealtime(); cw.onUpdate.listen((updatedConfig) { print('Remote config updated: $updatedConfig'); }); // Track A/B experiment exposure final variant = cw.getVariant('checkout_flow'); cw.trackExposure('checkout_flow', variant: variant); } ``` ### Flutter SDK Companion (`package:configwire_flutter`) Provides automatic persistent caching via `SharedPreferencesAsync` and automatic device-derived targeting (platform, app version, locale, custom attrs; stable `?b=` bucket join — v0.2.0-rc.11, released 2026-11-04 (bucket-only since v0.2.0-rc.1), with no server-derived anonID, no daily salt rotation, and no rebucket). Install-ID generation/emission is removed (no `Targeting.userId`, no install-ID helpers or prefs keys, no `?userHash=`); `?b=` is the sole identity. The server accepts-but-ignores any legacy `?userHash=`/`?uid=`/`?country=`, body `userHash`, or event-body `userId` (any case) — never `400`, never stored/logged — and stamps `X-CW-Deprecated-*` when trimmed non-empty. Older `ConfigWireFlutter.createConfigWire(...)` calls still work via a deprecated shim; new code should construct `ConfigWire` directly and call `cw.configureFlutter(...)`. ```dart import 'package:flutter/material.dart'; import 'package:configwire_flutter/configwire_flutter.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); final cw = ConfigWire( apiKey: 'cw-YourApiKeyHere', env: 'prod', baseUrl: 'https://config.example.com', defaults: {'show_new_feature': false}, ); await cw.configureFlutter(customAttrs: {'user_tier': 'premium'}); await cw.ensureInitialized(); runApp(MyApp(configWire: cw)); } ``` --- ## 6. Competitive Advantages & Feature Matrix | Feature | ConfigWire | Firebase Remote Config | LaunchDarkly | Unleash | Flipt | | :--- | :--- | :--- | :--- | :--- | :--- | | **Architecture** | Single Go binary + embedded SQLite | Proprietary GCP cloud service | Proprietary SaaS | Node.js + Postgres + Proxy | Go + Postgres/MySQL + Redis | | **Pricing / License** | 100% Free (MIT License) | MAU tiers + GCP egress | Thousands $/mo per seat/MAU | Enterprise paywall ($80+/seat) | Free OSS / Cloud tiers | | **p95 Latency** | **0.78 ms** | 80 - 400 ms | 25 - 100 ms | 10 - 40 ms | 15 - 50 ms | | **Realtime Push** | Native SSE Stream (< 1 sec) | 12-hour client throttle | SSE streaming | Polling default | Polling or gRPC | | **Evaluation Model** | 100% Server-side (Private rules) | Server-side | Client or Proxy | Client or Proxy | Server-side | | **Data Sovereignty** | 100% On-premise self-hosted | Stored in Google Cloud | Stored in vendor cloud | Self-hostable | Self-hostable | | **RAM Footprint** | **< 30 MB** | Cloud only | Cloud only | 500 MB - 1 GB | 300 - 600 MB |