mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-09-13 21:49:04 +02:00
feat: per-provider custom base URL, OpenAI Responses only (#445)
* feat: drop OpenAI chat-completions gateway format, keep Responses only * docs: reframe custom base URL as a universal endpoint override * docs: show optional base URL in the any-other-provider example
This commit is contained in:
+4
-8
@@ -22,21 +22,15 @@ SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6
|
||||
# SHANNON_AI_MODEL=amazon-bedrock:us.anthropic.claude-opus-4-8
|
||||
|
||||
# --- Custom Base URL ---------------------------------------------------------
|
||||
# Route through a proxy or gateway (LiteLLM, an internal endpoint).
|
||||
# Pick the block matching the API dialect your gateway speaks, and uncomment all
|
||||
# three lines. The provider prefix picks the dialect; the model id is whatever
|
||||
# name your gateway serves it under.
|
||||
|
||||
# Anthropic compatible - Anthropic Messages:
|
||||
# Anthropic Messages API:
|
||||
# SHANNON_AI_API_KEY=your-gateway-key-here
|
||||
# SHANNON_AI_BASE_URL=https://llm-gateway.example.com
|
||||
# SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6
|
||||
|
||||
# OpenAI compatible - Chat Completions (default) or Responses:
|
||||
# OpenAI Responses API:
|
||||
# SHANNON_AI_API_KEY=your-gateway-key-here
|
||||
# SHANNON_AI_BASE_URL=https://llm-gateway.example.com/v1
|
||||
# SHANNON_AI_MODEL=openai:gpt-5.5
|
||||
# SHANNON_AI_OPENAI_FORMAT=responses
|
||||
|
||||
# --- Other provider ----------------------------------------------------------
|
||||
# Any other provider the Pi harness supports. Name it in SHANNON_AI_MODEL and
|
||||
@@ -44,6 +38,8 @@ SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6
|
||||
# and model at preflight.
|
||||
# SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3
|
||||
# SHANNON_AI_API_KEY=your-api-key-here
|
||||
# Optional: point that provider at a proxy or LLM gateway.
|
||||
# SHANNON_AI_BASE_URL=https://llm-gateway.example.com
|
||||
|
||||
# --- Misc --------------------------------------------------------------------
|
||||
# Forward /etc/hosts entries into the worker container.
|
||||
|
||||
@@ -121,8 +121,8 @@ body:
|
||||
- "xAI"
|
||||
- "AWS Bedrock"
|
||||
- "Custom base URL - Anthropic Messages"
|
||||
- "Custom base URL - OpenAI Chat Completions"
|
||||
- "Custom base URL - OpenAI Responses"
|
||||
- "Other provider (Pi catalogue)"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ These reports are from Shannon Open Source scans of Photoview 2.4.0, one of the
|
||||
|
||||
- **Docker**: required for the worker container.
|
||||
- **Node.js 18+**: required for the recommended `npx` workflow.
|
||||
- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, [any other provider](docs/ai-providers.md#any-other-provider) in the harness catalogue, and any endpoint that speaks the Anthropic Messages API or the OpenAI Chat Completions or Responses API through a [custom base URL](docs/ai-providers.md#custom-base-url). You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic. See [AI providers](docs/ai-providers.md#suggested-models) for suggested model IDs.
|
||||
- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and [any other provider](docs/ai-providers.md#any-other-provider) in the harness catalogue — each of which you can point at a proxy or LLM gateway through a [custom base URL](docs/ai-providers.md#custom-base-url). You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic. See [AI providers](docs/ai-providers.md#suggested-models) for suggested model IDs.
|
||||
- **Cyber safeguards cleared with your provider**: Anthropic and OpenAI apply real-time safeguards to cyber-security workloads, which can interrupt a scan mid-run. Complete their guidance for legitimate security testers before your first run - see [AI providers](docs/ai-providers.md#cyber-safeguards-do-this-before-your-first-scan).
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ For source builds, authenticated scans, provider-specific setup, and platform no
|
||||
- **Resumable workspaces**: Resumes interrupted scans without repeating completed work.
|
||||
- **Native CI/CD integrations**: Runs through the official GitHub Action or GitLab CI/CD component, preserves artifacts, publishes findings, and gates releases on proven vulnerabilities.
|
||||
- **Multi-format reports**: Produces evidence-rich PDF and Markdown reports plus JSON and SARIF 2.1.0. SARIF is enabled by default for exploit-mode scans.
|
||||
- **Provider agnostic and BYOK**: Supports Anthropic, OpenAI, xAI, AWS Bedrock, compatible APIs and gateways, and local models served through Ollama, vLLM, or LM Studio.
|
||||
- **Provider agnostic and BYOK**: Supports Anthropic, OpenAI, xAI, AWS Bedrock, compatible APIs and LLM gateways, and local models served through Ollama, vLLM, or LM Studio.
|
||||
- **Private by design**: Runs in your infrastructure, stores results locally, and sends model requests directly to your chosen endpoint. A local endpoint keeps data inside your environment.
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ Use these guides for operational detail:
|
||||
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Source build and CLI commands](docs/development.md) | Cloning, building, common commands, output paths, and local development. |
|
||||
| [Configuration](docs/configuration.md) | Authenticated testing, login flows, rules of engagement, and report filters. |
|
||||
| [AI providers](docs/ai-providers.md) | Selecting the model, the supported providers (Anthropic, OpenAI, xAI, AWS Bedrock, and any other Pi-supported provider), and custom gateways. |
|
||||
| [AI providers](docs/ai-providers.md) | Selecting the model, the supported providers (Anthropic, OpenAI, xAI, AWS Bedrock, and any other Pi-supported provider), and custom LLM gateways. |
|
||||
| [Platforms and networking](docs/platforms.md) | Windows/WSL2, Linux, macOS, Docker networking, local apps, and custom hostnames. |
|
||||
| [Workspaces and resuming](docs/workspaces.md) | Naming workspaces, resuming interrupted scans, and workspace storage. |
|
||||
| [Safety and limitations](docs/safety.md) | Authorized-use requirements, non-production guidance, mutative effects, cost, and model caveats. |
|
||||
@@ -306,7 +306,7 @@ Important limitations:
|
||||
|
||||
- Shannon Open Source is tuned for fast, code-informed pentesting in everyday development and CI/CD. Exhaustive agentic SAST, broader scanner coverage, centralized governance, and full-lifecycle vulnerability management are delivered through the Keygraph Enterprise Platform.
|
||||
- Findings still require human review. LLM-generated reports can contain weakly supported or incorrect details.
|
||||
- Anthropic, OpenAI, xAI, and AWS Bedrock are built-in providers, and any Anthropic Messages API or OpenAI Chat Completions or Responses API endpoint works through a custom base URL. Model capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker results.
|
||||
- Anthropic, OpenAI, xAI, and AWS Bedrock are built-in providers, and any other provider in the harness catalogue works too — each reachable through a custom base URL that points it at a proxy or LLM gateway. Model capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker results.
|
||||
- A full run can take roughly 1 to 1.5 hours and may incur LLM API costs depending on model pricing and application complexity.
|
||||
- Do not scan untrusted or adversarial codebases. AI-powered tools that read source code can be exposed to prompt injection.
|
||||
|
||||
@@ -375,11 +375,11 @@ Yes. Shannon emits SARIF 2.1.0, the OASIS standard format for static analysis re
|
||||
|
||||
### Which AI providers does Shannon support?
|
||||
|
||||
Anthropic, OpenAI, xAI, and AWS Bedrock are built in and configured directly by provider ID. Beyond those, Shannon runs on any endpoint that implements the Anthropic Messages API or the OpenAI Chat Completions or Responses API, reached through a custom base URL. The rule is the API format, not the vendor. Shannon uses a single unified model setting throughout a pentest.
|
||||
Anthropic, OpenAI, xAI, and AWS Bedrock are built in and configured directly by provider ID. Beyond those, Shannon runs on any provider in the Pi harness catalogue, named the same `<provider>:<model-id>` way. Any provider can be pointed at a proxy or LLM gateway through a custom base URL, which overrides only the endpoint and keeps that provider's API dialect. Shannon uses a single unified model setting throughout a pentest.
|
||||
|
||||
### Can I run Shannon on a local or self-hosted model?
|
||||
|
||||
Shannon works with local models served through Ollama, vLLM, or LM Studio, which expose an OpenAI-compatible endpoint, as well as routers such as OpenRouter and gateways such as LiteLLM. Point Shannon at the endpoint with a custom base URL. Capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests than a frontier model, so take this path only if you know how your chosen model behaves. See [AI providers](docs/ai-providers.md#custom-base-url).
|
||||
Shannon works with local models served through Ollama, vLLM, or LM Studio, which expose an OpenAI-compatible endpoint, as well as routers such as OpenRouter and LLM gateways such as LiteLLM. Point Shannon at the endpoint with a custom base URL. Capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests than a frontier model, so take this path only if you know how your chosen model behaves. See [AI providers](docs/ai-providers.md#custom-base-url).
|
||||
|
||||
### Does Shannon actually exploit vulnerabilities, or just scan?
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ It analyzes your source code, identifies attack paths, and executes real exploit
|
||||
|
||||
- **Docker**: required for the worker container.
|
||||
- **Node.js 18+**: required for the recommended `npx` workflow.
|
||||
- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, any other provider in the harness catalogue, and any endpoint that speaks the Anthropic Messages API or the OpenAI Chat Completions or Responses API through a custom base URL. You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic.
|
||||
- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and any other provider in the harness catalogue — each of which you can point at a proxy or LLM gateway through a custom base URL. You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic.
|
||||
- **Cyber safeguards cleared with your provider**: Anthropic and OpenAI apply real-time safeguards to cyber-security workloads, which can interrupt a scan mid-run. Complete their guidance for legitimate security testers before your first run.
|
||||
|
||||
### Run Shannon
|
||||
|
||||
@@ -10,7 +10,7 @@ import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import * as p from '@clack/prompts';
|
||||
import { type ShannonConfig, saveConfig } from '../config/writer.js';
|
||||
import { CURATED_PROVIDERS, type CuratedProviderId, isCuratedProvider, type OpenAiFormat } from '../model-spec.js';
|
||||
import { CURATED_PROVIDERS, type CuratedProviderId, isCuratedProvider } from '../model-spec.js';
|
||||
import { displaySplash } from '../splash.js';
|
||||
import { requireInteractive } from '../tty.js';
|
||||
import { getVersion } from '../version.js';
|
||||
@@ -22,24 +22,16 @@ const CUSTOM_BASE_URL = '__custom_base_url__';
|
||||
const OTHER_PROVIDER = '__other_provider__';
|
||||
|
||||
/**
|
||||
* Wire formats reachable through the gateway route. The format picks the provider
|
||||
* that supplies the credential, and for OpenAI it also picks which of the two
|
||||
* OpenAI APIs Shannon calls.
|
||||
* API dialects reachable through the gateway route. The dialect picks the provider
|
||||
* that supplies the credential and names the wire protocol the endpoint must speak.
|
||||
*/
|
||||
const GATEWAY_DIALECTS: readonly {
|
||||
value: string;
|
||||
label: string;
|
||||
provider: 'anthropic' | 'openai';
|
||||
format?: OpenAiFormat;
|
||||
}[] = [
|
||||
{ value: 'anthropic', label: 'Anthropic Messages', provider: 'anthropic' },
|
||||
{
|
||||
value: 'openai-chat-completions',
|
||||
label: 'OpenAI Chat Completions',
|
||||
provider: 'openai',
|
||||
format: 'chat-completions',
|
||||
},
|
||||
{ value: 'openai-responses', label: 'OpenAI Responses', provider: 'openai', format: 'responses' },
|
||||
{ value: 'openai', label: 'OpenAI Responses', provider: 'openai' },
|
||||
];
|
||||
|
||||
/** Suggested models per curated provider, best-first. Free-text entry accepts any model in the provider's catalogue. */
|
||||
@@ -78,7 +70,11 @@ export async function setup(): Promise<void> {
|
||||
{ value: 'openai' as const, label: 'OpenAI', hint: 'GPT models' },
|
||||
{ value: 'xai' as const, label: 'xAI', hint: 'Grok models' },
|
||||
{ value: 'amazon-bedrock' as const, label: 'AWS Bedrock', hint: 'Claude models via AWS' },
|
||||
{ value: CUSTOM_BASE_URL as typeof CUSTOM_BASE_URL, label: 'Custom Base URL', hint: 'your own proxy or gateway' },
|
||||
{
|
||||
value: CUSTOM_BASE_URL as typeof CUSTOM_BASE_URL,
|
||||
label: 'Custom Base URL',
|
||||
hint: 'route through a proxy or LLM gateway',
|
||||
},
|
||||
{
|
||||
value: OTHER_PROVIDER as typeof OTHER_PROVIDER,
|
||||
label: 'Other provider',
|
||||
@@ -88,20 +84,21 @@ export async function setup(): Promise<void> {
|
||||
});
|
||||
if (p.isCancel(selected)) return cancelAndExit();
|
||||
|
||||
// 2. Credentials — and, on the gateway route, the endpoint and its dialect.
|
||||
const { provider, config, gateway } = await setupSelection(selected);
|
||||
// 2. Credentials, and any endpoint override. A base URL overrides the endpoint
|
||||
// for whichever provider is chosen — the curated gateway route names it via
|
||||
// the dialect, the "Other provider" route asks for it directly.
|
||||
const { provider, config, baseUrl } = await setupSelection(selected);
|
||||
|
||||
// 3. The model that runs every phase.
|
||||
const modelId = await promptModel(provider);
|
||||
config.core = { ...config.core, model: `${provider}:${modelId}` };
|
||||
if (gateway) config.core = { ...config.core, base_url: gateway.baseUrl };
|
||||
if (baseUrl) config.core = { ...config.core, base_url: baseUrl };
|
||||
|
||||
saveConfig(config);
|
||||
|
||||
const configPath = path.join(SHANNON_HOME, 'config.toml');
|
||||
const summary = [`Provider ${provider}`, `Model ${modelId}`];
|
||||
if (gateway) summary.push(`Endpoint ${gateway.baseUrl}`);
|
||||
if (gateway?.format) summary.push(`API ${gateway.format}`);
|
||||
if (baseUrl) summary.push(`Endpoint ${baseUrl}`);
|
||||
|
||||
p.log.success(`Configuration saved to ${configPath}`);
|
||||
p.log.info(summary.join('\n'));
|
||||
@@ -111,7 +108,7 @@ export async function setup(): Promise<void> {
|
||||
interface Selection {
|
||||
provider: string;
|
||||
config: ShannonConfig;
|
||||
gateway?: GatewaySetup;
|
||||
baseUrl?: string;
|
||||
}
|
||||
|
||||
/** Resolve the provider selection into a provider id and its credential config. */
|
||||
@@ -120,7 +117,7 @@ async function setupSelection(
|
||||
): Promise<Selection> {
|
||||
if (selected === CUSTOM_BASE_URL) {
|
||||
const gateway = await setupGateway();
|
||||
return { provider: gateway.provider, config: gateway.config, gateway };
|
||||
return { provider: gateway.provider, config: gateway.config, baseUrl: gateway.baseUrl };
|
||||
}
|
||||
if (selected === OTHER_PROVIDER) {
|
||||
return setupOtherProvider();
|
||||
@@ -144,6 +141,8 @@ async function setupProvider(provider: CuratedProviderId): Promise<ShannonConfig
|
||||
/**
|
||||
* Any pi provider Shannon does not curate. The id is free text — the worker's
|
||||
* preflight validates it — and the key is stored generically as SHANNON_AI_API_KEY.
|
||||
* An optional base URL points that provider at a proxy or LLM gateway; left blank, the
|
||||
* provider's own endpoint is used.
|
||||
*/
|
||||
async function setupOtherProvider(): Promise<Selection> {
|
||||
p.log.info('Browse supported providers and models at https://pi.dev/models');
|
||||
@@ -159,7 +158,13 @@ async function setupOtherProvider(): Promise<Selection> {
|
||||
if (p.isCancel(provider)) return cancelAndExit();
|
||||
|
||||
const apiKey = await promptSecret('Enter the API key');
|
||||
return { provider: provider.trim(), config: { provider: { api_key: apiKey } } };
|
||||
const baseUrl = await promptOptionalBaseUrl();
|
||||
|
||||
return {
|
||||
provider: provider.trim(),
|
||||
config: { provider: { api_key: apiKey } },
|
||||
...(baseUrl && { baseUrl }),
|
||||
};
|
||||
}
|
||||
|
||||
// === Provider Setup Flows ===
|
||||
@@ -200,11 +205,10 @@ interface GatewaySetup {
|
||||
provider: CuratedProviderId;
|
||||
config: ShannonConfig;
|
||||
baseUrl: string;
|
||||
format?: OpenAiFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gateway route: the endpoint decides where requests go, but the format still
|
||||
* Gateway route: the endpoint decides where requests go, but the dialect still
|
||||
* picks a real provider, because that is what supplies the credential and the
|
||||
* wire protocol.
|
||||
*/
|
||||
@@ -236,11 +240,9 @@ async function setupGateway(): Promise<GatewaySetup> {
|
||||
|
||||
const authToken = await promptSecret('Enter the auth token for the endpoint');
|
||||
const config: ShannonConfig =
|
||||
provider === 'anthropic'
|
||||
? { anthropic: { api_key: authToken } }
|
||||
: { openai: { api_key: authToken, ...(dialect.format && { format: dialect.format }) } };
|
||||
provider === 'anthropic' ? { anthropic: { api_key: authToken } } : { openai: { api_key: authToken } };
|
||||
|
||||
return { provider, config, baseUrl, ...(dialect.format && { format: dialect.format }) };
|
||||
return { provider, config, baseUrl };
|
||||
}
|
||||
|
||||
// === Model Selection ===
|
||||
@@ -308,6 +310,31 @@ async function promptModelId(provider: string, placeholder?: string): Promise<st
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
/**
|
||||
* Optional endpoint override. Empty input means the provider's default endpoint;
|
||||
* any value must be a valid URL.
|
||||
*/
|
||||
async function promptOptionalBaseUrl(): Promise<string | undefined> {
|
||||
const baseUrl = await p.text({
|
||||
message: 'Custom base URL (optional, leave blank for the provider default)',
|
||||
placeholder: 'https://llm-gateway.example.com',
|
||||
validate: (value) => {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed) return undefined;
|
||||
try {
|
||||
new URL(trimmed);
|
||||
} catch {
|
||||
return 'Must be a valid URL';
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
if (p.isCancel(baseUrl)) return cancelAndExit();
|
||||
|
||||
const trimmed = baseUrl?.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
async function promptSecret(message: string): Promise<string> {
|
||||
const value = await p.password({
|
||||
message,
|
||||
|
||||
@@ -40,9 +40,8 @@ const CONFIG_MAP: readonly ConfigMapping[] = [
|
||||
{ env: 'ANTHROPIC_API_KEY', toml: 'anthropic.api_key', type: 'string' },
|
||||
{ env: 'CLAUDE_CODE_OAUTH_TOKEN', toml: 'anthropic.oauth_token', type: 'string' },
|
||||
|
||||
// OpenAI — format picks the wire API a gateway serves
|
||||
// OpenAI
|
||||
{ env: 'OPENAI_API_KEY', toml: 'openai.api_key', type: 'string' },
|
||||
{ env: 'SHANNON_AI_OPENAI_FORMAT', toml: 'openai.format', type: 'string' },
|
||||
|
||||
// xAI
|
||||
{ env: 'XAI_API_KEY', toml: 'xai.api_key', type: 'string' },
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getConfigFile } from '../home.js';
|
||||
export interface ShannonConfig {
|
||||
core?: { model?: string; base_url?: string };
|
||||
anthropic?: { api_key?: string; oauth_token?: string };
|
||||
openai?: { api_key?: string; format?: string };
|
||||
openai?: { api_key?: string };
|
||||
xai?: { api_key?: string };
|
||||
bedrock?: { region?: string; token?: string };
|
||||
/** Generic credential for any provider Shannon does not curate. Maps to SHANNON_AI_API_KEY. */
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
const COMMON_FORWARD_VARS = [
|
||||
'SHANNON_AI_MODEL',
|
||||
'SHANNON_AI_BASE_URL',
|
||||
'SHANNON_AI_OPENAI_FORMAT',
|
||||
// Opt-in debug flag: when set, the worker persists a bounded, sanitized snippet of a failed
|
||||
// provider turn's raw error message to error.log. Off by default; provider prose stays out of
|
||||
// durable state unless an operator deliberately enables it for a diagnosis.
|
||||
|
||||
@@ -52,15 +52,6 @@ export const PROVIDER_CREDENTIAL_HINT: Readonly<Record<CuratedProviderId, string
|
||||
/** Model used when SHANNON_AI_MODEL is unset. */
|
||||
export const DEFAULT_MODEL_SPEC = 'anthropic:claude-sonnet-4-6';
|
||||
|
||||
/**
|
||||
* Values SHANNON_AI_OPENAI_FORMAT accepts, selecting the wire format an
|
||||
* OpenAI-compatible gateway serves. Mirrors OPENAI_FORMATS in
|
||||
* apps/worker/src/ai/models.ts; the worker validates and applies it.
|
||||
*/
|
||||
export const OPENAI_FORMATS = ['chat-completions', 'responses'] as const;
|
||||
|
||||
export type OpenAiFormat = (typeof OPENAI_FORMATS)[number];
|
||||
|
||||
export interface ModelSpec {
|
||||
providerId: string;
|
||||
modelId: string;
|
||||
|
||||
@@ -78,42 +78,6 @@ export const DEFAULT_MODEL_SPEC = 'anthropic:claude-sonnet-4-6';
|
||||
/** Browsable pi model catalogue — the source of valid `<provider>:<model-id>` ids. */
|
||||
export const PI_CATALOG_URL = 'https://pi.dev/models';
|
||||
|
||||
/**
|
||||
* Wire formats an OpenAI-compatible gateway may serve, named by
|
||||
* SHANNON_AI_OPENAI_FORMAT. Only `openai` offers a choice: every other supported
|
||||
* provider has exactly one API in pi's registry.
|
||||
*/
|
||||
export const OPENAI_FORMATS = {
|
||||
'chat-completions': 'openai-completions',
|
||||
responses: 'openai-responses',
|
||||
} as const;
|
||||
|
||||
export type OpenAiFormat = keyof typeof OPENAI_FORMATS;
|
||||
|
||||
/** Format assumed when a gateway is configured but no format is named. */
|
||||
export const DEFAULT_OPENAI_FORMAT: OpenAiFormat = 'chat-completions';
|
||||
|
||||
function isOpenAiFormat(value: string): value is OpenAiFormat {
|
||||
return value in OPENAI_FORMATS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SHANNON_AI_OPENAI_FORMAT. Unset returns undefined, which lets the caller
|
||||
* distinguish "not configured" from an explicit choice and reject the variable
|
||||
* where it has no effect.
|
||||
*/
|
||||
export function resolveOpenAiFormat(): OpenAiFormat | undefined {
|
||||
const raw = process.env.SHANNON_AI_OPENAI_FORMAT?.trim();
|
||||
if (!raw) return undefined;
|
||||
|
||||
if (!isOpenAiFormat(raw)) {
|
||||
throw new Error(
|
||||
`SHANNON_AI_OPENAI_FORMAT must be one of: ${Object.keys(OPENAI_FORMATS).join(', ')}. Got "${raw}".`,
|
||||
);
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
export interface ModelSpec {
|
||||
providerId: string;
|
||||
modelId: string;
|
||||
@@ -256,26 +220,6 @@ export interface ModelSelection {
|
||||
readonly credentialSource: 'api-key' | 'pi-auth' | 'ambient';
|
||||
}
|
||||
|
||||
/**
|
||||
* Point a model descriptor at a gateway.
|
||||
*
|
||||
* An OpenAI gateway may serve either wire format, named by
|
||||
* SHANNON_AI_OPENAI_FORMAT and defaulting to chat completions, which is what
|
||||
* most gateway software exposes. Switching to completions also drops the stored
|
||||
* `compat` block: the catalogue's block describes Responses, and an explicit
|
||||
* entry outranks pi's `detectCompat`, so leaving it would apply Responses
|
||||
* settings to a completions request. Staying on Responses keeps it, since it
|
||||
* then describes the format in use. Every other provider has one API and only
|
||||
* changes address.
|
||||
*/
|
||||
function pointAtGateway(model: Model<Api>, providerId: string, baseUrl: string, format: OpenAiFormat): Model<Api> {
|
||||
if (providerId !== 'openai') return { ...model, baseUrl };
|
||||
if (format === 'responses') return { ...model, baseUrl, api: OPENAI_FORMATS.responses };
|
||||
|
||||
const { compat: _responsesCompat, ...withoutCompat } = model;
|
||||
return { ...withoutCompat, baseUrl, api: OPENAI_FORMATS['chat-completions'] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a model against a runtime.
|
||||
*
|
||||
@@ -293,44 +237,17 @@ export function resolveModel(
|
||||
providerId: string,
|
||||
modelId: string,
|
||||
baseUrl: string | undefined,
|
||||
format: OpenAiFormat = DEFAULT_OPENAI_FORMAT,
|
||||
): Model<Api> | undefined {
|
||||
const found = modelRuntime.getModel(providerId, modelId);
|
||||
if (found) {
|
||||
return baseUrl ? pointAtGateway(found, providerId, baseUrl, format) : found;
|
||||
return baseUrl ? { ...found, baseUrl } : found;
|
||||
}
|
||||
if (!baseUrl) return undefined;
|
||||
|
||||
const reference = modelRuntime.getModels(providerId)[0];
|
||||
if (!reference) return undefined;
|
||||
|
||||
return pointAtGateway({ ...reference, id: modelId, name: modelId }, providerId, baseUrl, format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate SHANNON_AI_OPENAI_FORMAT against the rest of the configuration and
|
||||
* return the format a gateway run should use.
|
||||
*
|
||||
* The variable only reaches a request when both an OpenAI model and a gateway
|
||||
* are configured, so it is rejected outside that combination rather than
|
||||
* silently ignored.
|
||||
*/
|
||||
export function resolveGatewayFormat(providerId: string, baseUrl: string | undefined): OpenAiFormat {
|
||||
const configured = resolveOpenAiFormat();
|
||||
if (!configured) return DEFAULT_OPENAI_FORMAT;
|
||||
|
||||
if (providerId !== 'openai') {
|
||||
throw new Error(
|
||||
`SHANNON_AI_OPENAI_FORMAT applies to openai models only, but SHANNON_AI_MODEL selects "${providerId}". ` +
|
||||
`${providerId} serves a single API, so there is no format to choose.`,
|
||||
);
|
||||
}
|
||||
if (!baseUrl) {
|
||||
throw new Error(
|
||||
'SHANNON_AI_OPENAI_FORMAT applies to gateway runs only. Set SHANNON_AI_BASE_URL, or unset the format to call OpenAI directly.',
|
||||
);
|
||||
}
|
||||
return configured;
|
||||
return { ...reference, id: modelId, name: modelId, baseUrl };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,12 +257,11 @@ export function resolveGatewayFormat(providerId: string, baseUrl: string | undef
|
||||
export async function resolveModelSelection(): Promise<ModelSelection> {
|
||||
const { providerId, modelId } = resolveModelSpec();
|
||||
const credentials = resolveProviderCredentials(providerId);
|
||||
const format = resolveGatewayFormat(providerId, credentials.baseUrl);
|
||||
|
||||
const mountedPiAuth = piAuthPresent();
|
||||
const modelRuntime = await createModelRuntime(providerId, credentials.apiKey);
|
||||
|
||||
const model = resolveModel(modelRuntime, providerId, modelId, credentials.baseUrl, format);
|
||||
const model = resolveModel(modelRuntime, providerId, modelId, credentials.baseUrl);
|
||||
if (!model) {
|
||||
throw new Error(
|
||||
`Model not found in pi registry: provider="${providerId}" model="${modelId}". Browse valid providers and models at ${PI_CATALOG_URL}.`,
|
||||
|
||||
@@ -40,10 +40,8 @@ import {
|
||||
createModelRuntime,
|
||||
GENERIC_API_KEY_ENV,
|
||||
type ModelSpec,
|
||||
type OpenAiFormat,
|
||||
PI_CATALOG_URL,
|
||||
piAuthPresent,
|
||||
resolveGatewayFormat,
|
||||
resolveModel,
|
||||
resolveModelSpec,
|
||||
resolveProviderCredentials,
|
||||
@@ -329,24 +327,7 @@ async function validateCredentials(logger: ActivityLogger): Promise<Result<void,
|
||||
// needs one API key.
|
||||
const credentials = resolveProviderCredentials(spec.providerId);
|
||||
|
||||
// 3. Wire format for an OpenAI gateway. Rejects a format named where it cannot
|
||||
// take effect, rather than letting the run proceed on the wrong API.
|
||||
let format: OpenAiFormat;
|
||||
try {
|
||||
format = resolveGatewayFormat(spec.providerId, credentials.baseUrl);
|
||||
} catch (error) {
|
||||
return err(
|
||||
new PentestError(
|
||||
error instanceof Error ? error.message : String(error),
|
||||
'config',
|
||||
false,
|
||||
{ providerId: spec.providerId },
|
||||
ErrorCode.AUTH_FAILED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// With a mounted pi auth.json the env-var checks don't apply — step 5's probe validates it.
|
||||
// With a mounted pi auth.json the env-var checks don't apply — step 4's probe validates it.
|
||||
const isBedrock = spec.providerId === 'amazon-bedrock';
|
||||
const missing =
|
||||
isBedrock && !piAuthPresent() ? ['AWS_REGION', 'AWS_BEARER_TOKEN_BEDROCK'].filter((n) => !process.env[n]) : [];
|
||||
@@ -362,12 +343,12 @@ async function validateCredentials(logger: ActivityLogger): Promise<Result<void,
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Model must exist in the registry, for every provider — Bedrock IDs are the
|
||||
// 3. Model must exist in the registry, for every provider — Bedrock IDs are the
|
||||
// easiest to get wrong, since region prefixes and version suffixes differ per
|
||||
// model (`us.anthropic.claude-opus-5` exists, bare `anthropic.` does not).
|
||||
// A custom endpoint is exempt: it may serve models under its own names.
|
||||
const modelRuntime = await createModelRuntime(spec.providerId, credentials.apiKey);
|
||||
const baseModel = resolveModel(modelRuntime, spec.providerId, spec.modelId, credentials.baseUrl, format);
|
||||
const baseModel = resolveModel(modelRuntime, spec.providerId, spec.modelId, credentials.baseUrl);
|
||||
if (!baseModel) {
|
||||
return err(
|
||||
new PentestError(
|
||||
@@ -385,10 +366,10 @@ async function validateCredentials(logger: ActivityLogger): Promise<Result<void,
|
||||
);
|
||||
}
|
||||
if (credentials.baseUrl && spec.providerId === 'openai') {
|
||||
logger.info(`Gateway API: ${format} (${baseModel.api})`);
|
||||
logger.info(`Gateway API: ${baseModel.api}`);
|
||||
}
|
||||
|
||||
// 5. One real request, so a credential the account cannot use fails here
|
||||
// 4. One real request, so a credential the account cannot use fails here
|
||||
// rather than partway through the run. Bedrock included: pi resolves the
|
||||
// bearer token from the primed credential and the region from AWS_REGION,
|
||||
// so the probe exercises the same auth path the scan will.
|
||||
|
||||
+14
-26
@@ -28,8 +28,9 @@ Shannon forwards only the selected provider's credential into the scan container
|
||||
Shannon accepts any provider and model present in the Pi harness catalogue. Browse them at [pi.dev/models](https://pi.dev/models).
|
||||
|
||||
```bash
|
||||
export SHANNON_AI_API_KEY=your-api-key # the provider's API key
|
||||
export SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3 # <provider>:<model-id>
|
||||
export SHANNON_AI_API_KEY=your-api-key # the provider's key — or the gateway's when a base URL is set
|
||||
export SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3 # <provider>:<model-id>
|
||||
export SHANNON_AI_BASE_URL=https://llm-gateway.example.com # optional: route through a proxy or LLM gateway
|
||||
```
|
||||
|
||||
This path covers providers whose credential is a single API key. Providers that need more than that are not currently supported.
|
||||
@@ -48,7 +49,7 @@ Review each vendor's guidance and complete the verification or enrollment they a
|
||||
- Anthropic - [Real-time cyber safeguards on Claude Opus and Sonnet](https://support.claude.com/en/articles/14604842-real-time-cyber-safeguards-on-claude-opus-and-sonnet)
|
||||
- OpenAI - [Cyber](https://chatgpt.com/cyber)
|
||||
|
||||
This applies to the Anthropic and OpenAI providers, including when either is reached through a gateway. Bedrock serves Claude models and is subject to Anthropic's safeguards as well.
|
||||
This applies to the Anthropic and OpenAI providers, including when either is reached through an LLM gateway. Bedrock serves Claude models and is subject to Anthropic's safeguards as well.
|
||||
|
||||
## Suggested models
|
||||
|
||||
@@ -104,17 +105,16 @@ Bedrock uses bearer-token authentication only. IAM access keys, session tokens,
|
||||
|
||||
## Custom base URL
|
||||
|
||||
To route model traffic through your own infrastructure — a corporate proxy, an LLM gateway such as LiteLLM, or a regional endpoint — set a base URL alongside your normal model selection. The provider half of `SHANNON_AI_MODEL` decides which key is sent and which API Shannon speaks, so pick the one your gateway serves:
|
||||
`SHANNON_AI_BASE_URL` routes model traffic through a proxy or LLM gateway instead of the provider's default endpoint — an LLM gateway such as LiteLLM, a regional endpoint, or any other host you choose. It is a plain endpoint override: it changes only *where* requests go. The provider half of `SHANNON_AI_MODEL` still decides which credential is sent and which API dialect is spoken, and that is unchanged by the base URL.
|
||||
|
||||
| Gateway serves | Model prefix | API key |
|
||||
| --- | --- | --- |
|
||||
| Anthropic Messages | `anthropic:` | `SHANNON_AI_API_KEY` |
|
||||
| OpenAI Chat Completions | `openai:` | `SHANNON_AI_API_KEY` |
|
||||
| OpenAI Responses | `openai:` + `SHANNON_AI_OPENAI_FORMAT=responses` | `SHANNON_AI_API_KEY` |
|
||||
This works for **any** provider, curated or not. The one rule is that a provider's dialect is fixed, so the endpoint you point at must speak that provider's dialect:
|
||||
|
||||
The model ID is whatever name your gateway serves it under; it does not have to exist in Shannon's catalogue.
|
||||
| Provider prefix | Dialect the endpoint must speak |
|
||||
| --- | --- |
|
||||
| `anthropic:` | Anthropic Messages |
|
||||
| `openai:` | OpenAI Responses |
|
||||
|
||||
Anthropic Messages:
|
||||
Anthropic Messages LLM gateway:
|
||||
|
||||
```bash
|
||||
export SHANNON_AI_API_KEY=sk-ant-...
|
||||
@@ -122,7 +122,7 @@ export SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6
|
||||
export SHANNON_AI_BASE_URL=https://llm-gateway.example.com
|
||||
```
|
||||
|
||||
OpenAI Chat Completions:
|
||||
OpenAI Responses LLM gateway:
|
||||
|
||||
```bash
|
||||
export SHANNON_AI_API_KEY=sk-...
|
||||
@@ -130,19 +130,7 @@ export SHANNON_AI_MODEL=openai:gpt-5.6-sol
|
||||
export SHANNON_AI_BASE_URL=https://llm-gateway.example.com/v1
|
||||
```
|
||||
|
||||
`SHANNON_AI_MODEL` is always `<provider>:<model-id>`, gateway or not.
|
||||
|
||||
OpenAI is the one provider serving two APIs, so a gateway run picks one:
|
||||
|
||||
```bash
|
||||
export SHANNON_AI_OPENAI_FORMAT=responses # default: chat-completions
|
||||
```
|
||||
|
||||
Chat Completions is the default because that is what most gateway software exposes. Set `responses` for a gateway that passes the Responses API through — it preserves reasoning state between turns, which Chat Completions cannot. `openai:gpt-5` with no base URL always calls OpenAI's Responses API directly.
|
||||
|
||||
The variable is rejected in preflight where it cannot take effect: with a non-`openai` model, since Anthropic, xAI, and Bedrock each serve one API, and with no `SHANNON_AI_BASE_URL`, since a direct OpenAI run is always Responses.
|
||||
|
||||
`npx @keygraph/shannon setup` covers this under **Custom Base URL**, which asks which API your gateway serves and configures the matching provider for you.
|
||||
`npx @keygraph/shannon setup` configures a base URL two ways: **Custom Base URL** covers the common Anthropic Messages and OpenAI Responses LLM gateways, and **Other provider** takes any provider ID plus an optional base URL of its own.
|
||||
|
||||
## OpenAI Codex (ChatGPT Plus/Pro subscription)
|
||||
|
||||
@@ -209,7 +197,7 @@ These instructions apply only to `shannon-v1`.
|
||||
|
||||
Checks run before a scan starts, so mistakes fail immediately rather than partway through a run:
|
||||
|
||||
- **Provider and model ID** — validated against the Pi harness catalogue. An unknown provider or model ID fails preflight with a pointer to [pi.dev/models](https://pi.dev/models). A custom base URL exempts the model ID, since a gateway may serve its own names.
|
||||
- **Provider and model ID** — validated against the Pi harness catalogue. An unknown provider or model ID fails preflight with a pointer to [pi.dev/models](https://pi.dev/models). A custom base URL exempts the model ID, since an LLM gateway may serve its own names.
|
||||
- **Credential presence** — validated for the selected provider, or read from Pi when `SHANNON_USE_PI_AUTH=1`.
|
||||
- **Credential validity** — one minimal request against the model the scan will use, so a rejected key, an exhausted quota, or a model the account cannot reach fails before any agent runs. Bedrock included: its bearer token and region go through the same probe.
|
||||
|
||||
|
||||
+20
-32
@@ -97,7 +97,7 @@ Sample penetration test reports from intentionally vulnerable applications, prod
|
||||
|
||||
- **Docker**: required for the worker container.
|
||||
- **Node.js 18+**: required for the recommended `npx` workflow.
|
||||
- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, [any other provider](docs/ai-providers.md#any-other-provider) in the harness catalogue, and any endpoint that speaks the Anthropic Messages API or the OpenAI Chat Completions or Responses API through a [custom base URL](docs/ai-providers.md#custom-base-url). You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic. See [AI providers](docs/ai-providers.md#suggested-models) for suggested model IDs.
|
||||
- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and [any other provider](docs/ai-providers.md#any-other-provider) in the harness catalogue — each of which you can point at a proxy or LLM gateway through a [custom base URL](docs/ai-providers.md#custom-base-url). You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic. See [AI providers](docs/ai-providers.md#suggested-models) for suggested model IDs.
|
||||
- **Cyber safeguards cleared with your provider**: Anthropic and OpenAI apply real-time safeguards to cyber-security workloads, which can interrupt a scan mid-run. Complete their guidance for legitimate security testers before your first run - see [AI providers](docs/ai-providers.md#cyber-safeguards-do-this-before-your-first-scan).
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ For source builds, authenticated scans, provider-specific setup, and platform no
|
||||
- **Resumable workspaces**: Shannon can resume interrupted runs without re-running completed agents.
|
||||
- **Native CI/CD integrations**: Run Shannon through the official GitHub Action or reusable GitLab CI/CD component. Preserve reports, SARIF, and logs as pipeline artifacts; publish findings into native security workflows; and gate releases only on vulnerabilities Shannon actually demonstrates.
|
||||
- **Professional and machine-readable reports**: Shannon generates evidence-rich PDF and Markdown reports plus structured JSON and SARIF 2.1.0. SARIF is enabled by default on exploit-mode scans and can be disabled with `report.sarif: "false"`.
|
||||
- **Bring your own key, provider-agnostic**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and any endpoint speaking the Anthropic Messages API or the OpenAI Chat Completions or Responses API, including self-hosted models served through Ollama, vLLM, or LM Studio and gateways such as OpenRouter and LiteLLM. You supply the credentials and choose exactly where model traffic goes. Local and self-hosted models are supported.
|
||||
- **Bring your own key, provider-agnostic**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and any other provider in the Pi harness catalogue, each of which you can point at a proxy or LLM gateway with a custom base URL — including self-hosted models served through Ollama, vLLM, or LM Studio and LLM gateways such as OpenRouter and LiteLLM. You supply the credentials and choose exactly where model traffic goes. Local and self-hosted models are supported.
|
||||
- **Private by design**: Shannon runs inside your infrastructure and writes results to a local workspace. Model requests go straight to the provider or endpoint you configure, and they carry source and application context with them, so choose that endpoint deliberately. Point Shannon at a local model endpoint and nothing leaves your environment.
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ Use these guides for operational detail:
|
||||
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Source build and CLI commands](docs/development.md) | Cloning, building, common commands, output paths, and local development. |
|
||||
| [Configuration](docs/configuration.md) | Authenticated testing, login flows, rules of engagement, and report filters. |
|
||||
| [AI providers](docs/ai-providers.md) | Selecting the model, the supported providers (Anthropic, OpenAI, xAI, AWS Bedrock, and any other Pi-supported provider), and custom gateways. |
|
||||
| [AI providers](docs/ai-providers.md) | Selecting the model, the supported providers (Anthropic, OpenAI, xAI, AWS Bedrock, and any other Pi-supported provider), and custom LLM gateways. |
|
||||
| [Platforms and networking](docs/platforms.md) | Windows/WSL2, Linux, macOS, Docker networking, local apps, and custom hostnames. |
|
||||
| [Workspaces and resuming](docs/workspaces.md) | Naming workspaces, resuming interrupted scans, and workspace storage. |
|
||||
| [Safety and limitations](docs/safety.md) | Authorized-use requirements, non-production guidance, mutative effects, cost, and model caveats. |
|
||||
@@ -285,7 +285,7 @@ Important limitations:
|
||||
|
||||
- Shannon Open Source is tuned for fast, code-informed pentesting in everyday development and CI/CD. Exhaustive agentic SAST, broader scanner coverage, centralized governance, and full-lifecycle vulnerability management are delivered through the Keygraph Enterprise Platform.
|
||||
- Findings still require human review. LLM-generated reports can contain weakly supported or incorrect details.
|
||||
- Anthropic, OpenAI, xAI, and AWS Bedrock are built-in providers, and any Anthropic Messages API or OpenAI Chat Completions or Responses API endpoint works through a custom base URL. Model capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker results.
|
||||
- Anthropic, OpenAI, xAI, and AWS Bedrock are built-in providers, and any other provider in the harness catalogue works too — each reachable through a custom base URL that points it at a proxy or LLM gateway. Model capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker results.
|
||||
- A full run can take roughly 1 to 1.5 hours and may incur LLM API costs depending on model pricing and application complexity.
|
||||
- Do not scan untrusted or adversarial codebases. AI-powered tools that read source code can be exposed to prompt injection.
|
||||
|
||||
@@ -346,11 +346,11 @@ Yes. Shannon emits SARIF 2.1.0, the OASIS standard format for static analysis re
|
||||
|
||||
### Which AI providers does Shannon support?
|
||||
|
||||
Anthropic, OpenAI, xAI, and AWS Bedrock are built in and configured directly by provider ID. Beyond those, Shannon runs on any endpoint that implements the Anthropic Messages API or the OpenAI Chat Completions or Responses API, reached through a custom base URL. The rule is the API format, not the vendor. Shannon uses a single unified model setting throughout a pentest.
|
||||
Anthropic, OpenAI, xAI, and AWS Bedrock are built in and configured directly by provider ID. Beyond those, Shannon runs on any provider in the Pi harness catalogue, named the same `<provider>:<model-id>` way. Any provider can be pointed at a proxy or LLM gateway through a custom base URL, which overrides only the endpoint and keeps that provider's API dialect. Shannon uses a single unified model setting throughout a pentest.
|
||||
|
||||
### Can I run Shannon on a local or self-hosted model?
|
||||
|
||||
Shannon works with local models served through Ollama, vLLM, or LM Studio, which expose an OpenAI-compatible endpoint, as well as routers such as OpenRouter and gateways such as LiteLLM. Point Shannon at the endpoint with a custom base URL. Capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests than a frontier model, so take this path only if you know how your chosen model behaves. See [AI providers](docs/ai-providers.md#custom-base-url).
|
||||
Shannon works with local models served through Ollama, vLLM, or LM Studio, which expose an OpenAI-compatible endpoint, as well as routers such as OpenRouter and LLM gateways such as LiteLLM. Point Shannon at the endpoint with a custom base URL. Capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests than a frontier model, so take this path only if you know how your chosen model behaves. See [AI providers](docs/ai-providers.md#custom-base-url).
|
||||
|
||||
### Does Shannon actually exploit vulnerabilities, or just scan?
|
||||
|
||||
@@ -747,8 +747,9 @@ Shannon forwards only the selected provider's credential into the scan container
|
||||
Shannon accepts any provider and model present in the Pi harness catalogue. Browse them at [pi.dev/models](https://pi.dev/models).
|
||||
|
||||
```bash
|
||||
export SHANNON_AI_API_KEY=your-api-key # the provider's API key
|
||||
export SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3 # <provider>:<model-id>
|
||||
export SHANNON_AI_API_KEY=your-api-key # the provider's key — or the gateway's when a base URL is set
|
||||
export SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3 # <provider>:<model-id>
|
||||
export SHANNON_AI_BASE_URL=https://llm-gateway.example.com # optional: route through a proxy or LLM gateway
|
||||
```
|
||||
|
||||
This path covers providers whose credential is a single API key. Providers that need more than that are not currently supported.
|
||||
@@ -767,7 +768,7 @@ Review each vendor's guidance and complete the verification or enrollment they a
|
||||
- Anthropic - [Real-time cyber safeguards on Claude Opus and Sonnet](https://support.claude.com/en/articles/14604842-real-time-cyber-safeguards-on-claude-opus-and-sonnet)
|
||||
- OpenAI - [Cyber](https://chatgpt.com/cyber)
|
||||
|
||||
This applies to the Anthropic and OpenAI providers, including when either is reached through a gateway. Bedrock serves Claude models and is subject to Anthropic's safeguards as well.
|
||||
This applies to the Anthropic and OpenAI providers, including when either is reached through an LLM gateway. Bedrock serves Claude models and is subject to Anthropic's safeguards as well.
|
||||
|
||||
## Suggested models
|
||||
|
||||
@@ -823,17 +824,16 @@ Bedrock uses bearer-token authentication only. IAM access keys, session tokens,
|
||||
|
||||
## Custom base URL
|
||||
|
||||
To route model traffic through your own infrastructure — a corporate proxy, an LLM gateway such as LiteLLM, or a regional endpoint — set a base URL alongside your normal model selection. The provider half of `SHANNON_AI_MODEL` decides which key is sent and which API Shannon speaks, so pick the one your gateway serves:
|
||||
`SHANNON_AI_BASE_URL` routes model traffic through a proxy or LLM gateway instead of the provider's default endpoint — an LLM gateway such as LiteLLM, a regional endpoint, or any other host you choose. It is a plain endpoint override: it changes only *where* requests go. The provider half of `SHANNON_AI_MODEL` still decides which credential is sent and which API dialect is spoken, and that is unchanged by the base URL.
|
||||
|
||||
| Gateway serves | Model prefix | API key |
|
||||
| --- | --- | --- |
|
||||
| Anthropic Messages | `anthropic:` | `SHANNON_AI_API_KEY` |
|
||||
| OpenAI Chat Completions | `openai:` | `SHANNON_AI_API_KEY` |
|
||||
| OpenAI Responses | `openai:` + `SHANNON_AI_OPENAI_FORMAT=responses` | `SHANNON_AI_API_KEY` |
|
||||
This works for **any** provider, curated or not. The one rule is that a provider's dialect is fixed, so the endpoint you point at must speak that provider's dialect:
|
||||
|
||||
The model ID is whatever name your gateway serves it under; it does not have to exist in Shannon's catalogue.
|
||||
| Provider prefix | Dialect the endpoint must speak |
|
||||
| --- | --- |
|
||||
| `anthropic:` | Anthropic Messages |
|
||||
| `openai:` | OpenAI Responses |
|
||||
|
||||
Anthropic Messages:
|
||||
Anthropic Messages LLM gateway:
|
||||
|
||||
```bash
|
||||
export SHANNON_AI_API_KEY=sk-ant-...
|
||||
@@ -841,7 +841,7 @@ export SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6
|
||||
export SHANNON_AI_BASE_URL=https://llm-gateway.example.com
|
||||
```
|
||||
|
||||
OpenAI Chat Completions:
|
||||
OpenAI Responses LLM gateway:
|
||||
|
||||
```bash
|
||||
export SHANNON_AI_API_KEY=sk-...
|
||||
@@ -849,19 +849,7 @@ export SHANNON_AI_MODEL=openai:gpt-5.6-sol
|
||||
export SHANNON_AI_BASE_URL=https://llm-gateway.example.com/v1
|
||||
```
|
||||
|
||||
`SHANNON_AI_MODEL` is always `<provider>:<model-id>`, gateway or not.
|
||||
|
||||
OpenAI is the one provider serving two APIs, so a gateway run picks one:
|
||||
|
||||
```bash
|
||||
export SHANNON_AI_OPENAI_FORMAT=responses # default: chat-completions
|
||||
```
|
||||
|
||||
Chat Completions is the default because that is what most gateway software exposes. Set `responses` for a gateway that passes the Responses API through — it preserves reasoning state between turns, which Chat Completions cannot. `openai:gpt-5` with no base URL always calls OpenAI's Responses API directly.
|
||||
|
||||
The variable is rejected in preflight where it cannot take effect: with a non-`openai` model, since Anthropic, xAI, and Bedrock each serve one API, and with no `SHANNON_AI_BASE_URL`, since a direct OpenAI run is always Responses.
|
||||
|
||||
`npx @keygraph/shannon setup` covers this under **Custom Base URL**, which asks which API your gateway serves and configures the matching provider for you.
|
||||
`npx @keygraph/shannon setup` configures a base URL two ways: **Custom Base URL** covers the common Anthropic Messages and OpenAI Responses LLM gateways, and **Other provider** takes any provider ID plus an optional base URL of its own.
|
||||
|
||||
## OpenAI Codex (ChatGPT Plus/Pro subscription)
|
||||
|
||||
@@ -928,7 +916,7 @@ These instructions apply only to `shannon-v1`.
|
||||
|
||||
Checks run before a scan starts, so mistakes fail immediately rather than partway through a run:
|
||||
|
||||
- **Provider and model ID** — validated against the Pi harness catalogue. An unknown provider or model ID fails preflight with a pointer to [pi.dev/models](https://pi.dev/models). A custom base URL exempts the model ID, since a gateway may serve its own names.
|
||||
- **Provider and model ID** — validated against the Pi harness catalogue. An unknown provider or model ID fails preflight with a pointer to [pi.dev/models](https://pi.dev/models). A custom base URL exempts the model ID, since an LLM gateway may serve its own names.
|
||||
- **Credential presence** — validated for the selected provider, or read from Pi when `SHANNON_USE_PI_AUTH=1`.
|
||||
- **Credential validity** — one minimal request against the model the scan will use, so a rejected key, an exhausted quota, or a model the account cannot reach fails before any agent runs. Bedrock included: its bearer token and region go through the same probe.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Use this file as the concise entry point for AI agents and LLMs reading this rep
|
||||
|
||||
- [Development](docs/development.md): Source-build workflow, common CLI commands, repository paths, and output locations.
|
||||
- [Configuration](docs/configuration.md): Authenticated testing, login flows, rules of engagement, report filters, credential precedence, and rate-limit settings.
|
||||
- [AI Providers](docs/ai-providers.md): Anthropic, OpenAI, xAI, AWS Bedrock, any other Pi-supported provider, and custom gateway setup.
|
||||
- [AI Providers](docs/ai-providers.md): Anthropic, OpenAI, xAI, AWS Bedrock, any other Pi-supported provider, and custom LLM gateway setup.
|
||||
- [Platforms and Networking](docs/platforms.md): Windows/WSL2, Linux, macOS, Docker networking, local applications, and custom hostnames.
|
||||
- [Workspaces and Resuming](docs/workspaces.md): Workspace storage, naming, resuming interrupted scans, and examples.
|
||||
- [Safety and Limitations](docs/safety.md): Authorized-use requirements, non-production guidance, mutative effects, model caveats, scope limits, cost, and performance.
|
||||
|
||||
Reference in New Issue
Block a user