diff --git a/DEVELOPERS.md b/DEVELOPERS.md
new file mode 100644
index 0000000..73c662a
--- /dev/null
+++ b/DEVELOPERS.md
@@ -0,0 +1,368 @@
+# Strix for Developers
+
+Strix is a single static binary with embedded web UI and SQLite camera database. No config files, no external dependencies (except optional `ffmpeg` for H264/H265 screenshot conversion). Designed to run alongside your project the same way [go2rtc](https://github.com/AlexxIT/go2rtc) does.
+
+## Binary
+
+Download from [GitHub Releases](https://github.com/eduard256/Strix/releases). Two platforms: `linux/amd64` and `linux/arm64`.
+
+```bash
+chmod +x strix-linux-amd64
+./strix-linux-amd64
+```
+
+The binary needs `cameras.db` in the working directory. Download it from [StrixCamDB](https://github.com/eduard256/StrixCamDB/releases):
+
+```bash
+curl -fsSL https://github.com/eduard256/StrixCamDB/releases/latest/download/cameras.db -o cameras.db
+./strix-linux-amd64
+```
+
+## Docker
+
+```bash
+docker run -d --name strix --network host eduard256/strix:latest
+```
+
+Database is already embedded in the image.
+
+## Environment Variables
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `STRIX_LISTEN` | `:4567` | HTTP listen address |
+| `STRIX_DB_PATH` | `cameras.db` | Path to SQLite database |
+| `STRIX_LOG_LEVEL` | `info` | `trace`, `debug`, `info`, `warn`, `error` |
+| `STRIX_FRIGATE_URL` | auto-discovery | Frigate URL, e.g. `http://localhost:5000` |
+| `STRIX_GO2RTC_URL` | auto-discovery | go2rtc URL, e.g. `http://localhost:1984` |
+
+## Integration Flow
+
+Typical automation flow using the API:
+
+```
+1. Probe device GET /api/probe?ip=192.168.1.100
+2. Search database GET /api/search?q=hikvision
+3. Build stream URLs GET /api/streams?ids=b:hikvision&ip=192.168.1.100&user=admin&pass=12345
+4. Test streams POST /api/test {sources: {streams: [...]}}
+5. Poll results GET /api/test?id=xxx
+6. Generate config POST /api/generate {mainStream: "rtsp://...", subStream: "rtsp://..."}
+```
+
+All endpoints return JSON. CORS is enabled. No authentication.
+
+---
+
+## API Reference
+
+### System
+
+#### `GET /api`
+
+```json
+{"version": "2.0.0", "platform": "amd64"}
+```
+
+#### `GET /api/health`
+
+```json
+{"version": "2.0.0", "uptime": "1h30m0s"}
+```
+
+#### `GET /api/log`
+
+Returns in-memory log in `application/jsonlines` format. Passwords are masked automatically.
+
+#### `DELETE /api/log`
+
+Clears in-memory log. Returns `204`.
+
+---
+
+### Search
+
+#### `GET /api/search?q={query}`
+
+Search camera database by brand, model, or preset name. Empty `q` returns all presets + first brands (limit 50).
+
+```bash
+curl "localhost:4567/api/search?q=hikvision"
+```
+
+```json
+{
+ "results": [
+ {"type": "brand", "id": "b:hikvision", "name": "Hikvision"},
+ {"type": "model", "id": "m:hikvision:DS-2CD2032", "name": "Hikvision: DS-2CD2032"}
+ ]
+}
+```
+
+Result types:
+
+| Type | ID format | Description |
+|------|-----------|-------------|
+| `preset` | `p:{preset_id}` | Curated URL pattern sets (e.g. "ONVIF", "Popular RTSP") |
+| `brand` | `b:{brand_id}` | All URL patterns for a brand |
+| `model` | `m:{brand_id}:{model}` | URL patterns for a specific model |
+
+Multi-word queries match independently: `hikvision DS-2CD` matches brand "Hikvision" AND model containing "DS-2CD".
+
+#### `GET /api/streams`
+
+Build full stream URLs from database patterns with credentials and placeholders substituted.
+
+| Param | Required | Description |
+|-------|----------|-------------|
+| `ids` | yes | Comma-separated IDs from search results |
+| `ip` | yes | Camera IP address |
+| `user` | no | Username (URL-encoded automatically) |
+| `pass` | no | Password (URL-encoded automatically) |
+| `channel` | no | Channel number, default `0` |
+| `ports` | no | Comma-separated port filter (only return URLs matching these ports) |
+
+```bash
+curl "localhost:4567/api/streams?ids=b:hikvision&ip=192.168.1.100&user=admin&pass=12345"
+```
+
+```json
+{
+ "streams": [
+ "rtsp://admin:12345@192.168.1.100/Streaming/Channels/101",
+ "rtsp://admin:12345@192.168.1.100/Streaming/Channels/102",
+ "http://admin:12345@192.168.1.100/ISAPI/Streaming/channels/101/picture"
+ ]
+}
+```
+
+Maximum 20,000 URLs per request. URLs are deduplicated.
+
+---
+
+### Testing
+
+#### `POST /api/test`
+
+Create a test session. 20 parallel workers connect to each URL, extract codecs, capture screenshots.
+
+```bash
+curl -X POST localhost:4567/api/test -d '{
+ "sources": {
+ "streams": [
+ "rtsp://admin:12345@192.168.1.100/Streaming/Channels/101",
+ "rtsp://admin:12345@192.168.1.100/Streaming/Channels/102"
+ ]
+ }
+}'
+```
+
+```json
+{"session_id": "a1b2c3d4e5f6g7h8"}
+```
+
+#### `GET /api/test`
+
+List all active and completed sessions.
+
+```json
+{
+ "sessions": [
+ {
+ "session_id": "a1b2c3d4",
+ "status": "running",
+ "total": 604,
+ "tested": 341,
+ "alive": 191,
+ "with_screenshot": 191
+ }
+ ]
+}
+```
+
+#### `GET /api/test?id={session_id}`
+
+Get session details with full results. Poll this endpoint to track progress.
+
+```json
+{
+ "session_id": "a1b2c3d4",
+ "status": "done",
+ "total": 604,
+ "tested": 604,
+ "alive": 375,
+ "with_screenshot": 375,
+ "results": [
+ {
+ "source": "rtsp://admin:***@192.168.1.100/Streaming/Channels/101",
+ "codecs": ["H264", "PCMA"],
+ "width": 1920,
+ "height": 1080,
+ "latency_ms": 45,
+ "screenshot": "api/test/screenshot?id=a1b2c3d4&i=0"
+ }
+ ]
+}
+```
+
+- `status`: `running` or `done`
+- `codecs`: detected media codecs (H264, H265, PCMA, PCMU, OPUS, etc.)
+- `width`, `height`: resolution extracted from JPEG screenshot
+- `screenshot`: relative URL to fetch the JPEG image
+- Sessions expire 30 minutes after completion
+
+#### `DELETE /api/test?id={session_id}`
+
+Cancel a running session and delete it.
+
+```json
+{"status": "deleted"}
+```
+
+#### `GET /api/test/screenshot?id={session_id}&i={index}`
+
+Returns raw JPEG image. `Content-Type: image/jpeg`.
+
+---
+
+### Config Generation
+
+#### `POST /api/generate`
+
+Generate Frigate config from stream URLs.
+
+```bash
+curl -X POST localhost:4567/api/generate -d '{
+ "mainStream": "rtsp://admin:12345@192.168.1.100/Streaming/Channels/101",
+ "subStream": "rtsp://admin:12345@192.168.1.100/Streaming/Channels/102",
+ "name": "front_door",
+ "objects": ["person", "car"]
+}'
+```
+
+```json
+{
+ "config": "mqtt:\n enabled: false\n\nrecord:\n enabled: true\n\ngo2rtc:\n streams:\n ...",
+ "added": [1, 2, 3, 4, 5]
+}
+```
+
+- `config`: complete Frigate YAML
+- `added`: 1-based line numbers of new lines (for highlighting in UI)
+
+**Merge into existing config** -- pass `existingConfig` field:
+
+```json
+{
+ "mainStream": "rtsp://...",
+ "existingConfig": "go2rtc:\n streams:\n existing_cam:\n - rtsp://...\n\ncameras:\n existing_cam:\n ..."
+}
+```
+
+Strix finds the right insertion points in go2rtc streams and cameras sections. Camera and stream names are deduplicated automatically.
+
+
+Full request schema
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `mainStream` | string | **yes** | Main stream URL |
+| `subStream` | string | no | Sub stream URL for detect role |
+| `name` | string | no | Camera name (auto-generated from IP if empty) |
+| `existingConfig` | string | no | Existing Frigate YAML to merge into |
+| `objects` | string[] | no | Objects to track (default: `["person"]`) |
+| `go2rtc` | object | no | `{mainStreamName, subStreamName, mainStreamSource, subStreamSource}` |
+| `frigate` | object | no | `{mainStreamPath, subStreamPath, mainStreamInputArgs, subStreamInputArgs}` |
+| `detect` | object | no | `{enabled, fps, width, height}` |
+| `record` | object | no | `{enabled, retain_days, mode, alerts_days, detections_days, pre_capture, post_capture}` |
+| `motion` | object | no | `{enabled, threshold, contour_area}` |
+| `snapshots` | object | no | `{enabled}` |
+| `audio` | object | no | `{enabled, filters[]}` |
+| `ffmpeg` | object | no | `{hwaccel, gpu}` |
+| `live` | object | no | `{height, quality}` |
+| `birdseye` | object | no | `{enabled, mode}` |
+| `onvif` | object | no | `{host, port, user, password, autotracking, required_zones[]}` |
+| `ptz` | object | no | `{enabled, presets{}}` |
+| `notifications` | object | no | `{enabled}` |
+| `ui` | object | no | `{order, dashboard}` |
+
+
+
+---
+
+### Probe
+
+#### `GET /api/probe?ip={ip}`
+
+Probe a network device. Runs 6 checks in parallel within 100ms: port scan, ICMP ping, ARP + OUI vendor lookup, reverse DNS, mDNS/HomeKit query, HTTP probe.
+
+```bash
+curl "localhost:4567/api/probe?ip=192.168.1.100"
+```
+
+```json
+{
+ "ip": "192.168.1.100",
+ "reachable": true,
+ "latency_ms": 2.5,
+ "type": "standard",
+ "probes": {
+ "ping": {"latency_ms": 2.5},
+ "ports": {"open": [80, 554, 8080]},
+ "dns": {"hostname": "ipcam.local"},
+ "arp": {"mac": "C0:56:E3:AA:BB:CC", "vendor": "Hikvision"},
+ "mdns": null,
+ "http": {"port": 80, "status_code": 401, "server": "Hikvision-Webs"}
+ }
+}
+```
+
+- `type`: `standard`, `homekit`, or `unreachable`
+- `ports.open`: scanned from 189 ports known in the camera database
+- `arp.vendor`: looked up from OUI table in SQLite database
+- HomeKit cameras return `mdns` with `name`, `model`, `category` (`camera` or `doorbell`), `device_id`, `paired`, `port`
+- ICMP ping requires `CAP_NET_RAW` capability. Falls back to port scan only.
+
+---
+
+### Frigate
+
+#### `GET /api/frigate/config`
+
+Get current Frigate config. Frigate is discovered automatically by probing known addresses (`localhost:5000`, `ccab4aaf-frigate:5000`) or via `STRIX_FRIGATE_URL`.
+
+```json
+{"connected": true, "url": "http://localhost:5000", "config": "mqtt:\n enabled: false\n ..."}
+```
+
+```json
+{"connected": false, "config": ""}
+```
+
+#### `POST /api/frigate/config/save?save_option={option}`
+
+Save config to Frigate. Request body is plain text (YAML config).
+
+| Option | Description |
+|--------|-------------|
+| `saveonly` | Save config without restart (default) |
+| `restart` | Save config and restart Frigate |
+
+---
+
+### go2rtc
+
+#### `PUT /api/go2rtc/streams?name={name}&src={source}`
+
+Add a stream to go2rtc. Proxied to local go2rtc instance (discovered automatically or via `STRIX_GO2RTC_URL`).
+
+```bash
+curl -X PUT "localhost:4567/api/go2rtc/streams?name=front_door&src=rtsp://admin:12345@192.168.1.100/Streaming/Channels/101"
+```
+
+```json
+{"success": true}
+```
+
+```json
+{"success": false, "error": "go2rtc not found"}
+```
diff --git a/README.md b/README.md
index 5e96c19..ed7d87b 100644
--- a/README.md
+++ b/README.md
@@ -118,269 +118,4 @@ Three entity types:
- **Brands** -- all URL patterns for a brand (e.g. "Hikvision", "Dahua")
- **Models** -- URL patterns for a specific model within a brand
-## API Reference
-
-All endpoints return JSON. CORS enabled. Base path: `/`.
-
-### System
-
-#### `GET /api`
-
-```json
-{"version": "2.0.0", "platform": "amd64"}
-```
-
-#### `GET /api/health`
-
-```json
-{"version": "2.0.0", "uptime": "1h30m0s"}
-```
-
-#### `GET /api/log`
-
-Returns in-memory log in `application/jsonlines` format.
-
-#### `DELETE /api/log`
-
-Clears in-memory log. Returns `204`.
-
-### Search
-
-#### `GET /api/search?q={query}`
-
-Search camera database. Empty `q` returns all presets + first brands.
-
-```bash
-curl "localhost:4567/api/search?q=hikvision"
-```
-
-```json
-{
- "results": [
- {"type": "brand", "id": "b:hikvision", "name": "Hikvision"},
- {"type": "model", "id": "m:hikvision:DS-2CD2032", "name": "Hikvision: DS-2CD2032"}
- ]
-}
-```
-
-Types: `preset`, `brand`, `model`. ID prefixes: `p:`, `b:`, `m:brandId:model`.
-
-#### `GET /api/streams?ids={ids}&ip={ip}&user={user}&pass={pass}&channel={n}&ports={ports}`
-
-Build stream URLs from database patterns.
-
-| Param | Required | Description |
-|-------|----------|-------------|
-| `ids` | yes | Comma-separated IDs from search results |
-| `ip` | yes | Camera IP address |
-| `user` | no | Username |
-| `pass` | no | Password |
-| `channel` | no | Channel number (default 0) |
-| `ports` | no | Comma-separated port filter |
-
-```bash
-curl "localhost:4567/api/streams?ids=b:hikvision&ip=192.168.1.100&user=admin&pass=12345"
-```
-
-```json
-{
- "streams": [
- "rtsp://admin:12345@192.168.1.100/Streaming/Channels/101",
- "rtsp://admin:12345@192.168.1.100/Streaming/Channels/102",
- "http://admin:12345@192.168.1.100/ISAPI/Streaming/channels/101/picture"
- ]
-}
-```
-
-### Testing
-
-#### `POST /api/test`
-
-Create a test session. Launches 20 parallel workers.
-
-```bash
-curl -X POST localhost:4567/api/test -d '{
- "sources": {
- "streams": [
- "rtsp://admin:12345@192.168.1.100/Streaming/Channels/101",
- "rtsp://admin:12345@192.168.1.100/Streaming/Channels/102"
- ]
- }
-}'
-```
-
-```json
-{"session_id": "a1b2c3d4e5f6g7h8"}
-```
-
-#### `GET /api/test`
-
-List all sessions.
-
-```json
-{
- "sessions": [
- {"session_id": "a1b2c3d4", "status": "running", "total": 604, "tested": 341, "alive": 191, "with_screenshot": 191}
- ]
-}
-```
-
-#### `GET /api/test?id={session_id}`
-
-Get session details with results.
-
-```json
-{
- "session_id": "a1b2c3d4",
- "status": "done",
- "total": 604,
- "tested": 604,
- "alive": 375,
- "with_screenshot": 375,
- "results": [
- {
- "source": "rtsp://admin:***@192.168.1.100/Streaming/Channels/101",
- "codecs": ["H264", "PCMA"],
- "width": 1920,
- "height": 1080,
- "latency_ms": 45,
- "screenshot": "api/test/screenshot?id=a1b2c3d4&i=0"
- }
- ]
-}
-```
-
-Status: `running` or `done`.
-
-#### `DELETE /api/test?id={session_id}`
-
-Cancel and delete session.
-
-#### `GET /api/test/screenshot?id={session_id}&i={index}`
-
-Returns JPEG image. `Content-Type: image/jpeg`.
-
-### Config Generation
-
-#### `POST /api/generate`
-
-Generate Frigate config.
-
-```bash
-curl -X POST localhost:4567/api/generate -d '{
- "mainStream": "rtsp://admin:12345@192.168.1.100/Streaming/Channels/101",
- "subStream": "rtsp://admin:12345@192.168.1.100/Streaming/Channels/102",
- "name": "front_door",
- "objects": ["person", "car"]
-}'
-```
-
-```json
-{
- "config": "mqtt:\n enabled: false\n\nrecord:\n ...",
- "added": [1, 2, 3, 4]
-}
-```
-
-`added` -- 1-based line numbers of new lines. Useful for highlighting in UI.
-
-To merge into existing config, pass `existingConfig`:
-
-```json
-{
- "mainStream": "rtsp://...",
- "existingConfig": "go2rtc:\n streams:\n ...\ncameras:\n ..."
-}
-```
-
-Strix finds the right insertion points, deduplicates camera and stream names.
-
-
-Full request schema
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `mainStream` | string | **Required.** Main stream URL |
-| `subStream` | string | Sub stream URL |
-| `name` | string | Camera name |
-| `existingConfig` | string | Existing Frigate YAML to merge into |
-| `objects` | string[] | Objects to track (default: `["person"]`) |
-| `go2rtc` | object | Override stream names/sources |
-| `frigate` | object | Override Frigate input paths/args |
-| `detect` | object | `{enabled, fps, width, height}` |
-| `record` | object | `{enabled, retain_days, mode, alerts_days, detections_days, pre_capture, post_capture}` |
-| `motion` | object | `{enabled, threshold, contour_area}` |
-| `snapshots` | object | `{enabled}` |
-| `audio` | object | `{enabled, filters[]}` |
-| `ffmpeg` | object | `{hwaccel, gpu}` |
-| `live` | object | `{height, quality}` |
-| `birdseye` | object | `{enabled, mode}` |
-| `onvif` | object | `{host, port, user, password, autotracking, required_zones[]}` |
-| `ptz` | object | `{enabled, presets{}}` |
-| `notifications` | object | `{enabled}` |
-| `ui` | object | `{order, dashboard}` |
-
-
-
-### Probe
-
-#### `GET /api/probe?ip={ip}`
-
-Probe a network device. Runs all checks in parallel within 100ms.
-
-```bash
-curl "localhost:4567/api/probe?ip=192.168.1.100"
-```
-
-```json
-{
- "ip": "192.168.1.100",
- "reachable": true,
- "latency_ms": 2.5,
- "type": "standard",
- "probes": {
- "ping": {"latency_ms": 2.5},
- "ports": {"open": [80, 554, 8080]},
- "dns": {"hostname": "ipcam.local"},
- "arp": {"mac": "C0:56:E3:AA:BB:CC", "vendor": "Hikvision"},
- "mdns": null,
- "http": {"port": 80, "status_code": 401, "server": "Hikvision-Webs"}
- }
-}
-```
-
-Type: `standard`, `homekit`, or `unreachable`.
-
-HomeKit cameras return `mdns` with `name`, `model`, `category`, `device_id`, `paired`, `port`.
-
-### Frigate
-
-#### `GET /api/frigate/config`
-
-Get Frigate config. Frigate is discovered automatically via known candidates or `STRIX_FRIGATE_URL`.
-
-```json
-{"connected": true, "url": "http://localhost:5000", "config": "mqtt:\n ..."}
-```
-
-```json
-{"connected": false, "config": ""}
-```
-
-#### `POST /api/frigate/config/save?save_option={option}`
-
-Save config to Frigate. Body: plain text (YAML). Options: `saveonly`, `restart`.
-
-### go2rtc
-
-#### `PUT /api/go2rtc/streams?name={name}&src={source}`
-
-Add stream to go2rtc. Proxied to local go2rtc instance.
-
-```bash
-curl -X PUT "localhost:4567/api/go2rtc/streams?name=front_door&src=rtsp://admin:12345@192.168.1.100/Streaming/Channels/101"
-```
-
-```json
-{"success": true}
-```
+**Developers:** integrate [Strix HTTP API](DEVELOPERS.md) into your smart home platform.