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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

## [0.9.9] - 2026-02-17

### Added
- `zeph-gateway` crate: axum HTTP gateway with POST /webhook ingestion, bearer auth (blake3 + ct_eq), per-IP rate limiting, GET /health endpoint, feature-gated (`gateway`) (#379)
- `zeph-core::daemon` module: component supervisor with health monitoring, PID file management, graceful shutdown, feature-gated (`daemon`) (#380)
Expand Down Expand Up @@ -800,7 +802,8 @@ let agent = Agent::new(provider, channel, &skills_prompt, executor);
- Agent calls channel.send_typing() before each LLM request
- Agent::run() uses tokio::select! to race channel messages against shutdown signal

[Unreleased]: https://github.com/bug-ops/zeph/compare/v0.9.8...HEAD
[Unreleased]: https://github.com/bug-ops/zeph/compare/v0.9.9...HEAD
[0.9.9]: https://github.com/bug-ops/zeph/compare/v0.9.8...v0.9.9
[0.9.8]: https://github.com/bug-ops/zeph/compare/v0.9.7...v0.9.8
[0.9.7]: https://github.com/bug-ops/zeph/compare/v0.9.6...v0.9.7
[0.9.6]: https://github.com/bug-ops/zeph/compare/v0.9.5...v0.9.6
Expand Down
26 changes: 13 additions & 13 deletions Cargo.lock

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

26 changes: 13 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ resolver = "3"
[workspace.package]
edition = "2024"
rust-version = "1.88"
version = "0.9.8"
version = "0.9.9"
authors = ["bug-ops"]
license = "MIT"
repository = "https://github.com/bug-ops/zeph"
Expand Down Expand Up @@ -77,18 +77,18 @@ unicode-width = "0.2"
url = "2.5"
uuid = "1.21"
cron = "0.15"
zeph-a2a = { path = "crates/zeph-a2a", version = "0.9.8" }
zeph-channels = { path = "crates/zeph-channels", version = "0.9.8" }
zeph-core = { path = "crates/zeph-core", version = "0.9.8" }
zeph-index = { path = "crates/zeph-index", version = "0.9.8" }
zeph-llm = { path = "crates/zeph-llm", version = "0.9.8" }
zeph-mcp = { path = "crates/zeph-mcp", version = "0.9.8" }
zeph-memory = { path = "crates/zeph-memory", version = "0.9.8" }
zeph-skills = { path = "crates/zeph-skills", version = "0.9.8" }
zeph-tools = { path = "crates/zeph-tools", version = "0.9.8" }
zeph-gateway = { path = "crates/zeph-gateway", version = "0.9.8" }
zeph-scheduler = { path = "crates/zeph-scheduler", version = "0.9.8" }
zeph-tui = { path = "crates/zeph-tui", version = "0.9.8" }
zeph-a2a = { path = "crates/zeph-a2a", version = "0.9.9" }
zeph-channels = { path = "crates/zeph-channels", version = "0.9.9" }
zeph-core = { path = "crates/zeph-core", version = "0.9.9" }
zeph-index = { path = "crates/zeph-index", version = "0.9.9" }
zeph-llm = { path = "crates/zeph-llm", version = "0.9.9" }
zeph-mcp = { path = "crates/zeph-mcp", version = "0.9.9" }
zeph-memory = { path = "crates/zeph-memory", version = "0.9.9" }
zeph-skills = { path = "crates/zeph-skills", version = "0.9.9" }
zeph-tools = { path = "crates/zeph-tools", version = "0.9.9" }
zeph-gateway = { path = "crates/zeph-gateway", version = "0.9.9" }
zeph-scheduler = { path = "crates/zeph-scheduler", version = "0.9.9" }
zeph-tui = { path = "crates/zeph-tui", version = "0.9.9" }

[workspace.lints.clippy]
all = "warn"
Expand Down
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ ZEPH_LLM_PROVIDER=claude ZEPH_CLAUDE_API_KEY=sk-ant-... ./target/release/zeph
ZEPH_LLM_PROVIDER=openai ZEPH_OPENAI_API_KEY=sk-... ./target/release/zeph
```

For Discord or Slack bot mode (requires respective feature):

```bash
cargo build --release --features discord
ZEPH_DISCORD_TOKEN="..." ZEPH_DISCORD_APP_ID="..." ./target/release/zeph

cargo build --release --features slack
ZEPH_SLACK_BOT_TOKEN="xoxb-..." ZEPH_SLACK_SIGNING_SECRET="..." ./target/release/zeph
```

For TUI dashboard (requires `tui` feature):

```bash
Expand Down Expand Up @@ -104,7 +114,7 @@ cargo build --release --features tui
| **Prompt Caching** | Automatic prompt caching for Anthropic and OpenAI providers, reducing latency and cost on repeated context | |
| **Graceful Shutdown** | Ctrl-C triggers ordered teardown with MCP server cleanup and pending task draining | |
| **TUI Dashboard** | ratatui terminal UI with tree-sitter syntax highlighting, markdown rendering, deferred model warmup, scrollbar, mouse scroll, thinking blocks, conversation history, splash screen, live metrics, message queueing (max 10, FIFO with Ctrl+K clear) | [TUI](https://bug-ops.github.io/zeph/guide/tui.html) |
| **Multi-Channel I/O** | CLI, Telegram, and TUI with streaming support | [Channels](https://bug-ops.github.io/zeph/guide/channels.html) |
| **Multi-Channel I/O** | CLI, Discord, Slack, Telegram, and TUI with streaming support | [Channels](https://bug-ops.github.io/zeph/guide/channels.html) |
| **Defense-in-Depth** | Shell sandbox with relative path traversal detection, file sandbox, command filter, secret redaction (Google/GitLab patterns), audit log, SSRF protection (agent + MCP), rate limiter TTL eviction, doom-loop detection | [Security](https://bug-ops.github.io/zeph/security.html) |

## Architecture
Expand All @@ -118,7 +128,7 @@ zeph (binary) — bootstrap, AnyChannel dispatch, vault resolution (anyhow for t
├── zeph-skills — SKILL.md parser, embedding matcher, hot-reload, self-learning, typed SkillError
├── zeph-memory — SQLite + Qdrant, semantic recall, summarization, typed MemoryError
├── zeph-index — AST-based code indexing, semantic retrieval, repo map (optional)
├── zeph-channels — Telegram adapter (teloxide) with streaming
├── zeph-channels — Discord, Slack, Telegram adapters with streaming
├── zeph-tools — schemars-driven tool registry (shell, file ops, web scrape), composite dispatch
├── zeph-mcp — MCP client, multi-server lifecycle, unified tool matching
├── zeph-a2a — A2A client + server, agent discovery, JSON-RPC 2.0
Expand Down Expand Up @@ -151,6 +161,8 @@ Deep dive: [Architecture overview](https://bug-ops.github.io/zeph/architecture/o
| `self-learning` | On | Skill evolution system |
| `vault-age` | On | Age-encrypted secret storage |
| `index` | On | AST-based code indexing and semantic retrieval |
| `discord` | Off | Discord bot with Gateway v10 WebSocket |
| `slack` | Off | Slack bot with Events API webhook |
| `gateway` | Off | HTTP gateway for webhook ingestion |
| `daemon` | Off | Daemon supervisor for component lifecycle |
| `scheduler` | Off | Cron-based periodic task scheduler |
Expand Down
4 changes: 4 additions & 0 deletions docs/src/feature-flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Zeph uses Cargo feature flags to control optional functionality. Default feature
| `qdrant` | Enabled | Qdrant-backed vector storage for skill matching (`zeph-skills`) and MCP tool registry (`zeph-mcp`) |
| `vault-age` | Enabled | Age-encrypted vault backend for file-based secret storage ([age](https://age-encryption.org/)) |
| `index` | Enabled | AST-based code indexing and semantic retrieval via tree-sitter ([guide](guide/code-indexing.md)) |
| `discord` | Disabled | Discord channel adapter with Gateway v10 WebSocket and slash commands ([guide](guide/channels.md#discord-channel)) |
| `slack` | Disabled | Slack channel adapter with Events API webhook and HMAC-SHA256 verification ([guide](guide/channels.md#slack-channel)) |
| `otel` | Disabled | OpenTelemetry tracing export via OTLP/gRPC ([guide](guide/observability.md)) |
| `gateway` | Disabled | HTTP gateway for webhook ingestion with bearer auth and rate limiting ([guide](guide/gateway.md)) |
| `daemon` | Disabled | Daemon supervisor with component lifecycle, PID file, and health monitoring ([guide](guide/daemon.md)) |
Expand All @@ -28,6 +30,8 @@ cargo build --release # all default features
cargo build --release --features metal # macOS with Metal GPU
cargo build --release --features cuda # Linux with NVIDIA GPU
cargo build --release --features tui # with TUI dashboard
cargo build --release --features discord # with Discord bot
cargo build --release --features slack # with Slack bot
cargo build --release --features gateway,daemon,scheduler # with infrastructure components
cargo build --release --no-default-features # minimal binary
```
Expand Down
135 changes: 133 additions & 2 deletions docs/src/guide/channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Zeph supports multiple I/O channels for interacting with the agent. Each channel
| Channel | Activation | Streaming | Confirmation |
|---------|-----------|-----------|--------------|
| **CLI** | Default (no config needed) | Token-by-token to stdout | y/N prompt |
| **Discord** | `ZEPH_DISCORD_TOKEN` env var or `[discord]` config (requires `discord` feature) | Edit-in-place every 1.5s | Reply "yes" to confirm |
| **Slack** | `ZEPH_SLACK_BOT_TOKEN` env var or `[slack]` config (requires `slack` feature) | `chat.update` every 2s | Reply "yes" to confirm |
| **Telegram** | `ZEPH_TELEGRAM_TOKEN` env var or `[telegram]` config | Edit-in-place every 10s | Reply "yes" to confirm |
| **TUI** | `--tui` flag or `ZEPH_TUI=true` (requires `tui` feature) | Real-time in chat panel | Auto-confirm (Phase 1) |

Expand Down Expand Up @@ -90,6 +92,133 @@ LLM responses are automatically converted from standard Markdown to Telegram's M

When the agent needs user confirmation (e.g., destructive shell commands), Telegram sends a text prompt asking the user to reply "yes" to confirm.

## Discord Channel

Run Zeph as a Discord bot with Gateway v10 WebSocket, slash commands, and edit-in-place streaming. Requires the `discord` feature flag.

```bash
cargo build --release --features discord
```

### Setup

1. Create an application at the [Discord Developer Portal](https://discord.com/developers/applications).
2. Under **Bot**, copy the bot token.
3. Under **OAuth2 > URL Generator**, select `bot` and `applications.commands` scopes, then invite the bot to your server.

4. Configure the token and application ID:

```bash
ZEPH_DISCORD_TOKEN="your-bot-token" ZEPH_DISCORD_APP_ID="123456789" ./zeph
```

Or in `config/default.toml`:

```toml
[discord]
token = "your-bot-token"
application_id = "123456789"
allowed_user_ids = []
allowed_role_ids = []
allowed_channel_ids = []
```

> Tokens are resolved via the vault provider (`ZEPH_DISCORD_TOKEN` and `ZEPH_DISCORD_APP_ID` secrets).

### Allowlists

Restrict access by Discord user IDs, role IDs, or channel IDs:

```toml
[discord]
allowed_user_ids = ["123456789012345678"]
allowed_role_ids = ["987654321098765432"]
allowed_channel_ids = ["111222333444555666"]
```

When all allowlists are empty, the bot accepts messages from all users in all channels.

### Slash Commands

Zeph registers two slash commands on startup via the Discord REST API:

| Command | Description |
|---------|-------------|
| `/ask <message>` | Send a message to the agent |
| `/clear` | Reset conversation context |

### Streaming Behavior

Discord enforces a rate limit of 5 message edits per 5 seconds. Streaming uses edit-in-place with a 1.5-second throttle:

- First chunk sends a new message immediately
- Subsequent chunks edit the existing message in-place (throttled to 1.5s intervals)
- On flush, a final edit delivers the complete response
- Long messages (>2000 chars) are automatically split

## Slack Channel

Run Zeph as a Slack bot with Events API webhook, HMAC-SHA256 signature verification, and streaming via message updates. Requires the `slack` feature flag.

```bash
cargo build --release --features slack
```

### Setup

1. Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps).
2. Under **OAuth & Permissions**, add the `chat:write` scope and install to your workspace. Copy the Bot User OAuth Token.
3. Under **Basic Information**, copy the Signing Secret.
4. Under **Event Subscriptions**, enable events and set the Request URL to `http://<host>:<port>/slack/events`.
5. Subscribe to the `message.channels` and `message.im` bot events.

6. Configure the tokens:

```bash
ZEPH_SLACK_BOT_TOKEN="xoxb-..." ZEPH_SLACK_SIGNING_SECRET="..." ./zeph
```

Or in `config/default.toml`:

```toml
[slack]
bot_token = "xoxb-..."
signing_secret = "..."
port = 3000
webhook_host = "127.0.0.1"
allowed_user_ids = []
allowed_channel_ids = []
```

> Tokens are resolved via the vault provider (`ZEPH_SLACK_BOT_TOKEN` and `ZEPH_SLACK_SIGNING_SECRET` secrets).

### Allowlists

Restrict access by Slack user IDs or channel IDs:

```toml
[slack]
allowed_user_ids = ["U01ABC123"]
allowed_channel_ids = ["C01XYZ456"]
```

When allowlists are empty, the bot accepts messages from all users in all channels.

### Security

- All incoming webhook requests are verified using HMAC-SHA256 with the signing secret (constant-time comparison)
- Requests with timestamps older than 5 minutes are rejected (replay protection)
- Request body size is limited to 256KB
- The bot filters its own messages to prevent infinite feedback loops (via `auth.test` at startup)

### Streaming Behavior

Slack enforces rate limits on `chat.update`. Streaming uses message updates with a 2-second throttle:

- First chunk posts a new message via `chat.postMessage`
- Subsequent chunks update the message via `chat.update` (throttled to 2s intervals)
- On flush, a final update delivers the complete response

## TUI Dashboard

A rich terminal interface based on ratatui with real-time agent metrics. Requires the `tui` feature flag.
Expand Down Expand Up @@ -130,7 +259,9 @@ When the queue is full (10 messages), new input is silently dropped until space
Zeph selects the channel at startup based on the following priority:

1. `--tui` flag or `ZEPH_TUI=true` → TUI channel (requires `tui` feature)
2. `ZEPH_TELEGRAM_TOKEN` set → Telegram channel
3. Otherwise → CLI channel
2. `[discord]` config with token → Discord channel (requires `discord` feature)
3. `[slack]` config with bot_token → Slack channel (requires `slack` feature)
4. `ZEPH_TELEGRAM_TOKEN` set → Telegram channel
5. Otherwise → CLI channel

Only one channel is active per session.
Loading