Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.26.1] - 2026-06-16

### Fixed

- **Canonical-rename stragglers** — fixed user-facing strings that still referenced the pre-rename flag/arg vocabulary after the 1.26.0 canonical rename: `file-tail add` usage and required-flag error (`--hostname`→`--host`), the `ai incident-context` required-flag errors (`--from`/`--to`→`--since`/`--until`), the search/abuse/ask-history "scan capped" hints (`--from`/`--to`→`--since`/`--until`), the `file_tails op=add` admin help parameter (`hostname`→`host`), and the `similar_incidents` MCP input example (`app_name`→`app`). Also corrected the `similar_incidents` doc "Response fields" line back to the unchanged output names (`hostname`/`app_name`), matching the actual response contract.

## [1.26.0] - 2026-06-16
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Added

- **Shell completion** — `cortex completions zsh` installs a tab-completion function that completes command names (with descriptions), per-command flags, and **live values** (hostnames, apps, source IDs pulled from the DB, cached ~60s with a 150 ms timeout that degrades silently). Driven by a hidden `cortex __complete` command and the single `ACTION_SPECS` registry.
- **Per-command examples in help** — `cortex <command> --help` now shows copy-paste examples sourced from `ACTION_SPECS`, kept in lockstep with the canonical flags.

### Changed

- **Canonical flag/argument vocabulary (breaking)** — unified the CLI flags and MCP tool-argument names so they match everywhere: `--hostname`→`--host`, `--source-ip`→`--source`, `--app-name`→`--app`, `--from`→`--since`, `--to`→`--until`, `--received-from`→`--received-since`, `--received-to`→`--received-until` (with `-s`/`-n` short forms for severity/limit). The rename spans every CLI command, the MCP tool arguments (the request-arg domain fields were renamed so the wire key = field name, no serde aliases), help text, the bundled skills, and docs. Response/output field names (e.g. a log entry's `hostname`/`source_ip`) are unchanged — the output contract is unaffected.
- `ACTION_SPECS` now carries per-action flag and example metadata; the CLI parser, completion, and help all derive from it.

## [1.25.1] - 2026-06-16

### Added
Expand Down Expand Up @@ -2376,7 +2394,10 @@ start and verify with `cortex --http db status`.

---

[Unreleased]: https://github.com/jmagar/cortex/compare/v1.20.0...HEAD
[Unreleased]: https://github.com/jmagar/cortex/compare/v1.26.1...HEAD
[1.26.1]: https://github.com/jmagar/cortex/compare/v1.26.0...v1.26.1
[1.26.0]: https://github.com/jmagar/cortex/compare/v1.25.1...v1.26.0
[1.25.1]: https://github.com/jmagar/cortex/compare/v1.20.0...v1.25.1
[1.20.0]: https://github.com/jmagar/cortex/compare/v1.19.0...v1.20.0
[1.14.0]: https://github.com/jmagar/cortex/compare/v1.13.3...v1.14.0
[1.13.3]: https://github.com/jmagar/cortex/compare/v1.13.2...v1.13.3
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cortex"
version = "1.25.1"
version = "1.26.1"
edition = "2024"
rust-version = "1.86"
license = "MIT"
Expand Down Expand Up @@ -42,7 +42,7 @@ rmcp = { version = "1.7", default-features = false, features = [
tower-http = { version = "0.6", features = ["cors", "limit", "trace"] }

# SQLite
rusqlite = { version = "0.39", features = ["bundled", "vtab", "backup"] }
rusqlite = { version = "0.39", features = ["bundled", "vtab", "backup", "hooks"] }
parking_lot = "0.12"
r2d2 = "0.8"
r2d2_sqlite = "0.33"
Expand Down
50 changes: 25 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,12 @@ Full-text search across all syslog messages with optional filters. Uses SQLite F
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `query` | string | no | — | FTS5 search query (see [FTS5 query syntax](#fts5-query-syntax)) |
| `hostname` | string | no | — | Exact hostname match. Use `cortex` with `action: "hosts"` to enumerate. |
| `source_ip` | string | no | — | Exact source identifier. Syslog entries use the verified network sender address (`IP:port`); OTLP rows use the verified peer IP; Docker ingest stream rows use `docker://host/container/stream`; Docker lifecycle event rows use `docker-event://host/container/action`. |
| `host` | string | no | — | Exact hostname match. Use `cortex` with `action: "hosts"` to enumerate. |
| `source` | string | no | — | Exact source identifier. Syslog entries use the verified network sender address (`IP:port`); OTLP rows use the verified peer IP; Docker ingest stream rows use `docker://host/container/stream`; Docker lifecycle event rows use `docker-event://host/container/action`. |
| `severity` | string | no | — | One of: `emerg alert crit err warning notice info debug` |
| `app_name` | string | no | — | Application name, e.g. `sshd`, `dockerd`, `kernel` |
| `from` | string | no | — | Start of time range (ISO 8601 / RFC 3339, e.g. `2025-01-15T00:00:00Z`) |
| `to` | string | no | — | End of time range (ISO 8601) |
| `app` | string | no | — | Application name, e.g. `sshd`, `dockerd`, `kernel` |
| `since` | string | no | — | Start of time range (relative like `1h`/`yesterday`, or ISO 8601 / RFC 3339, e.g. `2025-01-15T00:00:00Z`) |
| `until` | string | no | — | End of time range (relative or ISO 8601) |
| `limit` | integer | no | 100 | Max results (hard cap: 1000) |

**Response**
Expand Down Expand Up @@ -235,7 +235,7 @@ query: "restart*" # matches restart, restarted, restarting

Structured filter-only retrieval for correlation workflows. This action rejects `query`; use `search` for FTS5 message-body search.

Common filters match `search`: `hostname`, `source_ip`, `severity`, `app_name`, `facility`, `exclude_facility`, `process_id`, `from`, `to`, `received_from`, `received_to`, and `limit`.
Common filters match `search`: `host`, `source`, `severity`, `app`, `facility`, `exclude_facility`, `process_id`, `since`, `until`, `received_since`, `received_until`, and `limit`.

Correlation aliases include `source_kind` (`docker-stream`, `docker-event`, `agent-command`, `shell-history`, `transcript`, `claude`, `codex`, `gemini`), plus `tool`, `project`, `session_id`, `container`, `docker_host`, `stream`, and `event_action`.
`source_kind=file-tail` filters managed file-tail rows (`source_ip` prefix `file-tail://`).
Expand All @@ -250,9 +250,9 @@ Return the N most recent log entries. Equivalent to `tail -f` across all hosts.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `hostname` | string | no | — | Filter to a specific host |
| `source_ip` | string | no | — | Filter to an exact source identifier. Syslog entries use the verified network sender address (`IP:port`); OTLP rows use the verified peer IP; Docker ingest stream rows use `docker://host/container/stream`; Docker lifecycle event rows use `docker-event://host/container/action`. |
| `app_name` | string | no | — | Filter to a specific application |
| `host` | string | no | — | Filter to a specific host |
| `source` | string | no | — | Filter to an exact source identifier. Syslog entries use the verified network sender address (`IP:port`); OTLP rows use the verified peer IP; Docker ingest stream rows use `docker://host/container/stream`; Docker lifecycle event rows use `docker-event://host/container/action`. |
| `app` | string | no | — | Filter to a specific application |
| `n` | integer | no | 50 | Number of recent entries (hard cap: 500) |

**Response**
Expand All @@ -269,8 +269,8 @@ Summarize warnings and errors across all hosts in a time window. Groups by hostn

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `from` | string | no | all time | Start of time range (ISO 8601) |
| `to` | string | no | now | End of time range (ISO 8601) |
| `since` | string | no | all time | Start of time range (ISO 8601) |
| `until` | string | no | now | End of time range (ISO 8601) |

Severities included: `emerg`, `alert`, `crit`, `err`, `warning`.

Expand Down Expand Up @@ -321,9 +321,9 @@ List AI transcript sessions grouped by project, tool, session, and host.
|-----------|------|----------|---------|-------------|
| `project` | string | no | — | Exact project path, e.g. `/home/jmagar/workspace/cortex` |
| `tool` | string | no | — | AI tool filter: `claude`, `codex`, or `gemini` |
| `hostname` | string | no | — | Restrict to one host |
| `from` | string | no | — | Start of time range (ISO 8601) |
| `to` | string | no | — | End of time range (ISO 8601) |
| `host` | string | no | — | Restrict to one host |
| `since` | string | no | — | Start of time range (ISO 8601) |
| `until` | string | no | — | End of time range (ISO 8601) |
| `limit` | integer | no | 100 | Max sessions (hard cap: 1000) |

**Response**
Expand Down Expand Up @@ -358,8 +358,8 @@ Search for related events across multiple hosts within a ±N minute window aroun
| `reference_time` | string | **yes** | — | Center timestamp (ISO 8601, e.g. `2025-01-15T14:30:00Z`) |
| `window_minutes` | integer | no | 5 | Minutes before and after `reference_time` (max 60) |
| `severity_min` | string | no | `warning` | Minimum severity to include. `warning` returns `warning/err/crit/alert/emerg`. `debug` returns everything. |
| `hostname` | string | no | — | Limit correlation to one host |
| `source_ip` | string | no | — | Limit correlation to an exact source identifier. Syslog entries use the verified network sender address (`IP:port`); OTLP rows use the verified peer IP; Docker ingest stream rows use `docker://host/container/stream`; Docker lifecycle event rows use `docker-event://host/container/action`. |
| `host` | string | no | — | Limit correlation to one host |
| `source` | string | no | — | Limit correlation to an exact source identifier. Syslog entries use the verified network sender address (`IP:port`); OTLP rows use the verified peer IP; Docker ingest stream rows use `docker://host/container/stream`; Docker lifecycle event rows use `docker-event://host/container/action`. |
| `query` | string | no | — | FTS5 query to narrow results |
| `limit` | integer | no | 500 | Max total events (hard cap: 999) |

Expand Down Expand Up @@ -832,19 +832,19 @@ mounted and reviewed broader read-only roots such as `/var/log` or `/logs`.
```bash
cortex file-tail add --id swag-access \
--path /file-tail-root/swag/log/nginx/access.log \
--tag swag-access --hostname squirts --facility local4
--tag swag-access --host squirts --facility local4
cortex file-tail add --id swag-error \
--path /file-tail-root/swag/log/nginx/error.log \
--tag swag-error --hostname squirts --facility local4 --severity warning
--tag swag-error --host squirts --facility local4 --severity warning
cortex file-tail add --id fail2ban \
--path /file-tail-root/swag/log/fail2ban/fail2ban.log \
--tag fail2ban --hostname squirts --facility local5
--tag fail2ban --host squirts --facility local5
cortex file-tail add --id authelia \
--path /file-tail-root/authelia/logs/authelia.log \
--tag authelia --hostname squirts --facility local5
--tag authelia --host squirts --facility local5
cortex file-tail add --id adguard-query \
--path /file-tail-root/adguard/var/data/querylog.json \
--tag adguard-query --hostname squirts --facility local6
--tag adguard-query --host squirts --facility local6
```

The default starts at EOF. Add `--from-start` only when you intentionally want
Expand Down Expand Up @@ -978,9 +978,9 @@ Both modes use the same config and environment variable loader. `cortex mcp` is
The direct CLI uses the same shared service layer as the MCP tool, so results and validation match the MCP actions without needing an MCP client:

```bash
cortex search 'error AND nginx' --hostname proxy --limit 10
cortex tail -n 20 --app-name kernel
cortex errors --from 2026-01-01T00:00:00Z
cortex search 'error AND nginx' --host proxy --limit 10
cortex tail -n 20 --app kernel
cortex errors --since 2026-01-01T00:00:00Z
cortex hosts
cortex correlate --reference-time 2026-01-01T12:00:00Z --window-minutes 10 --severity-min warning
cortex entity host tootie
Expand All @@ -1001,7 +1001,7 @@ cortex clock-skew --since 2026-05-20T00:00:00Z
cortex anomalies --recent-minutes 30 --baseline-minutes 720
cortex compare --a-from 2026-05-20T00:00:00Z --a-to 2026-05-20T23:59:59Z \
--b-from 2026-05-21T00:00:00Z --b-to 2026-05-21T23:59:59Z
cortex apps --hostname dookie --limit 50
cortex apps --host dookie --limit 50
```

### REST endpoints (2026-05-22 surface parity)
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ services:
# Default tag is kept in sync by scripts/bump-version.sh (version canon);
# previously this was frozen at 1.0.0 while migrations moved forward —
# a stale binary against a newer schema (full-review OH1).
image: ghcr.io/jmagar/cortex:${CORTEX_VERSION:-1.25.1}
image: ghcr.io/jmagar/cortex:${CORTEX_VERSION:-1.26.1}
container_name: cortex
user: "${CORTEX_UID:-1000}:${CORTEX_GID:-1000}"
env_file:
Expand Down
50 changes: 25 additions & 25 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ configured database in `file-tails.json` and reconciled by the running
```bash
cortex file-tail list [--json]
cortex file-tail status [--json]
cortex file-tail add --id ID --path PATH --tag TAG --hostname HOST [--facility FACILITY] [--severity SEVERITY] [--from-start] [--json]
cortex file-tail add --id ID --path PATH --tag TAG --host HOST [--facility FACILITY] [--severity SEVERITY] [--from-start] [--json]
cortex file-tail remove --id ID [--json]
cortex file-tail enable --id ID [--json]
cortex file-tail disable --id ID [--json]
Expand Down Expand Up @@ -67,21 +67,21 @@ cortex search 'error AND nginx' --limit 5 --json
Search logs with optional FTS5 query and filters.

```bash
cortex search 'error AND nginx' --hostname proxy --limit 10
cortex search '"disk full"' --source-ip 10.0.0.5:514 --from 2026-01-01T00:00:00Z
cortex search 'error AND nginx' --host proxy --limit 10
cortex search '"disk full"' --source 10.0.0.5:514 --since 2026-01-01T00:00:00Z
```

Flags:

| Flag | Description |
| --- | --- |
| positional query | Optional SQLite FTS5 query. Multiple words are joined with spaces. |
| `--hostname HOST` | Exact claimed hostname filter |
| `--source-ip SOURCE` | Exact source identifier filter |
| `--host HOST` | Exact claimed hostname filter |
| `--source SOURCE` | Exact source identifier filter |
| `--severity LEVEL` | Syslog severity filter: `emerg`, `alert`, `crit`, `err`, `warning`, `notice`, `info`, `debug` |
| `--app-name APP` | Application/process name filter |
| `--from TIME` | RFC3339 start timestamp |
| `--to TIME` | RFC3339 end timestamp |
| `--app APP` | Application/process name filter |
| `--since TIME` | RFC3339 start timestamp |
| `--until TIME` | RFC3339 end timestamp |
| `--limit N` | Maximum returned rows |
| `--json` | Print JSON response |

Expand All @@ -91,7 +91,7 @@ Return recent log entries, optionally filtered by host, source, or app.

```bash
cortex tail -n 20
cortex tail 50 --hostname nas --app-name kernel
cortex tail 50 --host nas --app kernel
```

Flags:
Expand All @@ -100,9 +100,9 @@ Flags:
| --- | --- |
| positional `N` | Number of rows to return |
| `-n N`, `--n N` | Number of rows to return |
| `--hostname HOST` | Exact claimed hostname filter |
| `--source-ip SOURCE` | Exact source identifier filter |
| `--app-name APP` | Application/process name filter |
| `--host HOST` | Exact claimed hostname filter |
| `--source SOURCE` | Exact source identifier filter |
| `--app APP` | Application/process name filter |
| `--json` | Print JSON response |

### `cortex errors`
Expand All @@ -111,15 +111,15 @@ Summarize error and warning counts by host and severity.

```bash
cortex errors
cortex errors --from 2026-01-01T00:00:00Z --to 2026-01-02T00:00:00Z --json
cortex errors --since 2026-01-01T00:00:00Z --until 2026-01-02T00:00:00Z --json
```

Flags:

| Flag | Description |
| --- | --- |
| `--from TIME` | RFC3339 start timestamp |
| `--to TIME` | RFC3339 end timestamp |
| `--since TIME` | RFC3339 start timestamp |
| `--until TIME` | RFC3339 end timestamp |
| `--json` | Print JSON response |

### `cortex hosts`
Expand Down Expand Up @@ -166,9 +166,9 @@ Flags:
| --- | --- |
| `--project PATH` | Exact project path filter |
| `--tool TOOL` | AI tool filter: `claude`, `codex`, or `gemini` |
| `--hostname HOST` | Filter by host |
| `--from TIME` | RFC3339 start timestamp |
| `--to TIME` | RFC3339 end timestamp |
| `--host HOST` | Filter by host |
| `--since TIME` | RFC3339 start timestamp |
| `--until TIME` | RFC3339 end timestamp |
| `--limit N` | Maximum returned rows |
| `--json` | Print JSON response |

Expand All @@ -183,8 +183,8 @@ cortex ai search authentication --tool claude --limit 10
Human output now states that grouping is computed over the newest matching
candidate window. JSON includes `total_candidates`, `candidate_rows`,
`candidate_cap`, `candidate_window_truncated`, and `truncated`; when the
candidate window is truncated, narrow with `--project`, `--tool`, `--from`, or
`--to` for exact grouping within that filter.
candidate window is truncated, narrow with `--project`, `--tool`, `--since`, or
`--until` for exact grouping within that filter.

### `cortex ai abuse`

Expand Down Expand Up @@ -251,7 +251,7 @@ Bucket AI activity into 5-hour UTC windows.
cortex ai blocks --project /home/jmagar/workspace/cortex
```

When `--from` is omitted, usage blocks default to the last 30 days. Returned
When `--since` is omitted, usage blocks default to the last 30 days. Returned
JSON includes `total_blocks` and `truncated`; at most 1000 buckets are returned.

### `cortex ai context`
Expand Down Expand Up @@ -675,8 +675,8 @@ Flags:
| `--reference-time TIME` | RFC3339 center timestamp |
| `--window-minutes N` | Minutes before and after the reference time |
| `--severity-min LEVEL` | Minimum severity to include |
| `--hostname HOST` | Exact claimed hostname filter |
| `--source-ip SOURCE` | Exact source identifier filter |
| `--host HOST` | Exact claimed hostname filter |
| `--source SOURCE` | Exact source identifier filter |
| `--query FTS` | Optional FTS5 query |
| `--limit N` | Maximum total events |
| `--json` | Print JSON response |
Expand All @@ -686,7 +686,7 @@ Flags:
Return the latest bounded heartbeat state for one host.

```bash
cortex host-state --hostname tootie
cortex host-state --host tootie
cortex host-state --host-id host-a --limit 5 --json
```

Expand All @@ -695,7 +695,7 @@ Flags:
| Flag | Description |
| --- | --- |
| `--host-id ID` | Authoritative heartbeat host identity |
| `--hostname HOST` | Self-reported hostname fallback (must resolve to one host) |
| `--host HOST` | Self-reported hostname fallback (must resolve to one host) |
| `--since TIME` | Minimum `sampled_at` timestamp (ISO 8601) |
| `--limit N` | Number of samples (default 1, max 100) |
| `--json` | Print JSON response |
Expand Down
10 changes: 5 additions & 5 deletions docs/CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,36 +150,36 @@ cortex file-tail add \
--id swag-access \
--path /file-tail-root/swag/log/nginx/access.log \
--tag swag-access \
--hostname squirts \
--host squirts \
--facility local4

cortex file-tail add \
--id swag-error \
--path /file-tail-root/swag/log/nginx/error.log \
--tag swag-error \
--hostname squirts \
--host squirts \
--facility local4 \
--severity warning

cortex file-tail add \
--id fail2ban \
--path /file-tail-root/swag/log/fail2ban/fail2ban.log \
--tag fail2ban \
--hostname squirts \
--host squirts \
--facility local5

cortex file-tail add \
--id authelia \
--path /file-tail-root/authelia/logs/authelia.log \
--tag authelia \
--hostname squirts \
--host squirts \
--facility local5

cortex file-tail add \
--id adguard-query \
--path /file-tail-root/adguard/var/data/querylog.json \
--tag adguard-query \
--hostname squirts \
--host squirts \
--facility local6
```

Expand Down
2 changes: 1 addition & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ operators can opt out of HTTP transport for the duration of the script:
```bash
( unset CORTEX_USE_HTTP; \
for h in $(cortex hosts --json | jq -r '.hosts[].hostname'); do \
cortex tail --hostname "$h" --n 50; \
cortex tail --host "$h" --n 50; \
done )
```

Expand Down
Loading
Loading