Self-Adaptive Discovery daemon (Access-Point Registry)
297
š Language: English Versionā | Korean Versionā
š¦ GitHub Repository: https://github.com/jay94ks/lightaprā
APR is a 'Self-Adaptive Discovery' protocol designed to assemble dynamic network topologies at runtime in auto-scaling environments without hardcoded endpoints. Rather than employing heavy distributed consensus or excessive security validation, APR focuses on fast in-memory service discovery and lightweight integration.
An APR daemon listens on independent ports in a single process:
/resolve).Username prefixed with the server's role. Combine APR access key and Username as the MQTT password.{ role, workers, endpoint } to topic apr/node/meta. (Worker nodes or web browser clients that do not expose an HTTP endpoint specify "endpoint": null.)apr/{role}.app/{role} or app/{role}/{worker} MQTT topics as auxiliary channels.GRACE status) instead of immediate deletion.
OK status).ERASED), broadcasting expiration to the topology and removing the node from the registry.Multi-language SDKs and example projects are provided for seamless APR integration:
sdk/):
sdk/cpp: C++17 headers and static library (apr_sdk_cpp)sdk/csharp: .NET 8.0 C# SDK (Apr.Sdk)sdk/nodejs: Node.js SDK (@lightapr/sdk)sdk/ts: TypeScript SDK (@lightapr/sdk-ts)examples/):
examples/http_node: Service node example exposing an HTTP endpointexamples/worker_node: Background worker node example without an HTTP endpointexamples/ts/, examples/csharp/, examples/cpp/: Language-specific example variationsmonitor/): dependency-free, single-file HTML/CSS/JS apps, compiled directly into the lightapr binary at build time (no external file needed at runtime) and served over HTTP when explicitly enabled:
monitor/html/index.html: read-only observability dashboard - server status, the 7-way memory breakdown, live connection count, the node registry table, and a live topology event feed over WebSocket MQTT. Served at GET /monitor (and GET /) when started with --monitor.monitor/tester/index.html: interactive playground to exercise every daemon feature from a browser - an HTTP Playground for each REST endpoint, an MQTT Playground (connect/subscribe/publish with a decoded live message log), a node-registration simulator, and a capped burst-request tool that demonstrates the connection/rate limits below. Served at GET /tester when started with --tester.The LightAPR server daemon is published as a Docker Hub container image available with tag 1.0.0 and linked latest tag.
jay94ks/lightapr)jay94ks/lightapr:1.0.0, jay94ks/lightapr:latest1883: Native TCP MQTT Control Plane8083: WebSocket MQTT Control Plane8080: HTTP REST Management/Discovery PlaneCELL_ID: Cell identifier (Default: default_cell)ACCESS_KEY: Access authentication key (Default: lightapr_secret_key)MQTT_PORT: Native TCP MQTT port (Default: 1883)WS_PORT: WebSocket MQTT port (Default: 8083)HTTP_PORT: HTTP REST API port (Default: 8080)WORKER_THREADS: Event loop worker thread count (Default: 0, auto-detect CPU cores)LOG_FILE: Log file path (Default: /var/log/lightapr.log)LOG_LEVEL: Minimum log level: debug|info|warn|error (Default: info)STANDALONE: Standalone console logging mode (Default: true)CONFIG_FILE: Path to a JSON config file (optional; see below). Its values replace the CLI defaults, and are themselves overridden by any other CLI flag/env var set explicitly.IDLE_TIMEOUT: Seconds a connection may sit idle (no completed read) before the server closes it (Default: 90)MAX_BUFFER_BYTES: Max bytes a single session may buffer before an in-progress request/frame is considered complete; exceeding it closes the connection (Default: 262144, 256 KiB)MAX_NODE_EXTRA_BYTES: Max serialized size of one worker's extra announcement, settable via apr/node/extra (and the optional extra on apr/node/meta); an oversized update is ignored, keeping the previous value. A cap independent of MAX_BUFFER_BYTES (Default: 8192, 8 KiB)MAX_CONNECTIONS: Process-wide cap on simultaneously open connections across MQTT (TCP+WS) and HTTP combined; 0 = unlimited (Default: 10000)MAX_CONNECTIONS_PER_IP: Max simultaneous connections from a single source IP, across all listeners; 0 = unlimited (Default: 100)MAX_NEW_CONNECTIONS_PER_IP: Max new connections a single source IP may open within CONNECTION_RATE_WINDOW_SEC; 0 = unlimited (Default: 20)CONNECTION_RATE_WINDOW_SEC: Sliding window (seconds) used by MAX_NEW_CONNECTIONS_PER_IP (Default: 10)MAX_REQUESTS_PER_CONNECTION: Max HTTP requests served over one keep-alive connection before the server closes it (forcing a reconnect, which is then rate-limited again); 0 = unlimited (Default: 10000)MONITOR: Serve the read-only monitor dashboard at /monitor (and /) (Default: false - off unless explicitly enabled)TESTER: Serve the interactive tester app at /tester (Default: false - off unless explicitly enabled)LightAPR is designed to survive connection floods and abusive clients without exhausting server resources:
MAX_CONNECTIONS, MAX_CONNECTIONS_PER_IP, and the MAX_NEW_CONNECTIONS_PER_IP/CONNECTION_RATE_WINDOW_SEC rate limit before a session object is even created ā a rejected connection costs one map lookup and an immediate socket close, nothing more.IDLE_TIMEOUT, MAX_BUFFER_BYTES) is force-closed if it goes quiet too long or tries to buffer an unreasonably large partial request/frame ā this bounds a slow/stalled client's resource footprint regardless of intent.CONNECT (bad credentials or malformed packet) now closes the connection immediately after the error response, instead of allowing unlimited retry attempts on one socket ā every retry has to reconnect, and reconnects are rate-limited above.MAX_REQUESTS_PER_CONNECTION bounds how many requests a single connection can push through before being forced to reconnect (and pass the connection-rate check again), so keep-alive can't be used to bypass per-IP throttling.You can watch these limits in action live: enable --tester, open /tester, and use its "Burst Request Test" tool (capped at 100 requests) to see connections get rejected once a limit is hit.
Two operator-facing web apps ship compiled directly into the lightapr binary at build time (cmake/EmbedFile.cmakeā embeds them as byte arrays - no separate file to deploy) and are off by default, since they're debug/operator tools rather than part of the discovery API:
--monitor ā http://<host>:<http_port>/monitor (and /): live dashboard - node registry, the 8-way memory breakdown (registry/mqtt_rx/mqtt_tx/http_rx/http_tx/other/extra, plus OS RSS), live connection count, and a decoded WebSocket-MQTT topology event feed.--tester ā http://<host>:<http_port>/tester: interactive playground for every HTTP endpoint and MQTT operation (connect/subscribe/publish), a node-registration simulator, and the burst-request tool mentioned above.Both require only the flag (or MONITOR=true / TESTER=true in Docker) - no extra build step, since the HTML is embedded at compile time from monitor/html/index.html and monitor/tester/index.html.
LightAPR accepts every setting as a CLI flag, or you can point it at a JSON config file with -f/--config <path> to avoid a long argument list:
| Flag | Description | Default |
|---|---|---|
-s, --standalone | Run in the foreground with console logging | daemon mode |
-c, --cell-id | Cell identifier | default_cell |
-k, --access-key | MQTT access authentication key | lightapr_secret_key |
-p, --port | Native TCP MQTT port | 1883 |
-w, --ws-port | WebSocket MQTT port | 8083 |
-h, --http-port | HTTP REST API port | 8080 |
-t, --threads | Event loop worker thread count (0 = auto-detect) | 0 |
-l, --log-file | Log file path (daemon mode) | lightapr.log |
-v, --log-level | Minimum log level: debug|info|warn|error | info |
-f, --config | Path to a JSON config file | - |
--idle-timeout | Idle session timeout, seconds | 90 |
--max-buffer-bytes | Max buffered bytes per session | 262144 |
--max-connections | Process-wide connection cap (0 = unlimited) | 10000 |
--max-connections-per-ip | Per-source-IP connection cap (0 = unlimited) | 100 |
--max-new-connections-per-ip | Per-source-IP new-connection rate cap (0 = unlimited) | 20 |
--connection-rate-window-sec | Rate-limit window, seconds | 10 |
--max-requests-per-connection | Max HTTP requests per keep-alive connection (0 = unlimited) | 10000 |
--monitor | Serve the monitor dashboard at /monitor (and /) | off |
--tester | Serve the interactive tester app at /tester | off |
A sample config file is provided at config.example.jsonā :
{
"standalone": false,
"cell_id": "default_cell",
"access_key": "lightapr_secret_key",
"mqtt_port": 1883,
"ws_port": 8083,
"http_port": 8080,
"threads": 0,
"log_file": "lightapr.log",
"log_level": "info",
"session_idle_timeout_sec": 90,
"max_session_buffer_bytes": 262144,
"max_connections": 10000,
"max_connections_per_ip": 100,
"max_new_connections_per_ip": 20,
"connection_rate_window_sec": 10,
"max_requests_per_connection": 10000,
"monitor": false,
"tester": false
}
Precedence: built-in defaults ā --config file ā other explicit CLI flags (which always win for the fields they set, regardless of argument order).
# Run container using Docker CLI
docker run -d \
--name lightapr-server \
-p 1883:1883 \
-p 8083:8083 \
-p 8080:8080 \
-e CELL_ID="cell-prod-1" \
-e ACCESS_KEY="my_secret_key" \
jay94ks/lightapr:latest
docker-compose.yml)Utilize the included docker-compose.ymlā file to launch the service:
version: '3.8'
services:
lightapr:
image: jay94ks/lightapr:latest
container_name: lightapr-server
build:
context: .
dockerfile: docker/Dockerfile
restart: unless-stopped
ports:
- "1883:1883" # Native TCP MQTT
- "8083:8083" # WebSocket MQTT
- "8080:8080" # HTTP REST Management
environment:
- CELL_ID=default_cell
- ACCESS_KEY=lightapr_secret_key
- MQTT_PORT=1883
- WS_PORT=8083
- HTTP_PORT=8080
- LOG_FILE=/var/log/lightapr.log
- STANDALONE=true
- MAX_CONNECTIONS=10000
- MAX_CONNECTIONS_PER_IP=100
- MAX_NEW_CONNECTIONS_PER_IP=20
- MONITOR=false
- TESTER=false
# Run with Docker Compose
docker compose up -d
Content type
Image
Digest
sha256:473a07014ā¦
Size
4.8 MB
Last updated
about 1 month ago
docker pull jay94ks/lightapr