PEAK-Assistant is an AI-powered threat hunting assistant designed to guide hunters quickly through the process of researching and planning a hypothesis-driven hunt. It aligns with the PEAK Threat Hunting Framework and leverages large language models, teams of AI agents, and automated research tools to streamline the process of preparing for a hunt.
⛔️⛔️ The PEAK Assistant is intended solely as a proof-of-concept project to demonstrate the potential of agentic security solutions. It has not undergone security testing. Be cautious when deploying this to anything but a local system environment. ⛔️⛔️
The PEAK Assistant web app offers the following features:
- Generate detailed threat hunting research reports for specific techniques, tactics, or actors. It can access both Internet-based sources and local databases (ticket systems, wiki pages, threat intel platforms, etc).
- Suggest and refine threat hunting hypotheses based on the research it performed.
- Create PEAK ABLE tables to help scope the hunt.
- Automatically identify relevant indices, sourcetypes, and field names in your Splunk instance.
- Create step-by-step hunt plans, including guidance for how to analyze and interpret the results
- Export documents in Markdown format
- Upload documents that you have prepared yourself, so the AI doesn't have to regenerate them.
- Integration with research and Splunk data sources via user-provided local or remote MCP servers, including OAuth2, API key, and bearer token authentication support
- Most phases incorporate a chat-like interface so you can collaborate with the assistant to refine outputs until they exactly right
- A Bring-Your-Own-Model approach, allowing you to use any LLM you like. You can set a default LLM to be used by all agents, or specify different LLMs for each agent, allowing you to choose which model(s) work best for you.
Clone the GitHub repo to a directory on your local system:
git clone https://github.com/Cisco-Talos/PEAK-Assistant
cd PEAK-AssistantWe strongly recommend you use 'uv' to manage your python environment. It will take care of creating a virtual environment and installing all the dependencies.
uv syncThe PEAK Assistant uses a .env file to store API keys and other sensitive configuration. An example file is provided to get you started:
cp .env.example .envEdit .env and fill in the values for the provider(s) you plan to use. You only need to configure the variables for the provider(s) referenced in your model_config.json — the rest can stay commented out. See Model Configuration below for details on setting up your provider.
Once that's done, you'll need to generate the TLS certificate and private key. The files must be named cert.pem and key.pem, and reside in the local directory (or a directory specified by --cert-dir on startup). You can do this manually like so:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"If you prefer, you can use the generate_certificates.sh utility to do this for you. Just run the script and follow the prompts.
./generate_certificates.shYou will also need to configure the MCP servers the assistant uses to research topics and discover available data. Create a file called mcp_servers.json in the root of the repository. This file has the same format as you might be used to if you have configured MCP servers in Claude Desktop or other popular chat applications. You can use the following example as a template:
{
"mcpServers": {
"tavily-search": {
"transport": "stdio",
"description": "Provides Internet searches",
"command": "npx",
"args": [
"-y",
"tavily-mcp@0.1.2"
],
"env": {
"TAVILY_API_KEY": "tvly-dev-YOUR-KEY"
}
},
"splunk-mcp": {
"description": "Connect to a running Splunk server and run searches.",
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://YOUR-SERVER-HOST:8089/services/mcp",
"--header",
"Authorization: Bearer YOUR-SPLUNK-AUTH-TOKEN"
]
},
"atlassian-remote-mcp": {
"transport": "sse",
"description": "Provides access to Jira and Confluence",
"url": "https://mcp.atlassian.com/v1/sse"
}
},
"serverGroups": {
"research-external": [
"tavily-search"
],
"local-data-search": [
"atlassian-remote-mcp"
],
"data_discovery": [
"splunk-mcp"
]
}
}You must provide MCP servers for all three required server groups:
- Internet search (e.g., Tavily) - for external research
- Local data sources (e.g., Atlassian MCP, Confluence, Jira) - for internal research based on past hunts and organizational knowledge
- SIEM/data platform (e.g., Splunk MCP server) - for automated data discovery
In this example configuration, we use Tavily for Internet searches, Atlassian's official MCP server to search Jira tickets and Confluence wiki pages, and the Splunk MCP server for data discovery.
Feel free to substitute MCP servers with functional equivalents. For example, if you have a different Internet search provider, replace the Tavily configuration with whatever you're using.
In addition to defining the servers, you'll also have to add them to the appropriate MCP server groups, to let the different agents know which they should be using.
The server groups are:
research-external: Used for any Internet searches in the topic research phaselocal-data-search: Used for searching any local data sources during the topic research phasedata_discovery: Allows access to Splunk (or whatever other SIEM you use) for purposes of automated data discovery.
You may add multiple MCP servers to each group if you would like the Assistant to have access to several sources. All three groups are required and must have at least one server configured.
The MCP server configuration supports environment variable interpolation using ${ENV_VAR} syntax. This allows you to keep sensitive credentials out of your configuration files:
{
"mcpServers": {
"tavily-search": {
"transport": "stdio",
"command": "npx",
"args": ["-y", "tavily-mcp@0.1.2"],
"env": {
"TAVILY_API_KEY": "${TAVILY_API_KEY}"
}
},
"secure-api": {
"transport": "http",
"url": "https://api.example.com/mcp",
"auth": {
"type": "bearer",
"token": "${API_BEARER_TOKEN}"
}
}
}
}Supported syntax:
${ENV_VAR}- Replace with environment variable value (raises error if not found)${ENV_VAR|default}- Replace with environment variable or use default value${ENV_VAR|null}- Replace with environment variable or empty string
Store your actual secrets in your .env file (see Environment Variables above) or set them as environment variables before running the application.
You'll find a list of all configured MCP servers on the Assistant's 'MCP Servers' tab. If an MCP server
requires authentication, you'll see an Authenticate button on the server's row. Clicking this button will
redirect you to the authentication provider for that server.
The PEAK Assistant supports OAuth2 authentication for remote MCP servers, as well as OAuth resource autodiscovery. If your MCP server also supports those, you should be automatically directed to the server's authentication provider when you click the Authenticate button.
The Assistant supports an optional file for providing "local context". This provides a way for you to give the LLM hints and guidance about your environment or preferences so you can adapt the AI to your needs without having to edit the prompts. If present, this context file should be named context.txt and should be placed in the root of the repository. It should contain information that helps the AI agents understand your specific environment, such as:
- Organizational structure and naming conventions
- Specific technologies and tools in use
- Known threat actors or campaigns relevant to your organization
- Compliance requirements or regulatory considerations
- Hints about where to find previous security incident tickets, hunt documentation, or other local data sources
- Details about your most frequently used data sources and how to access them
There is no specific format requirement, but you may find it helpful to have some sort of basic structure to help you maintain it easily over time. Here's a simple example:
Environmental hints:
- We use primarily Splunk SIEM and Zeek NIDS.
Local Information Sources:
- Always consult the following sources of information when you are preparing your local research
reports, using the Atlassian MCP server.
- Hunt team documents hunts in Confluence wiki, under the "Threat Hunting" space
- Hunt team tracks in-progress and upcoming hunts in Jira, under the "Threat Hunting" project
- The Atlassian server is my-cloud-tenant.atlassian.net.
Splunk hints:
- If you encounter base64-encoded data, and if you decide that you must decode it,
you can use the following sample SPL as a reference:
<your query>
| code field=base64_encoded_field method=base64 action=decode destfield=decoded_field
<the rest of your query>
Where the "field" parameter is the name of the field that has base64 data in it, and
the "destfield" parameter is the name of a new field you want to hold the decoded value.
- Some of the indices are extremely large and have many events. You will need to check your
SPL queries carefully to ensure that they are as efficient as possible. One good strategy
is to use 'tstats' whenever possible, rather than normal searches.
- Don't try to use accelerated datamodels. There are no datamodels on this server.
We strongly recommend you also include at least some basic information about your most important Splunk indices, sourcetypes, and fields. This will help the AI agents understand your data better and generate more accurate queries. It's not required, because the automated data discovery is actually pretty good, but it can be helpful.
The PEAK Assistant requires a model_config.json file to configure LLM providers and models. This file must be placed in the repository root (the directory from which you run the application).
The following example shows a basic configuration that uses OpenAI's gpt-4.1 model for everything.
{
"version": "1",
"providers": {
"openai": {
"type": "openai",
"config": {
"api_key": "${OPENAI_API_KEY}",
}
}
},
"defaults": {
"provider": "openai",
"model": "gpt-4.1"
}
}More complex configurations are also supported, including:
- Use of multiple LLM providers (Azure OpenAI, OpenAI, OpenAI-compatible local or remote, Anthropic)
- Per-agent model assignment
- Assigning models to groups of agents at once
- Environment variable interpolation
See MODEL_CONFIGURATION.md for the full guide.
Now that it is configured it's time to run the app. Since you installed this as a module, you can simply run the assistant:
uv run peak-assistantBy default, the application will be available at https://127.0.0.1:8501/.
The PEAK-Assistant follows a structured workflow that aligns with the PEAK Threat Hunting Framework:
- Research Phase: Generate comprehensive research reports on specified cybersecurity techniques or threat actors based on public Internet sources
- Local Research Phase: Query local data sources such as incident tickets, hunt documentation, and threat intel databases for relevant information that will supplements the public research and can be used to tailor the plan
- Hypothesis Generation: Create testable hypotheses based on the research findings
- Hypothesis Refinement: Improve and refine hypotheses through automated or human-guided feedback
- ABLE Table Creation: Develop Actor, Behavior, Location, Evidence tables to scope the hunt
- Data Discovery: Identify relevant data sources in your SIEM
- Hunt Planning: Combine all components into a comprehensive threat hunting plan
This repository includes live integration tests that make real calls to the configured LLM provider. These tests are marked with @pytest.mark.live and require a properly configured model_config.json file in the current working directory.
- Run all live tests:
uv run pytest -m live tests/integration -q
Note: These tests make real network calls and may incur costs when using hosted LLM providers.
The PEAK Assistant can be run in a Docker container. To do this, you will need to have Docker installed on your system. Alternatives, such as podman or other container systems that use Docker images, work as well but are not officially supported.
The project provides pre-built Docker images, which can be downloaded by running the following command:
docker pull ghcr.io/cisco-talos/peak-assistant:latestIf you prefer to build the image from source, you can do so by running the following command from the root of the repository:
docker build -t peak-assistant .Note: The container image includes Node.js 20 (copied from the official
node:20-slimimage) so thatnpx-based MCP servers work out of the box. No remote scripts are executed during the build.
Once you have the image downloaded, you can run the container by running the following command:
docker run --rm -it \
--mount "type=bind,src=$(PWD)/cert.pem,target=/certs/cert.pem" \
--mount "type=bind,src=$(PWD)/key.pem,target=/certs/key.pem" \
--mount "type=bind,src=$(PWD)/context.txt,target=/home/peakassistant/context.txt" \
--mount "type=bind,src=$(PWD)/.env,target=/home/peakassistant/.env" \
--mount "type=bind,src=$(PWD)/model_config.json,target=/home/peakassistant/model_config.json" \
--mount "type=bind,src=$(PWD)/mcp_servers.json,target=/home/peakassistant/mcp_servers.json" \
-p "127.0.0.1:8501:8501" \
ghcr.io/cisco-talos/peak-assistant:latestNote that you will still need to provide the same configuration files as you would if you were running the app natively:
context.txtmodel_config.jsonmcp_servers.jsoncert.pem&key.pem.env
The sample command mounts the current directory as /certs, and maps the other files into the working directory of the app running in the container. It assumes these files are in the current directory, but you can adjust the paths as needed.
Once the container is running, you can access it just as though it were running natively. Open https://127.0.0.1:8501/ in your browser.
Podman commands are generally very compatible with Docker commands, so you should be able to use them interchangeably. The only difference is that you will need to use podman instead of docker in the commands above.
The Assistant supports multiple providers via the model_config.json file:
- OpenAI
- Azure OpenAI
- Anthropic
- OpenAI-compatible servers (e.g., Ollama, vLLM, LM Studio)
You can configure different models for different agents, or use a single model for all agents. See MODEL_CONFIGURATION.md for detailed configuration examples and provider-specific requirements.
The PEAK Assistant uses the model_config.json file to configure the LLM providers and models. If you're having issues with the file, you can try the following:
- Verify that the file is valid JSON
- Verify that the file is in the correct format
- Verify that the file is in the correct location
You can use the validate-config command to check the status of the model_config.json file:
uv run validate-configIf there are errors in your configuration, the command will tell you exactly where the JSON is invalid and what the issue is.
If the JSON is valid, the command provides detailed information about the agents and which model they each use:
================================================================================
Model Configuration Validation Report
================================================================================
✓ Configuration is valid
⚠ 1 warning(s):
⚠ Provider 'anthropic' is defined but not used by any agent
Providers (4 defined)
--------------------------------------------------------------------------------
├─ cisco-azure (azure)
│ ├─ Endpoint: https://YOUR-ENDPOINT
│ ├─ API Version: 2025-04-01-preview
│ └─ Credentials: ✓
│
└─ anthropic (anthropic)
├─ Credentials: ✓
└─ Model: (configured per agent)
Agent Model Assignments (14 agents)
================================================================================
┌────────────────────────────┬─────────────────┬─────────────────────┬───────────────────┐
│ Agent │ Provider │ Model │ Source │
├────────────────────────────┼─────────────────┼─────────────────────┼───────────────────┤
│ external_search_agent │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ summarizer_agent │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ summary_critic │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ research_team_lead │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ local_data_search_agent │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ local_data_summarizer_agent│ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ hypothesis-refiner │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ hypothesis-refiner-critic │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ Data_Discovery_Agent │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ Discovery_Critic_Agent │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ hunt_planner │ cisco-azure │ o4-mini (o4-mini) │ group:reasoning-ag│
│ hunt_plan_critic │ cisco-azure │ o4-mini (o4-mini) │ group:reasoning-ag│
│ able_table │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
│ hypothesizer_agent │ cisco-azure │ gpt-4.1 (gpt-4.1) │ defaults │
└────────────────────────────┴─────────────────┴─────────────────────┴───────────────────┘
Provider Usage Summary
================================================================================
Provider: cisco-azure (type: azure)
Total agents: 14
• gpt-4.1: 12 agent(s)
external_search_agent, summarizer_agent, summary_critic, research_team_lead, local_data_search_agent, ... (+7 more)
• o4-mini: 2 agent(s)
hunt_planner, hunt_plan_critic
================================================================================
⚠ Validation complete: 1 warning(s) found
================================================================================
If you're having issues with MCP servers or want to verify your configuration, use the mcp-status command to check the status of all configured servers:
uv run mcp-statusThis command will:
- Display all configured MCP servers organized by server group
- Show authentication status for each server
- Identify missing OAuth2 environment variables
- Provide exact
exportcommands to set required credentials
Example output:
✓ tavily-search
Transport: stdio
Auth: none
Status: Ready
✗ atlassian-remote-mcp
Transport: sse
Auth: oauth2_authorization_code (requires user authentication)
Status: Missing credentials
Missing environment variable(s):
✗ PEAK_MCP_ATLASSIAN_REMOTE_MCP_TOKEN
✗ PEAK_MCP_ATLASSIAN_REMOTE_MCP_USER_ID
To enable, set:
export PEAK_MCP_ATLASSIAN_REMOTE_MCP_TOKEN="your_token_here"
export PEAK_MCP_ATLASSIAN_REMOTE_MCP_USER_ID="your_user_id"
Verbose mode shows additional details like commands, URLs, and descriptions:
uv run mcp-status --verbose
# or
uv run mcp-status -vNote: The mcp-status command only checks configuration and environment variables—it does not attempt to connect to servers. This makes it fast and safe to run at any time.
OAuth2-authenticated MCP servers require browser-based authentication and are primarily designed for use with the Streamlit web interface. However, you can use them in CLI mode (e.g., with research-assistant) by providing OAuth tokens via environment variables:
# For servers without user authentication
export PEAK_MCP_SERVER_NAME_TOKEN="your_access_token"
# For servers requiring user authentication (e.g., Atlassian)
export PEAK_MCP_ATLASSIAN_REMOTE_MCP_TOKEN="your_access_token"
export PEAK_MCP_ATLASSIAN_REMOTE_MCP_USER_ID="your_user_id"To obtain tokens:
- Authenticate via the Streamlit web interface (
uv run peak-assistant) - Use the server's developer portal to generate a personal access token
- Set the environment variables before running CLI commands
Note: OAuth2 servers without environment variables will be automatically skipped in CLI mode with a clear warning message.
Note: This is an experimental feature, and may not work in all situations.
The most likely cause is that you are using self-signed TLS certificates and, while Chrome may allow you to access the app's pages, it will not allow you to download any files. If you can, use a recognized certificate authority to issue your TLS certificates. If this isn't feasible (e.g., if you are running on a local development machine), you will need to add your CA to the system's root certificate store as a trusted CA. The easiest way to do this is to use the mkcert tool to create the local CA, install it on your system, and then use it to create the TLS certificates for the app.
See the LICENSE for details.
