About this project
# IzgoN
IzgoN is a delta-sync server designed for fleets of devices that repeatedly report similar state. Instead of uploading full payloads every cycle, each node POSTs its current state; IzgoN compares it to the last state it saw and returns either `NO_CHANGE` (zero payload bytes), a minimal JSON delta, or the full state when a delta would be larger than the state it replaces.
## Key capabilities
- **Delta sync**: Only changed fields are sent back to the device, reducing reply payloads dramatically.
- **Conditional sync (v1.4.0+)**: Devices that have not changed can send a short checksum token instead of the full report, so the report itself never goes on the wire.
- **Savings measurement**: The dashboard and `/api/metrics` endpoint show live byte savings in both directions (reply and uplink).
- **Benchmark tool**: `benchmark.py` (stdlib-only) replays your own real reports to measure savings on your data, with synthetic mode for testing.
- **Adaptive polling suggestions**: The server can suggest a longer reporting interval after several identical reports, with explicit staleness trade-offs.
- **Silence alerts**: Webhook notifications when a node stops reporting and when it returns.
- **Batch sync**: Devices that buffered while offline can flush a queue in one request.
- **Free tier**: 10,000 syncs without a license key, enough to evaluate.
## How it works
A node sends its state inside `state` (or just a `checksum` if unchanged). The server responds with one of four statuses:
- `NO_CHANGE` — nothing changed, zero bytes sent.
- `SYNC_REQUIRED` — only changed keys are returned; client merges them.
- `FULL_STATE` — the complete new state is returned (when a delta would be larger).
- `SEND_STATE` — the server has no baseline or the checksum is unknown; client must resend the full state.
Nested objects are diffed recursively; lists are compared as a whole (a deliberate limitation). An optional `epoch` token forces a full state after a server restart or lost mirror, preventing silent desync.
## Quick start
```bash
docker run -p 8000:8000 -e DATAPULSE_API_KEY=change-me ghcr.io/izgamber/izgon:latest
```
Or with Docker Compose (includes Redis for persistent baselines):
```bash
git clone https://github.com/izGamber/IZgoN.git
cd IZgoN
cp .env.example .env
docker compose up -d
```
Dashboard at `http://localhost:8000`. Send a state:
```bash
curl -X POST http://localhost:8000/api/nodes/sensor-01/sync \
-H "Content-Type: application/json" \
-H "X-API-Key: dev-local-key" \
-d '{"state": {"temp": 21.5, "hum": 60, "batt": 98}}'
```
Repeat the same state → `NO_CHANGE` with zero delta bytes. Change one field → only that field is returned.
## Benchmark on your own data
```bash
python3 benchmark.py --payload-file my-reports.json
```
Accepts JSON arrays or JSON Lines, auto-detects device ID fields, and measures change rate from your data. Synthetic mode: `python3 benchmark.py --nodes 50 --rounds 100 --change-rate 0.05`.
Measured savings (5% change rate): ~94% on reply, ~40% on uplink (per-SIM), ~65% for polling clients. At 70% change rate, savings drop to ~35% — the honest boundary.
## API endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | `/api/nodes/{id}/sync` | API key | Submit state or checksum, get delta/full/NO_CHANGE |
| POST | `/api/nodes/{id}/sync/batch` | API key | Replay buffered queue in one request |
| GET | `/api/nodes` | API key | List nodes and baselines (paged) |
| GET | `/api/metrics` | none | Live byte savings totals |
| GET | `/api/license` | none | Current tier and remaining free syncs |
| GET | `/healthz` | none | Redis reachability, storage mode |
| GET | `/` | none | Dashboard |
## Configuration
All settings via environment variables (see `.env.example`). Key ones:
- `DATAPULSE_REDIS_URL` — Redis connection for baselines
- `DATAPULSE_API_KEY` — auth key (default `dev-local-key`, change it)
- `DATAPULSE_FREE_TIER_LIMIT` — free syncs before 402 (default 10000)
- `DATAPULSE_LICENSE_KEY` — paid license key (Ed25519-signed, offline validation)
- `DATAPULSE_ALERT_URL` / `DATAPULSE_ALERT_AFTER` — silence alerts
- `DATAPULSE_ADAPTIVE` — enable/disable polling interval suggestions
- `DATAPULSE_MAX_STATE_DEPTH` / `DATAPULSE_MAX_STATE_BYTES` — payload limits
## Security notes
- API key protects all writes; constant-time comparison.
- `/api/metrics` and `/healthz` are unauthenticated by design.
- CORS defaults to `*`; narrow it in production.
- No built-in rate limiting; put behind a reverse proxy.
- State is bounded in depth (32) and size (1 MB).
## Limitations
- Lists are not diffed element-by-element; changing one item sends the whole list.
- Shrinking payloads may trigger `FULL_STATE` (no saving on that sync).
- First report from any node is always full.
- Baselines live in Redis; if wiped, nodes resync once.
- Single instance, no clustering.
- No client SDK yet; integration is plain HTTP POST.
## License and pricing
Source-available, not open source. Free tier: 10,000 syncs. Commercial license: one-time payment, no subscription, offline Ed25519 signature validation. No phone-home.
## Status
Version 1.4.2. Built and maintained by one person. Live demo at `https://izgon-api.onrender.com` (first request may take 20–40s to wake).
Comments
0 Rating appears after 10 ratings
Sign in to join the discussion.