Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ jobs:
- name: Check version sync
run: bash scripts/check-version-sync.sh

- name: Check plugin manifest version policy
run: bash scripts/check-plugin-manifest-versions.sh

- name: Check agent memory symlinks
run: bash scripts/check-agent-memory-symlinks.sh

- name: Check public identity strings
run: bash scripts/check-public-identity.sh

fmt:
name: Formatting
runs-on: ubuntu-latest
Expand All @@ -45,7 +54,7 @@ jobs:
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2

- name: Clippy
run: cargo clippy -- -D warnings
run: cargo clippy --all-targets -- -D warnings

test:
name: Tests
Expand Down
313 changes: 0 additions & 313 deletions AGENTS.md

This file was deleted.

1 change: 1 addition & 0 deletions AGENTS.md
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.14.2] - 2026-06-07

### Fixed

- Shortened inventory graph projection write-lock scope by planning expensive
projection work in memory before applying graph mutations, and avoided the
redundant graph count query on successful projection.
- Hardened SSH-backed inventory and drop-in deploy flows with shared host
validation, strict host key checking by default, explicit TOFU opt-in, and a
shared SSH argument policy. Inventory collectors and remote Docker event
streams also share bounded concurrency and retry backoff.
- Aligned MCP argument parsing with typed request structs so unknown fields are
rejected consistently with HTTP validation, and moved action dispatch behind
the action registry.
- Moved expensive log pattern clustering out of the DB closure, bound SQL
limits instead of interpolating them on touched query paths, and reused the
freshly collected inventory snapshot for graph projection.

### Changed

- Rebranded current docs, plugin docs, schema metadata, mcporter examples, and
release guidance around the `cortex` tool name, `cortex:*` scopes, and the
current plugin/package naming.
- Clarified current-versus-archive documentation authority, release/version
policy, live smoke gates, security trust assumptions, OAuth non-Unix behavior,
cargo-deny exception ownership, and rmcp lower-bound intent.
- Made `CLAUDE.md` the agent-memory source of truth and restored sibling
`AGENTS.md` symlinks wherever local `CLAUDE.md` files exist.

### CI

- Ran clippy with `--all-targets` and added checks for agent-memory symlinks,
unversioned plugin manifests, and stale public project identity strings.

## [1.14.1] - 2026-06-06

### Fixed
Expand Down
482 changes: 253 additions & 229 deletions CLAUDE.md

Large diffs are not rendered by default.

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.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[package]
name = "cortex"
version = "1.14.1"
version = "1.14.2"
edition = "2021"
rust-version = "1.86"
license = "MIT"
description = "Homelab intelligence platform — syslog/OTLP/Docker log aggregation, fleet awareness, and AI agent coordination over MCP, CLI, and HTTP"
autobins = false

Expand All @@ -17,6 +18,8 @@ tokio-util = { version = "0.7", features = ["rt"] }

# HTTP / MCP transport
axum = "0.8"
# rmcp 1.6.0 is the supported lower bound for cortex's current API surface.
# Cargo.lock may resolve a newer compatible 1.x release; see docs/RUST.md.
rmcp = { version = "1.6.0", default-features = false, features = [
"server",
"macros",
Expand Down
32 changes: 17 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ separate table:
```bash
cortex shell index --path ~/.zsh_history --shell zsh
cortex setup agent-command install
export CLAUDE_CODE_SHELL_PREFIX="$HOME/.local/bin/syslog-agent-command-wrapper"
export CLAUDE_CODE_SHELL_PREFIX="$HOME/.local/bin/cortex-agent-command-wrapper"
cortex agent-command ingest-spool --path ~/.local/state/cortex/agent-command.jsonl
```

Expand Down Expand Up @@ -564,16 +564,17 @@ The installer puts the host `cortex` binary in `~/.local/bin` and then runs
- `~/.cortex/compose/docker-compose.yml` — Docker Compose deployment assets
- `~/.cortex/data/cortex.db` — SQLite database and WAL/SHM sidecars

Setup writes `COMPOSE_PROJECT_NAME=syslog-jmagar-lab` so direct
`docker compose` commands in `~/.cortex/compose` target the same canonical
container as `cortex compose`.
Setup writes the compose project name used by the shared host deployment.
Existing installations may still use the legacy `syslog-jmagar-lab` project
name for container-label compatibility; prefer `cortex compose ...` commands
because they resolve the live owner before mutating the stack.

Useful installer controls:

```bash
CORTEX_INSTALL_DRY_RUN=1 ./install.sh
CORTEX_INSTALL_PREFIX=/opt/cortex ./install.sh
CORTEX_VERSION=0.25.4 ./install.sh
CORTEX_VERSION=<version> ./install.sh
CORTEX_INSTALL_SKIP_SETUP=1 ./install.sh
```

Expand Down Expand Up @@ -609,7 +610,7 @@ Install as a Claude Code plugin. The plugin handles deployment automatically —
| `batch_size` | no | `100` | Number of parsed messages per SQLite batch |
| `write_channel_capacity` | no | `10000` | Internal parsed-message queue capacity before listener backpressure |
| `docker_ingest_enabled` | no | `false` | Pull container logs from remote `docker-socket-proxy` endpoints |
| `fleet_hosts` | no | — | SSH aliases of fleet hosts. Used for Docker ingest (when enabled, each becomes `http://<alias>:2375`) and the `syslog-deploy-dropins` skill |
| `fleet_hosts` | no | — | SSH aliases of fleet hosts. Used for Docker ingest (when enabled, each becomes `http://<alias>:2375`) and the `cortex-deploy-dropins` skill |

**SessionStart hook automation** (in server mode):

Expand All @@ -621,11 +622,11 @@ Install as a Claude Code plugin. The plugin handles deployment automatically —

**Bundled skills**:

- `syslog-dr` — health check covering MCP, service status, syslog port, fleet drop-ins, and live log flow; tails service logs on failure
- `syslog-deploy-dropins` — SSH-based one-shot rsyslog drop-in deployment to every host in `fleet_hosts`
- `syslog-redeploy` — re-run plugin setup after config or plugin changes
- `syslog-logs` — Docker Compose service log tailing
- `syslog-version-check` — check whether the running Docker container matches the local Compose image; add `--pull` to pull first, otherwise checks only the local image cache
- `cortex-dr` — health check covering MCP, service status, syslog port, fleet drop-ins, and live log flow; tails service logs on failure
- `cortex-deploy-dropins` — SSH-based one-shot rsyslog drop-in deployment to every host in `fleet_hosts`
- `cortex-redeploy` — re-run plugin setup after config or plugin changes
- `cortex-logs` — Docker Compose service log tailing
- `cortex-version-check` — check whether the running Docker container matches the local Compose image; add `--pull` to pull first, otherwise checks only the local image cache

The plugin deploys the server with Docker Compose through the same `cortex setup`
path as the one-line installer. You can still build and run the binary locally
Expand Down Expand Up @@ -838,10 +839,10 @@ The MCP query API (port 3100, default loopback) supports two auth modes:

| Mode | Config | Effect |
|------|--------|--------|
| Bearer token | `CORTEX_TOKEN=<token>` | Static token grants `syslog:read` by default; set `CORTEX_STATIC_TOKEN_ADMIN=true` to also grant `syslog:admin` |
| Bearer token | `CORTEX_TOKEN=<token>` | Static token grants `cortex:read` by default; set `CORTEX_STATIC_TOKEN_ADMIN=true` to also grant `cortex:admin` |
| Google OAuth | `CORTEX_AUTH_MODE=oauth` | OAuth users authenticated via `CORTEX_AUTH_ADMIN_EMAIL` |

**Important**: Admin actions such as `ack_error`, `unack_error`, and `notifications_test` require `syslog:admin`. Static bearer tokens are read-only unless `CORTEX_STATIC_TOKEN_ADMIN=true` is explicitly set.
**Important**: Admin actions such as `ack_error`, `unack_error`, and `notifications_test` require `cortex:admin`. Static bearer tokens are read-only unless `CORTEX_STATIC_TOKEN_ADMIN=true` is explicitly set.

The MCP port defaults to `127.0.0.1:3100` (loopback only). To expose it on a network interface, set `CORTEX_HOST=0.0.0.0` and configure a TLS-terminating reverse proxy in front of it.

Expand Down Expand Up @@ -1084,7 +1085,8 @@ Before upgrading a populated database:
3. Start the new version and monitor logs for `Migration N: starting ...` and `Migration N: ... created`.
4. Keep the previous image or binary available until `/health` returns `ok` and `cortex stats` reports sane counts.

See [docs/runbooks/deploy.md](docs/runbooks/deploy.md) for the deploy checklist.
See [docs/RELEASE.md](docs/RELEASE.md) for the current release and deploy
gate checklist.

---

Expand Down Expand Up @@ -1285,7 +1287,7 @@ Stdio mode does not use bearer auth because it is local child-process access. It
{
"mcpServers": {
"cortex": {
"command": "/path/to/syslog",
"command": "/path/to/cortex",
"args": ["mcp"],
"env": {
"CORTEX_DB_PATH": "/data/cortex.db",
Expand Down
2 changes: 1 addition & 1 deletion config/mcporter.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"mcpServers": {
"syslog": {
"cortex": {
"url": "http://localhost:3100/mcp",
"transport": "http"
}
Expand Down
14 changes: 9 additions & 5 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ yanked = "deny"
ignore = [
# RUSTSEC-2023-0071 — Marvin Attack (RSA timing side-channel) in the `rsa` crate.
# Pulled in transitively: lab-auth → jsonwebtoken → rsa v0.9.10 / v0.10.0-rc.18.
# Owner: cortex maintainer. Review cadence: every release; next review no later
# than 2026-07-31 or when lab-auth/jsonwebtoken releases a hardened path.
# No upstream fix exists yet (as of 2026-05). The RSA keys are used for JWT
# signing/verification only (short-lived tokens, not long-term data encryption)
# and the server is not on the PKCS#1 v1.5 decrypt path that is vulnerable.
Expand All @@ -38,7 +40,6 @@ allow = [
"Unicode-3.0",
"CC0-1.0",
"Zlib", # foldhash (via hashbrown → rusqlite → lab-auth)
"MPL-2.0", # option-ext (via dirs-sys → dirs)
"CDLA-Permissive-2.0", # webpki-roots / webpki-root-certs (via reqwest → rustls)
]
# Confidence threshold for license text detection (0.0–1.0)
Expand All @@ -47,10 +48,13 @@ confidence-threshold = 0.8
# ── Bans ──────────────────────────────────────────────────────────────────────

[bans]
# Warn on multiple versions of the same crate — forces explicit resolution
multiple-versions = "warn"
# "warn" not "deny": git deps with a pinned rev have no semver version field,
# which cargo-deny treats as a wildcard — but the rev IS the pin.
# Duplicate versions are dominated by the transitive auth/MCP/Windows target
# stack and are reviewed during release dependency audits. Keep this check quiet
# so cargo-deny output highlights advisories, license drift, and source drift.
multiple-versions = "allow"
# Keep wildcard dependency requirements visible. Git deps are pinned by `rev`,
# and source policy below restricts remotes, but accidental broad crate specs
# should still show up during dependency audits.
wildcards = "warn"
# Allow path deps (e.g. the self-referencing dev dep) to omit a version.
allow-wildcard-paths = true
Expand Down
29 changes: 17 additions & 12 deletions docs/CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Plugin Checklist -- cortex
# Release Audit Checklist -- cortex

Pre-release and quality checklist. Complete all items before tagging a release.
Supplemental pre-release audit checklist. `docs/RELEASE.md` is the source of
truth for hermetic and live release gates.

## Version and metadata

- [ ] All version-bearing files in sync: `Cargo.toml`, `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, `gemini-extension.json`, `server.json`
- [ ] Version-bearing files in sync: `Cargo.toml`, `Cargo.lock`,
`server.json`, `mcpb/manifest.json`, and `CHANGELOG.md`
- [ ] Plugin manifests are unversioned:
`.claude-plugin/plugin.json` and `plugins/**/plugin.json`
- [ ] `CHANGELOG.md` has an entry for the new version
- [ ] README version badge is correct

Expand All @@ -18,7 +22,7 @@ Pre-release and quality checklist. Complete all items before tagging a release.

- [ ] `CLAUDE.md` is current and matches repo structure
- [ ] `README.md` has up-to-date tool reference and environment variable table
- [ ] `skills/cortex/SKILL.md` has correct frontmatter and tool descriptions
- [ ] `plugins/cortex/skills/cortex/SKILL.md` has correct frontmatter and tool descriptions
- [ ] Setup instructions work from a clean clone

## Security
Expand All @@ -34,26 +38,27 @@ Pre-release and quality checklist. Complete all items before tagging a release.

## Build and test

- [ ] Docker image builds: `just docker-build`
- [ ] Docker healthcheck passes: `just health`
- [ ] CI pipeline passes: `just lint && just test`
- [ ] Docker image builds: `docker compose build`
- [ ] Docker healthcheck passes against the intended deployment
- [ ] CI pipeline passes the hermetic gates in `docs/RELEASE.md`
- [ ] Live smoke test passes: `just test-live`
- [ ] `cargo clippy -- -D warnings` produces zero warnings
- [ ] `cargo clippy --all-targets -- -D warnings` produces zero warnings

## Deployment

- [ ] `docker-compose.yml` uses correct ports (1514 UDP/TCP, 3100 TCP)
- [ ] `entrypoint.sh` is executable
- [ ] SWAG reverse proxy config tested (see `docs/syslog.subdomain.conf`)
- [ ] `cortex compose doctor` passes before lifecycle mutations
- [ ] Reverse proxy config tested when exposing the service externally

## Registry (if publishing)

- [ ] `server.json` for MCP registry is valid JSON with correct version
- [ ] `mcpb/manifest.json` is valid JSON with matching package metadata
- [ ] OCI image published to `ghcr.io/jmagar/cortex`
- [ ] Crate published to crates.io (if applicable)
- [ ] DNS verification for `tv.tootie/cortex`

## Marketplace (if applicable)

- [ ] Entry in `claude-homelab` marketplace manifest
- [ ] Plugin installs correctly: `/plugin marketplace add jmagar/claude-homelab`
- [ ] Entry in the active plugin marketplace manifest is current
- [ ] Plugin installs correctly from the current marketplace source
20 changes: 10 additions & 10 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ spool into SQLite.

```bash
cortex setup agent-command install
export CLAUDE_CODE_SHELL_PREFIX="$HOME/.local/bin/syslog-agent-command-wrapper"
export CLAUDE_CODE_SHELL_PREFIX="$HOME/.local/bin/cortex-agent-command-wrapper"

cortex agent-command ingest-spool --path ~/.local/state/cortex/agent-command.jsonl
cortex agent-command wrap --spool ~/.local/state/cortex/agent-command.jsonl -- cargo test
Expand Down Expand Up @@ -515,7 +515,7 @@ remain blocking errors. Installing the watch service disables the older
### `cortex setup debug-wrapper`

Install, remove, or inspect the host-local debug wrapper at
`~/.local/bin/syslog`.
`~/.local/bin/cortex`.

```bash
cortex setup debug-wrapper install
Expand All @@ -524,11 +524,11 @@ cortex setup debug-wrapper remove
```

The wrapper is intentionally machine-local. It `cd`s into the configured repo
or worktree, builds `cargo build --bin syslog` into `.cache/cargo`, then execs
or worktree, builds `cargo build --bin cortex` into `.cache/cargo`, then execs
the fresh debug binary. For non-server commands it defaults Docker ingest off
and bearer auth mode on, so regular CLI checks do not accidentally start
container-log ingestion or OAuth-only config paths. Override the source checkout
with `CORTEX_REPO=/path/to/cortex syslog ...`.
with `CORTEX_REPO=/path/to/cortex cortex ...`.

### `cortex setup debug-compose`

Expand All @@ -544,10 +544,10 @@ cortex setup debug-compose remove
The override is machine-local. It points the canonical Docker Compose project at
the current repo/worktree and builds the `cortex:local-debug` image with the
debug profile. This keeps `docker compose up -d --build` aligned with the same
code that the host debug wrapper builds. `cortex setup` also writes
`COMPOSE_PROJECT_NAME=syslog-jmagar-lab` to the setup `.env`, so direct
`docker compose` commands target the canonical project instead of a cwd-derived
project name.
code that the host debug wrapper builds. Existing setup environments may still
carry the legacy `COMPOSE_PROJECT_NAME=syslog-jmagar-lab` for container-label
compatibility; use `cortex compose ...` when possible because it resolves the
live owner before mutating the stack.

### `cortex setup doctor`

Expand Down Expand Up @@ -626,7 +626,7 @@ bash scripts/smoke-ai-mcp.sh
```

The smoke scripts resolve `CORTEX_BIN` first, then `cortex` on `PATH`, then the
repo-local debug binary at `target/debug/syslog`.
repo-local debug binary at `target/debug/cortex`.

With `syslog-ai-watch.service` installed, new transcript lines usually become
searchable within a few seconds of the writer closing or flushing the file.
Expand Down Expand Up @@ -993,7 +993,7 @@ models.
The MCP-only `status` and `help` actions are runtime/protocol helpers, not
direct database queries. Compose mutations (`up`, `down`, `restart`, `pull`,
`logs`) are CLI-only and are not exposed over MCP. Admin MCP actions such as
`ack_error`, `unack_error`, and `notifications_test` require `syslog:admin`
`ack_error`, `unack_error`, and `notifications_test` require `cortex:admin`
when auth is mounted.

Use direct CLI mode for terminal queries and scripts on a host that can read the
Expand Down
Loading
Loading