The Coordinator module provides REST API for frontend applications and orchestrates queries across multiple Node instances via FlightSQL/HTTP.
┌─────────────────────────────────────────────────────────────────────┐
│ Homer Coordinator │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ REST API (:8080) │ │
│ │ /api/v4/transactions/search │ │
│ │ /api/v4/auth/sessions │ │
│ │ /api/v4/dashboards │ │
│ │ ... │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ FlightService │ │
│ │ (Query routing to nodes via HTTP) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────┼────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ :50051/HTTP │ │ :50051/HTTP │ │ :50051/HTTP │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Settings DB (DuckDB) │ │
│ │ Users, Dashboards, User Settings │ │
│ └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
- REST API - Full API for Homer UI (v1, v3, v4 endpoints)
- Multi-node routing - Query distribution across multiple nodes
- JWT Authentication - Secure API access with tokens
- OAuth2 Support - External authentication providers
- Settings Storage - DuckDB-based user and dashboard storage
- Embedded UI - Optional built-in web interface
{
"coordinator": {
"enable": true,
"http_server": {
"enable": true,
"host": "0.0.0.0",
"port": 8080,
"read_timeout": 30,
"write_timeout": 30,
"static_path": ""
},
"nodes": [
{
"name": "local",
"host": "127.0.0.1",
"port": 50051,
"use_tls": false,
"token": "your-node-token",
"priority": 1
}
],
"settings_db_path": "/var/lib/homer/homer_settings.duckdb",
"transaction_view_max_opens": 3,
"ip_alias_cache_ttl_sec": 30,
"jwt": {
"secret": "your-jwt-secret-minimum-32-characters",
"expire_hours": 24
},
"auth": { "type": "internal" }
}
}transaction_view_max_opens (default 3) caps how many successful GET /export/view/:uuid responses each shared view token may serve before it is exhausted (the expires_at TTL still applies).
ip_alias_cache_ttl_sec (default 30, min 5, max 86400) controls how long the coordinator reuses the in-memory IP-alias lookup table when enriching search/QoS/message rows. Creating, updating, or deleting an alias still clears the cache immediately.
{
"coordinator": {
"enable": true,
"http_server": {
"host": "0.0.0.0",
"port": 8080
},
"nodes": [
{
"name": "node-eu",
"host": "node-eu.example.com",
"port": 50051,
"use_tls": true,
"token": "eu-node-token",
"priority": 1
},
{
"name": "node-us",
"host": "node-us.example.com",
"port": 50051,
"use_tls": true,
"token": "us-node-token",
"priority": 2
},
{
"name": "node-asia",
"host": "node-asia.example.com",
"port": 50051,
"use_tls": true,
"token": "asia-node-token",
"priority": 3
}
],
"settings_db_path": "/var/lib/homer/homer_settings.duckdb",
"jwt": {
"secret": "your-jwt-secret-minimum-32-characters",
"expire_hours": 24
},
"auth": { "type": "internal" }
}
}Authorization code flow (server exchanges code, loads userinfo, provisions DuckDB user). See AUTH_LDAP_AND_OAUTH.md.
{
"coordinator": {
"enable": true,
"http_server": {
"host": "0.0.0.0",
"port": 8080
},
"nodes": [
{
"name": "local",
"host": "127.0.0.1",
"port": 50051
}
],
"settings_db_path": "/var/lib/homer/homer_settings.duckdb",
"jwt": {
"secret": "your-jwt-secret",
"expire_hours": 24
},
"auth": { "type": "internal" },
"oauth2_provider": {
"enable": true,
"name": "google",
"type": "oauth2",
"provider_name": "Google",
"provider_image": "/assets/google.svg",
"position": 1,
"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"client_secret": "YOUR_CLIENT_SECRET",
"auth_url": "https://accounts.google.com/o/oauth2/v2/auth",
"token_url": "https://oauth2.googleapis.com/token",
"redirect_url": "http://localhost:8080/api/v4/auth/oauth2/google/callback",
"profile_url": "https://openidconnect.googleapis.com/v1/userinfo",
"scopes": ["openid", "email", "profile"],
"use_pkce": false,
"callback_url": "http://localhost:8080/",
"auto_redirect": false
}
}
}| Parameter | Type | Default | Description |
|---|---|---|---|
enable |
bool | true | Enable HTTP server |
host |
string | "0.0.0.0" | Listen address |
port |
int | 8080 | HTTP server port |
read_timeout |
int | 30 | Read timeout in seconds |
write_timeout |
int | 30 | Write timeout in seconds |
static_path |
string | "" | Path to UI static files (optional) |
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
string | - | Node identifier |
host |
string | - | Node hostname or IP |
port |
int | 50051 | Node FlightSQL port |
use_tls |
bool | false | Use TLS for connection |
token |
string | "" | Authentication token for node |
priority |
int | 1 | Query routing priority (lower = higher priority) |
| Parameter | Type | Default | Description |
|---|---|---|---|
secret |
string | - | JWT signing secret (min 32 characters recommended) |
expire_hours |
int | 24 | Token expiration time in hours |
coordinator.auth may be a string (legacy), or an object with a type field:
| Form | Description |
|---|---|
{"type":"internal"} (recommended) |
Same bootstrap and defaults as the string "internal" (admin admin, default hash for sipcapture). On startup, if no users row exists for that admin username, the coordinator inserts it once. Login checks users only. |
String "internal" |
Backward compatible; same semantics as {"type":"internal"}. |
{"type":"ldap"} / {"type":"oauth"} |
Declares preferred password-auth mode metadata; does not enable LDAP/OAuth by itself (coordinator.ldap, oauth2_provider still apply). No internal bootstrap. |
Omitted (coordinator without auth) |
Same as {"type":"internal"} (default admin + sipcapture bootstrap). |
Object without type (or empty type) |
Same as {"type":"internal"}: internal bootstrap applies; unset admin_user / admin_password_hash get defaults, or your explicit values are used. |
First login (type internal or string "internal"): username admin, password sipcapture until changed in Settings → Users or in DuckDB.
Reset admin password — set coordinator.auth.admin_password_hash (and optional admin_user) in modular homer.json or via HOMER_COORDINATOR_AUTH_ADMIN_PASSWORD_HASH, then run:
homer --config-path /path/to/homer.json --reset-admin-passwordThe process opens coordinator.settings_db_path, ensures schema, updates or inserts the users row for admin_user, and exits (no HTTP server). Details, JSON examples, and env overrides: AUTH_LDAP_AND_OAUTH.md.
Password hashes: Users created or updated via the API store bcrypt in users.password_hash. Login also accepts legacy SHA-256 hex (default bootstrap admin, migrated homer-app users). --reset-admin-password still expects SHA-256 hex in admin_password_hash (see AUTH_LDAP_AND_OAUTH.md).
Generating a SHA-256 hex hash (for admin_password_hash / reset only):
# Linux/macOS
echo -n "your-password" | sha256sum | cut -d' ' -f1
# Example: password "sipcapture" produces:
# 883ffc1f37fd0fe542b0fb9740035c4383e7d976c411161d24e62edace280f90| Parameter | Type | Default | Description |
|---|---|---|---|
type |
string | (see table above) | internal, ldap, or oauth. If the whole auth section is omitted, or type is omitted / empty on the object, the effective type is internal. |
admin_user |
string | "admin" | Admin username when type is internal (including when type is omitted and normalized to internal). |
admin_password_hash |
string | "" | SHA-256 hex for bootstrap / --reset-admin-password. For type internal (or omitted auth normalized to internal), empty means the default sipcapture hash after load. API user password changes use bcrypt separately. |
fallback_auth_type |
string | "" | If set to internal or ldap, password login tries this backend after the client-selected type fails (wrong password or backend unavailable). Must not be oauth. Empty disables the second attempt. |
disable_password_login |
bool | false | If true, hide internal/LDAP from GET /auth/providers and return 403 on POST /auth/sessions. Use with OAuth2 for IdP-only login. Env: HOMER_COORDINATOR_AUTH_DISABLE_PASSWORD_LOGIN. |
Single optional OAuth2 IdP object (OAuthProviderConfig in src/config/config.go). Uses the OAuth 2.0 authorization code flow on the coordinator (redirect → IdP → callback with code → token + userinfo → one-time browser token → POST /auth/oauth2/token for JWT).
| Parameter | Type | Description |
|---|---|---|
enable |
bool | Enable this provider |
name |
string | Stable id for URLs (/api/v4/auth/oauth2/{name}/...) |
type |
string | Usually oauth2 |
provider_name |
string | Display name |
provider_image |
string | Provider logo path |
position |
int | Display order in discovery |
client_id |
string | OAuth2 client id |
client_secret |
string | Client secret (required when use_pkce is false) |
auth_url |
string | IdP authorization endpoint |
token_url |
string | IdP token endpoint |
redirect_url |
string | Registered redirect URI (must equal https://<host>/api/v4/auth/oauth2/<name>/callback) |
profile_url |
string | UserInfo / profile GET URL |
scopes |
string[] | Optional; default openid, email |
use_pkce |
bool | Public clients: set true; client_secret may be empty |
callback_url |
string | Browser redirect after callback with ?token= or ?oauth_error= |
skip_auto_provision |
bool | If true, user must pre-exist in DuckDB |
admin_groups |
string[] | Profile group claim values that grant admin JWT |
group_claim |
string | JSON claim for groups (default groups) |
auto_redirect |
bool | If true, UI may redirect immediately to this provider |
The deprecated oauth2_providers array is still accepted at startup and migrated with a log warning; prefer oauth2_provider.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v4/auth/sessions |
Create session (login) |
| DELETE | /api/v4/auth/sessions/:id |
Delete session (logout) |
| GET | /api/v4/auth/providers |
List password backends (internal, ldap) and OAuth2 provider metadata |
| GET | /api/v4/me |
Get current user info |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v4/transactions |
List transactions |
| POST | /api/v4/transactions/search |
Search transactions |
| POST | /api/v4/transactions/messages |
Get transaction messages (with optional Lua call-id correlation, see LUA_CORRELATION.md) |
| GET | /api/v4/messages/:id |
Get single message |
| GET | /api/v4/messages/:id/decoded |
Get decoded message |
| POST | /api/v4/transactions/qos |
Get QoS data |
| POST | /api/v4/transactions/events |
Application log lines (proto 100) for session(s) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v4/dashboards |
List dashboards |
| POST | /api/v4/dashboards |
Create dashboard |
| GET | /api/v4/dashboards/:id |
Get dashboard |
| PUT | /api/v4/dashboards/:id |
Update dashboard |
| DELETE | /api/v4/dashboards/:id |
Delete dashboard |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v4/users |
List users |
| POST | /api/v4/users |
Create user |
| GET | /api/v4/users/:id |
Get user |
| PATCH | /api/v4/users/:id |
Update user |
| DELETE | /api/v4/users/:id |
Delete user |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v4/mappings |
List field mappings |
| GET | /api/v4/hepsubs |
List HEP subscriptions |
| GET | /api/v4/aliases |
List aliases |
| GET | /api/v4/db/nodes |
List configured nodes |
| GET | /api/v4/modules |
Get modules status |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v4/statistics/query |
Execute statistics query |
| GET | /api/v4/statistics/databases |
List databases |
| GET | /api/v4/statistics/measurements |
List measurements |
| GET | /api/v4/statistics/metrics |
List metrics |
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /api/v1/status |
System status with nodes info |
Single server running Ingest, Storage, Node, and Coordinator:
┌───────────────────────────────────────────────────────────┐
│ Homer Server │
│ ┌─────────┐ ┌─────────┐ ┌──────┐ ┌─────────────┐ │
│ │ Ingest │ │ Storage │ │ Node │ │ Coordinator │ │
│ │ :9060 │ │ │ │:50051│ │ :8080 │ │
│ └────┬────┘ └────┬────┘ └───┬──┘ └──────┬──────┘ │
│ │ │ │ │ │
│ └───────────┴──────────┴───────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ DuckLake │ │
│ └─────────────┘ │
└───────────────────────────────────────────────────────────┘
Separate instances for each module:
┌─────────────────┐
│ Coordinator │
│ :8080 │
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Ingest 1 │ │ Ingest 2 │ │ Ingest 3 │
│ Storage 1 │ │ Storage 2 │ │ Storage 3 │
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ :50051 │ │ :50051 │ │ :50051 │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ DuckLake │ │ DuckLake │ │ DuckLake │
│ (Region 1) │ │ (Region 2) │ │ (Region 3) │
└─────────────┘ └─────────────┘ └─────────────┘
Multiple Coordinators behind load balancer:
┌─────────────────┐
│ Load Balancer │
└────────┬────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Coordinator │ │ Coordinator │ │ Coordinator │
│ :8080 │ │ :8080 │ │ :8080 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└─────────────────┼─────────────────┘
│
┌────────┴────────┐
│ Node Pool │
│ (FlightSQL) │
└─────────────────┘
The Coordinator uses a DuckDB database for storing:
- users — accounts and credentials
- user_preferences — generic per-user JSON blobs (
/api/v4/me/settings) - global_settings — system-wide key/value rows (
/api/v4/advanced) - dashboard_settings — saved dashboards
- user_mapping_settings — per-user mapping widget overrides
- correlation_scripts — Lua correlation + script API rows
- dashboard_alerts, export_share_links, transaction_view_tokens — supporting tables
The database is created automatically at settings_db_path.
The canonical DDL is created in EnsureSettingsSchema in
src/coordinator/services/settings_db.go (DuckDB: BIGINT ids, JSON data, etc.).
Do not rely on older table names such as user_settings or hep_scripts.
See examples in the examples/ directory:
homer-coordinator.json- Coordinator onlyhomer-writer.json- Ingest + Storage + Nodehomer.json- Ingest + Storage + Node + Coordinator (all-in-one)
- JWT Secret - Use a strong, random secret (minimum 32 characters)
- Admin Password - Store as SHA256 hash, never plain text
- TLS - Enable
use_tlsfor node connections in production - Network - Restrict coordinator access via firewall/reverse proxy
- OAuth2 - Use HTTPS callback URLs in production