chore: rebrand farhoodliquor → farhoodlabs, API-only mode, split infra
- Rename org references from farhoodliquor to farhoodlabs in CI workflows and GHCR image tags - Rewrite README for Hightower as API-driven K8s fork of Shannon - Update CLAUDE.md to reflect API-only deployment model - Delete docker-compose files (K8s only, no Docker Compose support) - Delete shannon CLI entry point (API-only going forward) - Move K8s manifests to farhoodlabs/hightower-infra Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
name: Type-check & lint
|
name: Type-check & lint
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
name: Build & push worker image
|
name: Build & push worker image
|
||||||
needs: check
|
needs: check
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
@@ -68,14 +68,14 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
ghcr.io/farhoodliquor/shannon:latest
|
ghcr.io/farhoodlabs/shannon:latest
|
||||||
ghcr.io/farhoodliquor/shannon:sha-${{ github.sha }}
|
ghcr.io/farhoodlabs/shannon:sha-${{ github.sha }}
|
||||||
|
|
||||||
build-api:
|
build-api:
|
||||||
name: Build & push API image
|
name: Build & push API image
|
||||||
needs: check
|
needs: check
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
@@ -102,5 +102,5 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
no-cache: true
|
no-cache: true
|
||||||
tags: |
|
tags: |
|
||||||
ghcr.io/farhoodliquor/hightower-api:latest
|
ghcr.io/farhoodlabs/hightower-api:latest
|
||||||
ghcr.io/farhoodliquor/hightower-api:sha-${{ github.sha }}
|
ghcr.io/farhoodlabs/hightower-api:sha-${{ github.sha }}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
preflight:
|
preflight:
|
||||||
name: Preflight
|
name: Preflight
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.version.outputs.version }}
|
version: ${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
build-docker:
|
build-docker:
|
||||||
name: Build Docker (worker)
|
name: Build Docker (worker)
|
||||||
needs: preflight
|
needs: preflight
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ jobs:
|
|||||||
build-docker-api:
|
build-docker-api:
|
||||||
name: Build Docker (API)
|
name: Build Docker (API)
|
||||||
needs: preflight
|
needs: preflight
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ jobs:
|
|||||||
sign-docker:
|
sign-docker:
|
||||||
name: Sign Docker images
|
name: Sign Docker images
|
||||||
needs: [preflight, build-docker, build-docker-api]
|
needs: [preflight, build-docker, build-docker-api]
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
@@ -165,7 +165,7 @@ jobs:
|
|||||||
publish-npm:
|
publish-npm:
|
||||||
name: Publish npm (beta)
|
name: Publish npm (beta)
|
||||||
needs: [preflight, sign-docker]
|
needs: [preflight, sign-docker]
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
preflight:
|
preflight:
|
||||||
name: Preflight
|
name: Preflight
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
outputs:
|
outputs:
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
name: Build Docker (worker)
|
name: Build Docker (worker)
|
||||||
needs: preflight
|
needs: preflight
|
||||||
if: needs.preflight.outputs.should_release == 'true'
|
if: needs.preflight.outputs.should_release == 'true'
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ jobs:
|
|||||||
name: Build Docker (API)
|
name: Build Docker (API)
|
||||||
needs: preflight
|
needs: preflight
|
||||||
if: needs.preflight.outputs.should_release == 'true'
|
if: needs.preflight.outputs.should_release == 'true'
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ jobs:
|
|||||||
sign-docker:
|
sign-docker:
|
||||||
name: Sign Docker images
|
name: Sign Docker images
|
||||||
needs: [preflight, build-docker, build-docker-api]
|
needs: [preflight, build-docker, build-docker-api]
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
@@ -183,7 +183,7 @@ jobs:
|
|||||||
publish-npm:
|
publish-npm:
|
||||||
name: Publish npm
|
name: Publish npm
|
||||||
needs: [preflight, sign-docker]
|
needs: [preflight, sign-docker]
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
@@ -228,7 +228,7 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
name: Create GitHub release
|
name: Create GitHub release
|
||||||
needs: [preflight, publish-npm]
|
needs: [preflight, publish-npm]
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
rollback:
|
rollback:
|
||||||
name: Roll back npm beta dist-tag
|
name: Roll back npm beta dist-tag
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
steps:
|
steps:
|
||||||
- name: Validate target version
|
- name: Validate target version
|
||||||
id: target
|
id: target
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
rollback:
|
rollback:
|
||||||
name: Roll back npm, Docker, and GitHub release latest
|
name: Roll back npm, Docker, and GitHub release latest
|
||||||
runs-on: runners-farhoodliquor
|
runs-on: runners-farhoodlabs
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout tags
|
- name: Checkout tags
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|||||||
@@ -1,78 +1,12 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
AI-powered penetration testing agent for defensive security analysis. Automates vulnerability assessment by combining reconnaissance tools with AI-powered code analysis.
|
Hightower is a fork of [Shannon](https://github.com/KeygraphHQ/shannon) by Keygraph — an AI-powered penetration testing agent for defensive security analysis. It wraps Shannon's autonomous pentesting engine with a REST API and Kubernetes deployment tooling.
|
||||||
|
|
||||||
|
**Upstream policy:** `apps/cli/` and `apps/worker/` are kept as close to upstream Shannon as possible for backporting. Only `apps/api/`, CI/CD workflows, and infra are Hightower-specific.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
**Prerequisites:** Docker, AI provider credentials (`.env` for local, `shn setup` or env vars for npx)
|
|
||||||
|
|
||||||
### Dual CLI
|
|
||||||
|
|
||||||
Shannon supports two CLI modes, auto-detected based on the current working directory:
|
|
||||||
|
|
||||||
| | **npx** (`npx @keygraph/shannon`) | **Local** (`./shannon`) |
|
|
||||||
|---|---|---|
|
|
||||||
| **Install** | Zero-install via npm | Clone the repo |
|
|
||||||
| **Image** | Pulled from Docker Hub (`keygraph/shannon:latest`) | Built locally (`shannon-worker`) |
|
|
||||||
| **State** | `~/.shannon/` | Project directory |
|
|
||||||
| **Credentials** | `~/.shannon/config.toml` (via `shn setup`) or env vars | `./.env` |
|
|
||||||
| **Config** | `~/.shannon/config.toml` (via `shn setup`) | N/A |
|
|
||||||
| **Prompts** | Bundled in Docker image | Mounted from `./apps/worker/prompts/` (live-editable) |
|
|
||||||
|
|
||||||
Mode auto-detection: local mode activates when env var `SHANNON_LOCAL=1` is set by the `./shannon` entry point (`apps/cli/src/mode.ts`). Otherwise npx mode.
|
|
||||||
|
|
||||||
### npx Quick Start
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Configure credentials (interactive wizard)
|
|
||||||
npx @keygraph/shannon setup
|
|
||||||
|
|
||||||
# Or export env vars directly (non-interactive / CI)
|
|
||||||
export ANTHROPIC_API_KEY=your-key
|
|
||||||
|
|
||||||
# Run
|
|
||||||
npx @keygraph/shannon start -u <url> -r /path/to/repo
|
|
||||||
```
|
|
||||||
|
|
||||||
### Local (Development) Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Setup
|
|
||||||
echo "ANTHROPIC_API_KEY=your-key" > .env
|
|
||||||
|
|
||||||
# Build (auto-runs if image missing)
|
|
||||||
./shannon build
|
|
||||||
|
|
||||||
# Run
|
|
||||||
./shannon start -u <url> -r my-repo
|
|
||||||
./shannon start -u <url> -r my-repo -c ./apps/worker/configs/my-config.yaml
|
|
||||||
./shannon start -u <url> -r /any/path/to/repo
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Setup (npx mode only — one-time credential configuration)
|
|
||||||
npx @keygraph/shannon setup
|
|
||||||
|
|
||||||
# Workspaces & Resume
|
|
||||||
./shannon start -u <url> -r my-repo -w my-audit # New named workspace
|
|
||||||
./shannon start -u <url> -r my-repo -w my-audit # Resume (same command)
|
|
||||||
./shannon workspaces # List all workspaces
|
|
||||||
|
|
||||||
# Monitor
|
|
||||||
./shannon logs <workspace> # Tail workflow log
|
|
||||||
./shannon status # Show running workers
|
|
||||||
# Temporal Web UI: http://localhost:8233
|
|
||||||
|
|
||||||
# Stop
|
|
||||||
./shannon stop # Preserves workflow data
|
|
||||||
./shannon stop --clean # Full cleanup including volumes (confirms first)
|
|
||||||
|
|
||||||
# Image management
|
|
||||||
./shannon build [--no-cache] # Local mode: build worker image
|
|
||||||
npx @keygraph/shannon uninstall # npx mode: remove ~/.shannon/ (confirms first)
|
|
||||||
|
|
||||||
# Build TypeScript (development)
|
# Build TypeScript (development)
|
||||||
pnpm run build # Build all packages via Turborepo
|
pnpm run build # Build all packages via Turborepo
|
||||||
pnpm run check # Type-check all packages
|
pnpm run check # Type-check all packages
|
||||||
@@ -82,42 +16,25 @@ pnpm biome:fix # Auto-fix lint, format, and import sorting
|
|||||||
|
|
||||||
**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace.
|
**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace.
|
||||||
|
|
||||||
**Options:** `-c <file>` (YAML config), `-o <path>` (output directory), `-w <name>` (named workspace; auto-resumes if exists), `--pipeline-testing` (minimal prompts, 10s retries), `--router` (multi-model routing via [claude-code-router](https://github.com/musistudio/claude-code-router))
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Monorepo Layout
|
### Monorepo Layout
|
||||||
|
|
||||||
```
|
```
|
||||||
apps/cli/ — @keygraph/shannon (published to npm, bundled with tsdown)
|
apps/api/ — @shannon/api (Hightower REST API, K8s-native)
|
||||||
apps/worker/ — @shannon/worker (private, Temporal worker + pipeline logic)
|
apps/cli/ — @keygraph/shannon (upstream CLI, not used in production)
|
||||||
|
apps/worker/ — @shannon/worker (upstream Temporal worker + pipeline logic)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### API Package (`apps/api/`)
|
||||||
|
Hightower-specific REST API for triggering and managing pentests. Deployed on Kubernetes.
|
||||||
|
|
||||||
### CLI Package (`apps/cli/`)
|
### CLI Package (`apps/cli/`)
|
||||||
Published as `@keygraph/shannon` on npm. Contains only Docker orchestration logic — no Temporal SDK, business logic, or prompts. Bundled with tsdown for single-file ESM output.
|
Upstream Shannon CLI — kept for backporting compatibility. Not used in Hightower's K8s deployment.
|
||||||
|
|
||||||
- `apps/cli/src/index.ts` — CLI dispatcher (`setup`, `start`, `stop`, `logs`, `workspaces`, `status`, `build`, `uninstall`, `info`)
|
|
||||||
- `apps/cli/src/mode.ts` — Auto-detection: local mode if `SHANNON_LOCAL=1` env var is set
|
|
||||||
- `apps/cli/src/docker.ts` — Compose lifecycle, image pull/build, ephemeral `docker run` worker spawning
|
|
||||||
- `apps/cli/src/home.ts` — State directory management (`~/.shannon/` for npx, `./` for local)
|
|
||||||
- `apps/cli/src/env.ts` — `.env` loading, TOML fallback (npx only) via `apps/cli/src/config/resolver.ts`, credential validation, env flag building
|
|
||||||
- `apps/cli/src/config/resolver.ts` — Cascading config (npx only): env vars → `~/.shannon/config.toml` (parsed with `smol-toml`)
|
|
||||||
- `apps/cli/src/config/writer.ts` — TOML serialization and secure file persistence (0o600)
|
|
||||||
- `apps/cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only)
|
|
||||||
- `apps/cli/src/paths.ts` — Repo/config path resolution (bare name → `./repos/<name>`, or any absolute/relative path)
|
|
||||||
- `apps/cli/src/commands/` — Command handlers
|
|
||||||
- `apps/cli/infra/compose.yml` — Bundled Temporal + router compose file for npx mode
|
|
||||||
- `apps/cli/tsdown.config.ts` — tsdown bundler config
|
|
||||||
- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `apps/cli/dist/index.mjs`
|
|
||||||
|
|
||||||
### Docker Architecture
|
|
||||||
Infra (Temporal + router) runs via `docker-compose.yml`. Workers are ephemeral `docker run --rm` containers, one per scan, each with a unique task queue and isolated volume mounts.
|
|
||||||
|
|
||||||
- `docker-compose.yml` — Infra only: `shannon-temporal` (port 7233/8233) and `shannon-router` (port 3456, optional via profile). Network: `shannon-net`
|
|
||||||
- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD ["node", "apps/worker/dist/temporal/worker.js"]`
|
|
||||||
- No `docker-compose.docker.yml` — host gateway handled via `--add-host` flag in CLI
|
|
||||||
|
|
||||||
### Worker Package (`apps/worker/`)
|
### Worker Package (`apps/worker/`)
|
||||||
|
Upstream Shannon worker — Temporal worker + pipeline logic. Runs as ephemeral K8s Jobs.
|
||||||
|
|
||||||
- `apps/worker/src/paths.ts` — Centralized path constants (`PROMPTS_DIR`, `CONFIGS_DIR`, `WORKSPACES_DIR`)
|
- `apps/worker/src/paths.ts` — Centralized path constants (`PROMPTS_DIR`, `CONFIGS_DIR`, `WORKSPACES_DIR`)
|
||||||
- `apps/worker/src/session-manager.ts` — Agent definitions (`AGENTS` record). Agent types in `apps/worker/src/types/agents.ts`
|
- `apps/worker/src/session-manager.ts` — Agent definitions (`AGENTS` record). Agent types in `apps/worker/src/types/agents.ts`
|
||||||
- `apps/worker/src/config-parser.ts` — YAML config parsing with JSON Schema validation
|
- `apps/worker/src/config-parser.ts` — YAML config parsing with JSON Schema validation
|
||||||
@@ -135,6 +52,7 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig
|
|||||||
- `apps/worker/src/temporal/summary-mapper.ts` — Maps `PipelineSummary` to `WorkflowSummary`
|
- `apps/worker/src/temporal/summary-mapper.ts` — Maps `PipelineSummary` to `WorkflowSummary`
|
||||||
- `apps/worker/src/temporal/worker.ts` — Combined worker + client entry point (per-invocation task queue, submits workflow, waits for result)
|
- `apps/worker/src/temporal/worker.ts` — Combined worker + client entry point (per-invocation task queue, submits workflow, waits for result)
|
||||||
- `apps/worker/src/temporal/shared.ts` — Types, interfaces, query definitions
|
- `apps/worker/src/temporal/shared.ts` — Types, interfaces, query definitions
|
||||||
|
|
||||||
### Five-Phase Pipeline
|
### Five-Phase Pipeline
|
||||||
|
|
||||||
1. **Pre-Recon** (`pre-recon`) — External scans (nmap, subfinder, whatweb) + source code analysis
|
1. **Pre-Recon** (`pre-recon`) — External scans (nmap, subfinder, whatweb) + source code analysis
|
||||||
@@ -143,8 +61,15 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig
|
|||||||
4. **Exploitation** (5 parallel agents, conditional) — Exploits confirmed vulnerabilities
|
4. **Exploitation** (5 parallel agents, conditional) — Exploits confirmed vulnerabilities
|
||||||
5. **Reporting** (`report`) — Executive-level security report
|
5. **Reporting** (`report`) — Executive-level security report
|
||||||
|
|
||||||
|
### Docker Images
|
||||||
|
- `Dockerfile` — Worker image: 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD ["node", "apps/worker/dist/temporal/worker.js"]`
|
||||||
|
- `apps/api/Dockerfile` — API image: minimal Alpine build
|
||||||
|
|
||||||
|
### Kubernetes Infrastructure
|
||||||
|
K8s manifests live in a separate repository: [farhoodlabs/hightower-infra](https://github.com/farhoodlabs/hightower-infra).
|
||||||
|
|
||||||
### Supporting Systems
|
### Supporting Systems
|
||||||
- **Configuration** — YAML configs in `apps/worker/configs/` with JSON Schema validation (`config-schema.json`). Supports auth settings, MFA/TOTP, and per-app testing parameters. Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `shn setup`)
|
- **Configuration** — YAML configs in `apps/worker/configs/` with JSON Schema validation (`config-schema.json`). Supports auth settings, MFA/TOTP, and per-app testing parameters
|
||||||
- **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts`
|
- **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts`
|
||||||
- **SDK Integration** — Uses `@anthropic-ai/claude-agent-sdk` with `maxTurns: 10_000` and `bypassPermissions` mode. Browser automation via `playwright-cli` with session isolation (`-s=<session>`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth
|
- **SDK Integration** — Uses `@anthropic-ai/claude-agent-sdk` with `maxTurns: 10_000` and `bypassPermissions` mode. Browser automation via `playwright-cli` with session isolation (`-s=<session>`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth
|
||||||
- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. Tracks session metrics, per-agent logs, prompts, and deliverables. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive
|
- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. Tracks session metrics, per-agent logs, prompts, and deliverables. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive
|
||||||
@@ -171,7 +96,7 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig
|
|||||||
- **Modular Error Handling** — `ErrorCode` enum, `Result<T,E>` for explicit error propagation, automatic retry (3 attempts per agent)
|
- **Modular Error Handling** — `ErrorCode` enum, `Result<T,E>` for explicit error propagation, automatic retry (3 attempts per agent)
|
||||||
- **Services Boundary** — Activities are thin Temporal wrappers; `apps/worker/src/services/` owns business logic, accepts `ActivityLogger`, returns `Result<T,E>`. No Temporal imports in services
|
- **Services Boundary** — Activities are thin Temporal wrappers; `apps/worker/src/services/` owns business logic, accepts `ActivityLogger`, returns `Result<T,E>`. No Temporal imports in services
|
||||||
- **DI Container** — Per-workflow in `apps/worker/src/services/container.ts`. `AuditSession` excluded (parallel safety)
|
- **DI Container** — Per-workflow in `apps/worker/src/services/container.ts`. `AuditSession` excluded (parallel safety)
|
||||||
- **Ephemeral Workers** — Each scan runs in its own `docker run --rm` container with a per-invocation task queue. Temporal routes activities by queue name, so per-scan queues ensure activities never land on a worker with the wrong repo mounted
|
- **Ephemeral Workers** — Each scan runs in its own container with a per-invocation task queue. Temporal routes activities by queue name, so per-scan queues ensure activities never land on a worker with the wrong repo mounted
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
Defensive security tool only. Use only on systems you own or have explicit permission to test.
|
Defensive security tool only. Use only on systems you own or have explicit permission to test.
|
||||||
@@ -223,26 +148,16 @@ Comments must be **timeless** — no references to this conversation, refactorin
|
|||||||
|
|
||||||
## Key Files
|
## Key Files
|
||||||
|
|
||||||
**CLI:** `shannon` (entry point), `apps/cli/src/index.ts` (dispatcher), `apps/cli/src/docker.ts` (orchestration), `apps/cli/src/mode.ts` (auto-detection)
|
**API:** `apps/api/src/` (Hightower REST API), `apps/api/Dockerfile`
|
||||||
|
|
||||||
**Entry Points:** `apps/worker/src/temporal/workflows.ts`, `apps/worker/src/temporal/activities.ts`, `apps/worker/src/temporal/worker.ts`
|
**Entry Points:** `apps/worker/src/temporal/workflows.ts`, `apps/worker/src/temporal/activities.ts`, `apps/worker/src/temporal/worker.ts`
|
||||||
|
|
||||||
**Core Logic:** `apps/worker/src/session-manager.ts`, `apps/worker/src/ai/claude-executor.ts`, `apps/worker/src/config-parser.ts`, `apps/worker/src/services/`, `apps/worker/src/audit/`
|
**Core Logic:** `apps/worker/src/session-manager.ts`, `apps/worker/src/ai/claude-executor.ts`, `apps/worker/src/config-parser.ts`, `apps/worker/src/services/`, `apps/worker/src/audit/`
|
||||||
|
|
||||||
**Config:** `docker-compose.yml`, `apps/cli/infra/compose.yml`, `apps/worker/configs/`, `apps/worker/prompts/`, `tsconfig.base.json` (shared compiler options), `turbo.json`, `biome.json`
|
**Config:** `Dockerfile`, `apps/worker/configs/`, `apps/worker/prompts/`, `tsconfig.base.json` (shared compiler options), `turbo.json`, `biome.json`
|
||||||
|
|
||||||
**CI/CD:** `.github/workflows/release.yml` (Docker Hub push + npm publish + GitHub release, manual dispatch)
|
**CI/CD:** `.github/workflows/ci.yml` (type-check, lint, build & push images to GHCR), `.github/workflows/release.yml` (Docker Hub push + GitHub release, manual dispatch)
|
||||||
|
|
||||||
## Package Installation
|
## Package Installation
|
||||||
|
|
||||||
Package managers are configured with a minimum release age (7 days). Requires pnpm >= 10.16.0. If `pnpm install` fails due to a package being too new, **do not attempt to bypass it** — report the blocked package to the user and stop.
|
Package managers are configured with a minimum release age (7 days). Requires pnpm >= 10.16.0. If `pnpm install` fails due to a package being too new, **do not attempt to bypass it** — report the blocked package to the user and stop.
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
- **"Repository not found"** — Pass a bare name (`-r my-repo`) for `./repos/my-repo`, or a path (`-r /path/to/repo`) for any directory
|
|
||||||
- **"Temporal not ready"** — Wait for health check or `docker compose logs temporal`
|
|
||||||
- **Worker not processing** — Check `docker ps --filter "name=shannon-worker-"`
|
|
||||||
- **Reset state** — `./shannon stop --clean`
|
|
||||||
- **Local apps unreachable** — Use `host.docker.internal` instead of `localhost`
|
|
||||||
- **Missing tools** — Use `--pipeline-testing` to skip nmap/subfinder/whatweb (graceful degradation)
|
|
||||||
- **Container permissions** — On Linux, may need `sudo` for docker commands
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: hightower-api
|
|
||||||
namespace: hightower
|
|
||||||
labels:
|
|
||||||
app: hightower-api
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: hightower-api
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: hightower-api
|
|
||||||
annotations:
|
|
||||||
kubectl.kubernetes.io/restartedAt: "2026-04-21T22:21:00Z"
|
|
||||||
spec:
|
|
||||||
serviceAccountName: hightower-api
|
|
||||||
containers:
|
|
||||||
- name: api
|
|
||||||
image: ghcr.io/farhoodliquor/hightower-api:sha-a0efe7604ebc2f27cc37ee88f117ae619e57003f
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- containerPort: 3000
|
|
||||||
name: http
|
|
||||||
env:
|
|
||||||
- name: TEMPORAL_ADDRESS
|
|
||||||
value: hightower-temporal:7233
|
|
||||||
- name: WORKER_IMAGE
|
|
||||||
value: ghcr.io/farhoodliquor/shannon:latest
|
|
||||||
- name: K8S_NAMESPACE
|
|
||||||
value: hightower
|
|
||||||
envFrom:
|
|
||||||
- secretRef:
|
|
||||||
name: hightower-credentials
|
|
||||||
volumeMounts:
|
|
||||||
- name: workspaces
|
|
||||||
mountPath: /app/workspaces
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /healthz
|
|
||||||
port: 3000
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /readyz
|
|
||||||
port: 3000
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 10
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: 128Mi
|
|
||||||
cpu: 100m
|
|
||||||
limits:
|
|
||||||
memory: 256Mi
|
|
||||||
volumes:
|
|
||||||
- name: workspaces
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: hightower-workspaces
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- deployment.yaml
|
|
||||||
- service.yaml
|
|
||||||
- serviceaccount.yaml
|
|
||||||
- rbac.yaml
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: Role
|
|
||||||
metadata:
|
|
||||||
name: hightower-api
|
|
||||||
namespace: hightower
|
|
||||||
rules:
|
|
||||||
- apiGroups: ["batch"]
|
|
||||||
resources: ["jobs"]
|
|
||||||
verbs: ["create", "get", "list", "delete", "watch"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "watch"]
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["pods/log"]
|
|
||||||
verbs: ["get"]
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: RoleBinding
|
|
||||||
metadata:
|
|
||||||
name: hightower-api
|
|
||||||
namespace: hightower
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: hightower-api
|
|
||||||
namespace: hightower
|
|
||||||
roleRef:
|
|
||||||
kind: Role
|
|
||||||
name: hightower-api
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: Role
|
|
||||||
metadata:
|
|
||||||
name: hightower-deploy-manager
|
|
||||||
namespace: hightower
|
|
||||||
rules:
|
|
||||||
- apiGroups: ["apps"]
|
|
||||||
resources: ["deployments"]
|
|
||||||
verbs: ["get", "list", "watch", "update", "patch"]
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: RoleBinding
|
|
||||||
metadata:
|
|
||||||
name: farh-net-deploy-manager
|
|
||||||
namespace: hightower
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: farh-net-paperclip
|
|
||||||
namespace: farh-net
|
|
||||||
roleRef:
|
|
||||||
kind: Role
|
|
||||||
name: hightower-deploy-manager
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: hightower-api
|
|
||||||
namespace: hightower
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: hightower-api
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 3000
|
|
||||||
targetPort: 3000
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: hightower-api
|
|
||||||
namespace: hightower
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- ../../base
|
|
||||||
patches:
|
|
||||||
- patch: |-
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: hightower-api
|
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: api
|
|
||||||
imagePullPolicy: Never
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
networks:
|
|
||||||
default:
|
|
||||||
name: shannon-net
|
|
||||||
|
|
||||||
services:
|
|
||||||
temporal:
|
|
||||||
image: temporalio/temporal:latest
|
|
||||||
container_name: shannon-temporal
|
|
||||||
command: ["server", "start-dev", "--db-filename", "/home/temporal/temporal.db", "--ip", "0.0.0.0"]
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:7233:7233"
|
|
||||||
- "127.0.0.1:8233:8233"
|
|
||||||
volumes:
|
|
||||||
- temporal-data:/home/temporal
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "temporal", "operator", "cluster", "health", "--address", "localhost:7233"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 10
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
router:
|
|
||||||
image: node:20-slim
|
|
||||||
container_name: shannon-router
|
|
||||||
profiles: ["router"]
|
|
||||||
command: >
|
|
||||||
sh -c "apt-get update && apt-get install -y gettext-base &&
|
|
||||||
npm install -g @musistudio/claude-code-router &&
|
|
||||||
mkdir -p /root/.claude-code-router &&
|
|
||||||
envsubst < /config/router-config.json > /root/.claude-code-router/config.json &&
|
|
||||||
ccr start"
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:3456:3456"
|
|
||||||
volumes:
|
|
||||||
- ./router-config.json:/config/router-config.json:ro
|
|
||||||
environment:
|
|
||||||
- HOST=0.0.0.0
|
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
|
||||||
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
|
||||||
- ROUTER_DEFAULT=${ROUTER_DEFAULT:-openai,gpt-4o}
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3456/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
temporal-data:
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: hightower-router
|
|
||||||
namespace: hightower
|
|
||||||
labels:
|
|
||||||
app: hightower-router
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: hightower-router
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: hightower-router
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: router
|
|
||||||
image: node:20-slim
|
|
||||||
command:
|
|
||||||
- sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
apt-get update && apt-get install -y gettext-base &&
|
|
||||||
npm install -g @musistudio/claude-code-router &&
|
|
||||||
mkdir -p /root/.claude-code-router &&
|
|
||||||
envsubst < /config/router-config.json > /root/.claude-code-router/config.json &&
|
|
||||||
ccr start
|
|
||||||
ports:
|
|
||||||
- containerPort: 3456
|
|
||||||
envFrom:
|
|
||||||
- secretRef:
|
|
||||||
name: hightower-credentials
|
|
||||||
env:
|
|
||||||
- name: HOST
|
|
||||||
value: "0.0.0.0"
|
|
||||||
volumeMounts:
|
|
||||||
- name: config
|
|
||||||
mountPath: /config
|
|
||||||
readOnly: true
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 3456
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 5
|
|
||||||
failureThreshold: 5
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: 128Mi
|
|
||||||
cpu: 100m
|
|
||||||
volumes:
|
|
||||||
- name: config
|
|
||||||
configMap:
|
|
||||||
name: hightower-router-config
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: hightower-router
|
|
||||||
namespace: hightower
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: hightower-router
|
|
||||||
ports:
|
|
||||||
- port: 3456
|
|
||||||
targetPort: 3456
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
# CNPG PostgreSQL cluster for Temporal persistence
|
|
||||||
apiVersion: postgresql.cnpg.io/v1
|
|
||||||
kind: Cluster
|
|
||||||
metadata:
|
|
||||||
name: hightower-temporal-db
|
|
||||||
namespace: hightower
|
|
||||||
spec:
|
|
||||||
instances: 1
|
|
||||||
storage:
|
|
||||||
size: 5Gi
|
|
||||||
storageClass: ceph-block
|
|
||||||
bootstrap:
|
|
||||||
initdb:
|
|
||||||
database: temporal
|
|
||||||
owner: temporal
|
|
||||||
postInitSQL:
|
|
||||||
- CREATE DATABASE temporal_visibility OWNER temporal;
|
|
||||||
---
|
|
||||||
# Temporal auto-setup — handles schema creation/migration automatically
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: hightower-temporal
|
|
||||||
namespace: hightower
|
|
||||||
labels:
|
|
||||||
app: hightower-temporal
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: hightower-temporal
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: hightower-temporal
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: temporal
|
|
||||||
image: temporalio/auto-setup:latest
|
|
||||||
ports:
|
|
||||||
- containerPort: 7233
|
|
||||||
name: grpc
|
|
||||||
- containerPort: 8233
|
|
||||||
name: web-ui
|
|
||||||
env:
|
|
||||||
- name: DB
|
|
||||||
value: postgres12
|
|
||||||
- name: DB_PORT
|
|
||||||
value: "5432"
|
|
||||||
- name: POSTGRES_SEEDS
|
|
||||||
value: hightower-temporal-db-rw
|
|
||||||
- name: DBNAME
|
|
||||||
value: temporal
|
|
||||||
- name: VISIBILITY_DBNAME
|
|
||||||
value: temporal_visibility
|
|
||||||
- name: POSTGRES_USER
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: hightower-temporal-db-app
|
|
||||||
key: username
|
|
||||||
- name: POSTGRES_PWD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: hightower-temporal-db-app
|
|
||||||
key: password
|
|
||||||
- name: NUM_HISTORY_SHARDS
|
|
||||||
value: "4"
|
|
||||||
- name: SKIP_DB_CREATE
|
|
||||||
value: "true"
|
|
||||||
readinessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 7233
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 5
|
|
||||||
failureThreshold: 15
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: 512Mi
|
|
||||||
cpu: 250m
|
|
||||||
limits:
|
|
||||||
memory: 1Gi
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: hightower-temporal
|
|
||||||
namespace: hightower
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: hightower-temporal
|
|
||||||
ports:
|
|
||||||
- name: grpc
|
|
||||||
port: 7233
|
|
||||||
targetPort: 7233
|
|
||||||
- name: web-ui
|
|
||||||
port: 8233
|
|
||||||
targetPort: 8233
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: hightower-workspaces
|
|
||||||
namespace: hightower
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteMany
|
|
||||||
storageClassName: ceph-filesystem
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 10Gi
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
networks:
|
|
||||||
default:
|
|
||||||
name: shannon-net
|
|
||||||
|
|
||||||
services:
|
|
||||||
temporal:
|
|
||||||
image: temporalio/temporal:latest
|
|
||||||
container_name: shannon-temporal
|
|
||||||
command: ["server", "start-dev", "--db-filename", "/home/temporal/temporal.db", "--ip", "0.0.0.0"]
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:7233:7233" # gRPC
|
|
||||||
- "127.0.0.1:8233:8233" # Web UI (built-in)
|
|
||||||
volumes:
|
|
||||||
- temporal-data:/home/temporal
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "temporal", "operator", "cluster", "health", "--address", "localhost:7233"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 10
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
# Optional: claude-code-router for multi-model support
|
|
||||||
# Start with: ROUTER=true ./shannon start ...
|
|
||||||
router:
|
|
||||||
image: node:20-slim
|
|
||||||
container_name: shannon-router
|
|
||||||
profiles: ["router"] # Only starts when explicitly requested
|
|
||||||
command: >
|
|
||||||
sh -c "apt-get update && apt-get install -y gettext-base &&
|
|
||||||
npm install -g @musistudio/claude-code-router &&
|
|
||||||
mkdir -p /root/.claude-code-router &&
|
|
||||||
envsubst < /config/router-config.json > /root/.claude-code-router/config.json &&
|
|
||||||
ccr start"
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:3456:3456"
|
|
||||||
volumes:
|
|
||||||
- ./apps/cli/infra/router-config.json:/config/router-config.json:ro
|
|
||||||
environment:
|
|
||||||
- HOST=0.0.0.0
|
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
|
||||||
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
|
||||||
- ROUTER_DEFAULT=${ROUTER_DEFAULT:-openai,gpt-4o}
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3456/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
temporal-data:
|
|
||||||
Reference in New Issue
Block a user