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
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CodeQL

on:
push:
branches: [main, develop]
branches: [main]
pull_request:
branches: [main, develop]
branches: [main]
schedule:
- cron: "17 3 * * 1"

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ jobs:
with:
node-version: '22'
- name: ClawHub Login
if: ${{ secrets.CLAWHUB_TOKEN != '' }}
continue-on-error: true
run: npx clawhub@latest login --token ${{ secrets.CLAWHUB_TOKEN }}
- name: Publish to ClawHub
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.4.0-alpha](https://github.com/OneStepAt4time/aegis/compare/v0.3.2-alpha...v0.4.0-alpha) (2026-04-10)
## [0.4.0-alpha](https://github.com/OneStepAt4time/aegis/compare/v0.3.2-alpha...v0.4.0-alpha) (2026-04-09)


### Features

* **#599:** expose pendingQuestion in get_status and REST endpoint ([#600](https://github.com/OneStepAt4time/aegis/issues/600)) ([38fc42f](https://github.com/OneStepAt4time/aegis/commit/38fc42f7c99efc67a9b6d3636647682f9a593c39))
* add batch session creation to CreateSessionModal ([#312](https://github.com/OneStepAt4time/aegis/issues/312)) ([a40fe97](https://github.com/OneStepAt4time/aegis/commit/a40fe9789392aa55651d932cf08cf4f4ad8a989e))
* add batch session creation UI ([#312](https://github.com/OneStepAt4time/aegis/issues/312)) ([206db55](https://github.com/OneStepAt4time/aegis/commit/206db55bc1ef6d11fe146a3b992cd8af56ee189b))
* add cursor-based replay contract for transcript endpoint ([#897](https://github.com/OneStepAt4time/aegis/issues/897)) ([d43bf23](https://github.com/OneStepAt4time/aegis/commit/d43bf23b3b282d70fca83276c588155bc5dfc0fb))
* add docs-sync skill for TSDoc coverage audit and README auto-update ([#1044](https://github.com/OneStepAt4time/aegis/issues/1044)) ([c59826e](https://github.com/OneStepAt4time/aegis/commit/c59826e479c05d698dedba5e65e2b23e4af72183))
Expand Down Expand Up @@ -122,6 +123,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* command injection in hook.ts via TMUX_PANE — Issue [#347](https://github.com/OneStepAt4time/aegis/issues/347) ([2216973](https://github.com/OneStepAt4time/aegis/commit/22169736187ba181d13205186b2edd840032b121))
* command injection in hook.ts via TMUX_PANE env var — Issue [#347](https://github.com/OneStepAt4time/aegis/issues/347) ([6bce734](https://github.com/OneStepAt4time/aegis/commit/6bce7349a50f97cffb77f8e577669aeba0f9dcba))
* complete cursor replay contract for events and metadata ([#922](https://github.com/OneStepAt4time/aegis/issues/922)) ([1931ab0](https://github.com/OneStepAt4time/aegis/commit/1931ab0ade6feb282423e779379424c324e8958c))
* correct batchCreateSessions return type to match backend ([#312](https://github.com/OneStepAt4time/aegis/issues/312)) ([54e478a](https://github.com/OneStepAt4time/aegis/commit/54e478ad9c65005c985e6d15f2ea65a67b1b239d))
* correct clawhub publish command in release.yml ([#802](https://github.com/OneStepAt4time/aegis/issues/802)) ([5ebfbc2](https://github.com/OneStepAt4time/aegis/commit/5ebfbc21b427e9fa2b07525bc0c2d48aa9618a96))
* correct README field name brief→prompt and update stale badges — Issue [#396](https://github.com/OneStepAt4time/aegis/issues/396) ([da18754](https://github.com/OneStepAt4time/aegis/commit/da18754452aa258664db793807fdf912f6da9c01))
* correct README field name brief→prompt and update stale badges — Issue [#396](https://github.com/OneStepAt4time/aegis/issues/396) ([18774d3](https://github.com/OneStepAt4time/aegis/commit/18774d3dd9ad40f8debc1b984ebdac3fee58457e))
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ src/
├── terminal-parser.ts # Claude Code UI state detection
├── monitor.ts # Stall detection, events
├── pipeline.ts # Batch/multi-stage orchestration
├── consensus.ts # Multi-agent review
├── auth.ts # API key management
└── config.ts # Configuration (AEGIS_* env vars)
```
Expand Down
38 changes: 1 addition & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,47 +327,12 @@ npx @onestepat4time/aegis # visit http://localhost:9100/dashboard/

---

## Use Cases & Deployment Tiers

Aegis serves three deployment scenarios:

### Tier 1 — Local Orchestration
**Single developer.** Run Claude Code tasks in the background, monitor via dashboard, approve via Telegram.

```bash
aegis
# Dashboard: http://localhost:9100/dashboard/
# Telegram approvals while AFK
```

### Tier 2 — CI/CD & Team Automation
**Development teams.** Policy-based permission control, batch operations, Slack notifications.

```bash
# Blueprint: PR Reviewer
curl -X POST http://localhost:9100/v1/pipelines \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"pr-reviewer","stages":[...],"permissionMode":"plan"}'
```

### Tier 3 — Zero-Trust Enterprise
**Banks, SaaS, regulated industries.** Docker-isolated containers, no network egress, audit-first.

- Each task runs in an ephemeral Docker container
- No cross-container networking
- Immutable audit log for compliance
- See [Enterprise Deployment](docs/enterprise.md) for production hardening guide

**Golden rule:** Intelligence stays outside Aegis. Aegis is a stupid-but-powerful middleware — flows, security, audit. OpenClaw (or any external orchestrator) provides the brains.

---

## Security

Aegis includes built-in security defaults:

- **Permission mode** — `default` requires approval for dangerous operations (shell commands, file writes). Change with `permissionMode` when creating a session.
- **Hook secrets** — use `X-Hook-Secret` header (preferred). Query-param `secret` remains backward compatible by default but is deprecated.
- **Hook secrets** — webhook and hook secrets are passed via headers (not query params) to prevent log leakage.
- **Auth tokens** — protect the API with `AEGIS_AUTH_TOKEN` (Bearer auth on all endpoints except `/v1/health`).
- **WebSocket auth** — session existence is not revealed before authentication.

Expand All @@ -387,7 +352,6 @@ Aegis includes built-in security defaults:
| `AEGIS_TG_TOKEN` | — | Telegram bot token |
| `AEGIS_TG_GROUP` | — | Telegram group chat ID |
| `AEGIS_WEBHOOKS` | — | Webhook URLs (comma-separated) |
| `AEGIS_HOOK_SECRET_HEADER_ONLY` | false | Enforce `X-Hook-Secret` header and reject deprecated `?secret=` transport |

---

Expand Down
14 changes: 7 additions & 7 deletions dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function App() {
<ErrorBoundary>
<Routes>
<Route
path="/login"
path="login"
element={
<Suspense fallback={<LoadingFallback />}>
<LoginPage />
Expand All @@ -41,47 +41,47 @@ export default function App() {
<Route element={<ProtectedRoute />}>
<Route element={<Layout />}>
<Route
path="/"
index
element={
<Suspense fallback={<LoadingFallback />}>
<OverviewPage />
</Suspense>
}
/>
<Route
path="/auth/keys"
path="auth/keys"
element={
<Suspense fallback={<LoadingFallback />}>
<AuthKeysPage />
</Suspense>
}
/>
<Route
path="/sessions/:id"
path="sessions/:id"
element={
<Suspense fallback={<LoadingFallback />}>
<SessionDetailPage />
</Suspense>
}
/>
<Route
path="/pipelines"
path="pipelines"
element={
<Suspense fallback={<LoadingFallback />}>
<PipelinesPage />
</Suspense>
}
/>
<Route
path="/pipelines/:id"
path="pipelines/:id"
element={
<Suspense fallback={<LoadingFallback />}>
<PipelineDetailPage />
</Suspense>
}
/>
<Route
path="/audit"
path="audit"
element={
<Suspense fallback={<LoadingFallback />}>
<AuditPage />
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/store/useAuthStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function clearAuthState(set: (partial: Partial<AuthState>) => void): void {
export const useAuthStore = create<AuthState>((set, get) => ({
token: localStorage.getItem(TOKEN_KEY),
isAuthenticated: false,
isVerifying: false,
isVerifying: true,
lastVerifiedAt: null,

login: async (token: string): Promise<boolean> => {
Expand Down
104 changes: 0 additions & 104 deletions docs/adr/0017-opentelemetry-tracing.md

This file was deleted.

16 changes: 0 additions & 16 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,6 @@ curl http://localhost:9100/v1/sessions

For multi-key auth, see [Enterprise Deployment](./enterprise.md#authentication).

## Claude Code Hook Receiver Authentication

`POST /v1/hooks/{eventName}` is the inbound callback endpoint used by Claude Code hooks.

- Send the session ID via `X-Session-Id` header (or `sessionId` query fallback).
- Send the hook secret via `X-Hook-Secret` header.
- Query-param `secret` is deprecated in compatibility mode and logs a warning.
- Set `AEGIS_HOOK_SECRET_HEADER_ONLY=true` to enforce header-only secret transport and reject query-param secrets.

```bash
curl -X POST "http://localhost:9100/v1/hooks/Stop?sessionId=<session-uuid>" \
-H "X-Hook-Secret: <hook-secret>" \
-H "Content-Type: application/json" \
-d '{}'
```

---

## Core Endpoints
Expand Down
42 changes: 5 additions & 37 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ Aegis is built around a layered architecture: a CLI entrypoint, a Fastify HTTP s
src/
├── cli.ts # CLI entrypoint — parses args, starts server or MCP
├── startup.ts # Server bootstrap — PID file, graceful shutdown
├── container.ts # Lightweight DI container + lifecycle orchestration
├── server.ts # Fastify HTTP server — all REST routes
├── config.ts # Configuration loading from env + config file
├── auth.ts # Backward-compatible auth re-export (services/auth)
├── auth.ts # API key management and bearer token classification
├── session.ts # Session lifecycle — create, send, kill, state tracking
├── session-cleanup.ts # Idle session reaping and resource cleanup
Expand Down Expand Up @@ -48,16 +47,7 @@ src/
├── ws-terminal.ts # WebSocket terminal relay
├── permission-guard.ts # Permission request interception and routing
├── services/
│ ├── auth/
│ │ ├── AuthManager.ts # API key management and bearer token classification
│ │ ├── RateLimiter.ts # Route-level IP and auth-failure rate limiting
│ │ ├── types.ts # Auth manager and API key types
│ │ └── index.ts # Auth service exports
│ └── permission/
│ ├── evaluator.ts # Permission profile evaluation logic
│ ├── types.ts # Permission evaluator input/output types
│ └── index.ts # Permission evaluator exports
├── permission-evaluator.ts # Permission profile evaluation logic
├── permission-request-manager.ts # Permission request queue and lifecycle
├── permission-routes.ts # REST endpoints for approve/reject/list permissions
Expand Down Expand Up @@ -118,7 +108,7 @@ dashboard/ # React dashboard (served by Fastify static)
| `cli.ts` | Parses CLI arguments, delegates to `server.ts` or `mcp-server.ts` |
| `startup.ts` | Writes PID file, registers signal handlers, coordinates shutdown |
| `config.ts` | Loads config from `aegis.config.json` and environment variables |
| `services/auth/AuthManager.ts` | Manages API keys and classifies bearer tokens for route protection |
| `auth.ts` | Manages API keys and classifies bearer tokens for route protection |

### 2. Session Management

Expand Down Expand Up @@ -172,7 +162,7 @@ dashboard/ # React dashboard (served by Fastify static)
| Module | Purpose |
|---|---|
| `permission-guard.ts` | Intercepts permission requests and routes to evaluator |
| `services/permission/evaluator.ts` | Evaluates permission requests against profiles |
| `permission-evaluator.ts` | Evaluates permission requests against profiles |
| `permission-request-manager.ts` | Queues and tracks pending permission requests |
| `permission-routes.ts` | REST endpoints: approve, reject, list pending permissions |

Expand Down Expand Up @@ -224,7 +214,7 @@ Client (curl / MCP / Dashboard)
server.ts (Fastify, port 9100)
├─ services/auth/AuthManager.ts (bearer token validation)
├─ auth.ts (bearer token validation)
├─ api-contracts.ts (request validation)
Expand All @@ -245,25 +235,3 @@ server.ts (Fastify, port 9100)
└─ mcp-server.ts (MCP protocol, stdio)
└─ tool-registry.ts (tool dispatch)
```

## Service lifecycle dependency graph

Issue #1622 introduces explicit service registration and dependency-driven startup/shutdown in `src/container.ts`.

```text
tmuxManager
└─ sessionManager
├─ channelManager
└─ sessionMonitor
authManager
```

| Service | Depends on | Startup action | Shutdown action |
|---|---|---|---|
| `tmuxManager` | — | `tmux.ensureSession()` | no-op |
| `sessionManager` | `tmuxManager` | `sessions.load()` | `sessions.save()` |
| `authManager` | — | `auth.load()` | no-op |
| `channelManager` | `sessionManager` | `channels.init(handleInbound)` | `channels.destroy()` |
| `sessionMonitor` | `tmuxManager`, `sessionManager`, `channelManager` | `monitor.start()` | `monitor.stop()` |

Startup follows topological order from the dependency graph. Graceful shutdown runs in the reverse order with per-service timeout protection.
Loading
Loading