Connectors
How Horus reads runtime evidence from Elasticsearch, Grafana, MongoDB, PostgreSQL, Redis/BullMQ, Sentry, Axiom, and Shopify.
Connectors are how Horus reads runtime evidence. They are read-only adapters to your existing systems. Horus does not write to production, replace your observability stack, or require you to change your applications.
Source intelligence is separate
Connectors are for runtime evidence. Source intelligence is for code context. You can use one without the other, but investigations are strongest when both are configured. See Source intelligence.
Supported runtime connectors
| Connector | Evidence type | When to use it |
|---|---|---|
| Elasticsearch | Logs and error signatures | You aggregate application logs in Elasticsearch and want error counts, first/last occurrences, and trace IDs. |
| Grafana | Metrics | You have dashboards in Grafana and want latency, error-rate, throughput, and queue-growth evidence. |
| MongoDB | Application state | You store operational or domain state in MongoDB and want counts, staleness, and anomalous statuses. |
| Redis / BullMQ | Queue runtime state | You run BullMQ on Redis and want queue depths, failed jobs, and worker health. |
| PostgreSQL | Application state | You store operational or domain state in PostgreSQL and want counts, staleness, and anomalous rows from allowlisted tables. |
| Sentry | Error events | You track exceptions in Sentry and want grouped errors with a direct seed into the implicated code. |
| Axiom | Logs and error signatures | You ship structured logs to Axiom and want incident-relevant error signatures (with counts) and recent events folded in as evidence. |
| Shopify | Admin / business state | You run a Shopify store and want order, inventory, and fulfillment state as evidence — supplied via your own read-only Admin GraphQL queries (--shopify-query, or config queries for watch). |
Git evidence — recent commits, ownership, and change history — is read from the local repository directly and does not require a connector.
Shared concepts
Every connector is scoped to a project and environment. The horus connect command writes non-secret settings into .horus/config.json and encrypts credentials (AES-256-GCM) into .horus/secrets.local.json. You can have different Elasticsearch, Grafana, MongoDB, PostgreSQL, Redis, Sentry, Axiom, or Shopify endpoints for staging and production inside the same project.
By default, horus connect tests the connection before saving. Use --no-test to skip the probe, for example when credentials are rotated separately.
horus connect --helpElasticsearch logs
The Elasticsearch connector reads log indices and turns them into error-signature evidence. It groups similar log lines, counts occurrences, finds the first and last timestamps, and extracts trace and request IDs when available.
When to use it
- Your services ship logs to Elasticsearch.
- You want to know whether an error is new, worsening, or correlated with a deployment.
- You need trace IDs to correlate log lines across services.
Setup
horus connect elasticsearch --url https://es.example.com:9200 --username horus --password $ES_PASSWORD --index-pattern 'my-service-*' --service my-service-prodRequired: --url and --index-pattern. --service scopes log queries to a single service.
Field presets
Horus ships two built-in field mappings: meritt (default) and ecs. You can override individual fields under elasticsearch.fields in .horus/config.json (or horus.config.js) if your indices use custom field names.
How it appears in investigations
During an investigation, Elasticsearch evidence shows up as error signatures: message patterns, counts, time range, and trace/request IDs. It supports correlation by trace ID, not full distributed tracing.
Troubleshooting
| Symptom | Fix |
|---|---|
horus connect fails with 401 | Check username, password, and that the user has read access to the target indices. |
| Investigation returns no log evidence | Verify --service matches the value stored in your logs and that --index-pattern covers the right time range. |
| Wrong timestamp or level field used | Override the field mapping under elasticsearch.fields in .horus/config.json (or horus.config.js) or use the ecs preset if your logs follow ECS. |
Grafana metrics
The Grafana connector queries metrics through Grafana's datasource proxy. It discovers dashboards and panels matching an incident hint, then surfaces latency spikes, error-rate changes, throughput drops, and queue growth.
When to use it
- Your team already uses Grafana dashboards.
- You want metric evidence tied to the same services and time windows as your investigation.
- You need hints like 'latency spike' or 'error rate' resolved to concrete panels.
Setup
horus connect grafana --url https://grafana.example.com --username horus --password $GRAFANA_PASSWORD --dashboard abc123Required: --url. --dashboard sets a default dashboard UID; Horus can also discover dashboards automatically during an investigation.
How it appears in investigations
Metric evidence includes panel titles, datasource queries, detected anomalies, and the time window covered. It is evidence, not a replacement for your Grafana UI.
Troubleshooting
| Symptom | Fix |
|---|---|
horus connect cannot reach Grafana | Confirm the URL and credentials. Check that the datasource proxy is accessible. |
| No panels match the hint | Broaden the hint or set a relevant default dashboard with --dashboard. |
| Queries return empty series | Verify the Prometheus/Loki/other datasource behind Grafana covers the service and time range. |
MongoDB state
The MongoDB connector reads application and operational state from allowlisted collections. It is intentionally scoped: you specify which databases and collections Horus can query, and Horus only runs read-only, allowlisted operations.
When to use it
- You store operational state in MongoDB that helps explain incidents: job statuses, user states, event records.
- You want to detect stale documents, anomalous status distributions, or recent spikes in collection counts.
Setup
horus connect mongodb --url mongodb://horus:password@mongodb.example.com:27017 --database my_app --collections users,orders,jobsRequired: --url and --database. --collections is optional: omit it to auto-discover and allow all collections in the database, or supply it to restrict the allowlist.
How it appears in investigations
MongoDB evidence shows collection counts, stale-document counts, and unusual status distributions. It is summarized; Horus does not dump full documents into the report.
Troubleshooting
| Symptom | Fix |
|---|---|
horus connect fails with authentication error | Check the connection string, user, password, and authSource if you use one. |
| Investigation cannot see a collection | Add the collection to --collections and re-run horus connect. |
| Counts look wrong | Verify the MongoDB user has read permission on the allowlisted collections. |
Redis / BullMQ queues
The Redis connector is used by the BullMQ provider to read queue runtime state. It reports queue depths, failed-job counts, delayed-job counts, and worker presence. It does not modify jobs, queues, or Redis keys.
When to use it
- You run BullMQ on Redis and want queue evidence in investigations.
- You suspect a backlog, worker crash, or job failure cascade.
Setup
horus connect redis --url redis://:password@redis.example.com:6379Required: --url. For authenticated Redis, embed the password in the URL as redis://:password@host:port. The CLI connect type is redis; the runtime provider is BullMQ.
How it appears in investigations
Queue evidence appears alongside source-intelligence queue stitching. You can see which queue is backing up, how many jobs failed, and whether workers are connected. Use horus queues --live for a standalone queue status view.
Troubleshooting
| Symptom | Fix |
|---|---|
horus connect cannot reach Redis | Check the URL, password, network, and TLS settings. |
| Queue evidence is missing | Confirm the queue names match between code and Redis, and that horus init has built the queue map. |
| Counts differ from BullMQ Dashboard | Horus reads the same Redis keys but may sample or summarize differently. Treat its numbers as evidence, not canonical ops metrics. |
PostgreSQL state
The PostgreSQL connector reads application and operational state from allowlisted tables. Like MongoDB, it is intentionally scoped: you specify the schema and tables Horus may query, and Horus runs only read-only, allowlisted operations.
When to use it
- You store operational or domain state in PostgreSQL: job rows, order status, user state, outbox tables.
- You want to detect stale rows, anomalous status distributions, or recent spikes in table counts.
Setup
horus connect postgres --url postgres://horus:password@db.example.com:5432/my_app --schema public --tables orders,jobs,usersRequired: --url. --schema defaults to public; --tables allowlists which tables Horus may read (omit to allow all in the schema). Run horus connect postgres with no flags for an interactive prompt.
How it appears in investigations
PostgreSQL evidence shows table counts, stale-row counts, and unusual status distributions, tagged as state. It is summarized; Horus does not dump full rows into the report.
Troubleshooting
| Symptom | Fix |
|---|---|
horus connect fails to connect | Check the connection string, credentials, SSL mode, and that the host is reachable. |
| Investigation cannot see a table | Add it to --tables (and confirm the --schema), then re-run horus connect postgres. |
| Counts look wrong | Verify the Postgres role has SELECT on the allowlisted tables. |
Sentry errors
The Sentry connector reads grouped exceptions (issues) from your Sentry project and turns them into error evidence. Each issue is summarized with its count and time range, and seeded with the top in-app stack frame so Horus can point directly at the implicated code.
When to use it
- You track exceptions in Sentry and want them correlated with code and other evidence during an investigation.
- You want a direct jump from a production error to the function and file most likely responsible.
Setup
horus connect sentry --org my-org --sentry-project my-project --auth-token $SENTRY_AUTH_TOKENRequired: --org, --sentry-project, and --auth-token. For self-hosted Sentry, pass --url https://sentry.example.com. Use a read-only auth token. Running horus connect sentry with no flags prompts interactively.
How it appears in investigations
Sentry evidence is tagged logs (kind log) — the same source tier as Elasticsearch and Axiom — carrying the group's event count, first/last seen, and a code seed from the top in-app frame. It is evidence, not a replacement for the Sentry UI.
Troubleshooting
| Symptom | Fix |
|---|---|
horus connect returns 401 or 403 | Check the auth token scope (issues and events read) and that the org and project slugs are correct. |
| No issues appear | Confirm the project slug and that the incident window overlaps recent Sentry activity. |
| Self-hosted Sentry not reachable | Pass --url with your Sentry base URL and verify network access. |
Axiom logs
The Axiom connector reads structured logs from an Axiom dataset and turns them into log evidence. It is incident-aware: rather than dumping the freshest lines, it queries the top error signatures over the window — grouped by message and level, highest-volume first, with occurrence counts — alongside a few recent raw error rows for grounding. When no error-level rows match, it falls back to a recent all-levels sample so non-error datasets still produce evidence.
Requires CLI 0.11.0 or newer
The Axiom connector shipped in Horus CLI 0.11.0. Run horus --version and update if needed.
When to use it
- You ship application logs to Axiom and want error counts and signatures correlated with code during an investigation.
- You want a high-volume error storm surfaced as one ranked signature with its real count, not buried under newer info lines.
- You query Axiom with APL today and want the same datasets folded into Horus evidence.
Setup
Run horus connect axiom with no flags for the interactive flow: it prompts for your API token (hidden input), lets you pick the region — US (api.axiom.co) or EU (api.eu.axiom.co), or a custom base URL — then lists your datasets live so you can select one (discover-then-select). If the dataset list cannot be fetched, you can type the name manually.
# Interactive: token (hidden) -> region -> pick a dataset from the live list
horus connect axiom
# Non-interactive
horus connect axiom --token $AXIOM_TOKEN --dataset my-logs
horus connect axiom --token $AXIOM_TOKEN --dataset my-logs --url https://api.eu.axiom.coRequired: --token and --dataset. --url selects the region or a self-hosted base URL and defaults to the US region (https://api.axiom.co); only a non-default URL is persisted to .horus/config.json. Supply the token via an environment variable rather than committing it.
How it appears in investigations
Axiom evidence is tagged logs. Each row becomes one log evidence item with its fields on the payload, a redacted message in the title, and provenance (the dataset query plus a collection timestamp). High-volume error signatures carry their occurrence count and latest-seen time in the title, and relevance is weighted by hint-term match, recency, and log level.
Troubleshooting
| Symptom | Fix |
|---|---|
horus connect cannot list datasets | Check the token, network, and region. You can also enter the dataset name manually, or pass --dataset directly. |
| No log evidence appears | Confirm the dataset name and that the incident window overlaps recent events. Verify the region with --url (EU is https://api.eu.axiom.co). |
horus connect axiom is not recognized | Update the CLI — the Axiom connector requires version 0.11.0 or newer. |
Shopify Admin state
The Shopify connector reads admin and business data — orders, inventory, fulfillment, customers, and more — from the Shopify Admin GraphQL API and folds each query result into an investigation as state evidence. It embeds no queries of its own: you supply the GraphQL document, either with --shopify-query at investigate time or as named default queries in .horus/config.json that horus watch runs each cycle. It is strictly read-only.
Requires CLI 0.19.0 or newer
The Shopify Admin connector shipped in Horus CLI 0.19.0. Run horus --version and update if needed.
When to use it
- You run a Shopify store and want admin state — order, fulfillment, and inventory counts and statuses — correlated with code and other evidence.
- You want
horus watchto poll named Shopify queries each cycle and fold the results in as evidence.
Setup
Shopify supports two auth models: a static Admin API access token (a Custom App shpat_… token used directly, with no access id), or a client-credentials app (client_id + client_secret, exchanged for a short-lived token that Horus caches and refreshes on 401).
# Static Admin API access token
horus connect shopify --store acme-store --secret $SHOPIFY_TOKEN
# Client-credentials app
horus connect shopify --store acme-store --access-id $SHOPIFY_CLIENT_ID --secret $SHOPIFY_CLIENT_SECRETRequired: --store and --secret. --store is the subdomain only (acme-store); Horus appends .myshopify.com automatically, and a full domain is also accepted. --access-id is the app client_id — omit it for a static token. --api-version defaults to 2025-10. The secret is encrypted at rest into .horus/secrets.local.json, never config.json. Running horus connect shopify with no flags prompts interactively and asks which auth model to use.
Supplying queries
At investigate time, pass your own read-only Admin GraphQL document: --shopify-query is repeatable and accepts @file, - (stdin), or a raw GraphQL string; --shopify-variables (@file or inline JSON object) applies to every query. $from and $to in the query are auto-bound to the investigation window.
horus investigate "orders stuck unfulfilled" \
--shopify-query @orders.graphql \
--shopify-variables '{"status":"OPEN"}'For horus watch, declare default queries under connectors.shopify.queries[] in .horus/config.json. Each entry has a name, a verbatim read-only query, optional variables, a kind (state | log | metric, default state), a relevance (0–1), and bindWindow to auto-bind the investigation window.
How it appears in investigations
Each query result becomes one evidence item, tagged state by default (a query may map to logs or metrics via its kind). The title reads Shopify <store> <query name>: N results · <time> (redacted), the GraphQL data rides on the payload, and provenance is shopify <store> <query name>. Relevance seeds at 0.6 (or the query's relevance), gains +0.2 on a hint-term match, and is clamped to 0.5–0.95. A configured Shopify connector with no queries contributes nothing — that is negative evidence, not a gap.
Troubleshooting
| Symptom | Fix |
|---|---|
| Token exchange returns 401 | Client-credentials: check --access-id/--secret and that the app is installed with the read scopes it needs. Static token: confirm the shpat_… token is valid and unexpired. |
| No Shopify evidence appears | Supply at least one query — --shopify-query at investigate time, or connectors.shopify.queries[] for horus watch. With no query the connector returns nothing. |
| Requests are slow or rate-limited | The client bounds every request to 8s and retries HTTP 429 and GraphQL THROTTLED responses with backoff; reduce query scope if throttling persists. |
How connector evidence appears together
During horus investigate, Horus asks every configured connector for evidence related to the hint. Each piece is tagged by source: logs (Elasticsearch, Axiom, Sentry), metrics (Grafana), state (MongoDB, PostgreSQL, Shopify), queue (BullMQ), and history (git). The report surfaces only the evidence that is relevant and non-empty.
Connector evidence is combined with source-intelligence evidence and git evidence. The scoring engine ranks causes based on the full set; no single source overrides another.
Security and permissions
- All connectors are read-only.
- Use dedicated read-only users where possible.
- Connector credentials are AES-256-GCM encrypted at rest into
.horus/secrets.local.json(neverconfig.json), unlocked by a master key held by your OS.horus connectauto-adds.horus/to.gitignore; manage the store withhorus secrets status|migrate|key. - MongoDB collections and PostgreSQL tables can be allowlisted with
--collections/--tables; an empty allowlist auto-discovers all. It is a scoping option, not an enforced boundary. - The Redis connection URL (including any embedded password) is itself an encrypted secret at rest.
- Shopify uses a
client_secretor static Admin API token, encrypted at rest; env fallbacks areSHOPIFY_ACCESS_ID/SHOPIFY_SECRET. - Sentry uses a read-only auth token; supply it via an environment variable rather than committing it.
- Axiom uses an API token; supply it via an environment variable rather than committing it.
What is not a runtime connector
These capabilities are handled differently and are not configured through horus connect:
- Source intelligence — configured via
horus init(use--sourceto point at an external host). - Git evidence — read from the local repository; no URL or credentials needed.
- Distributed tracing — Horus can correlate by trace ID when present in logs, but it does not ingest trace spans directly today.
Next steps
See Getting started for the first-run workflow, Source intelligence for code context, and CLI reference for every horus connect option.