Backend
Location: app/server/modules/monitor/
This document explains how the backend processes Cyphon requests.
Backend Architecture
API Request
│
▼
Export File (discovery.export.ts / intel.export.ts)
│
▼
Service Class (Discovery.ts / Search.ts)
│
▼
AI Processing / Data Sources
│
▼
ResponsePart 1: Command Classification (Discovery.ts)
File: app/server/modules/monitor/src/intel/Discovery.ts
When a user types a question, we need to figure out which commands to run.
The executeCyphonQuery Method
async executeCyphonQuery(question: string) {
// 1. Validate input
if (!question || question.trim().length === 0) {
throw new Error("Empty Query");
}
// 2. Build the AI prompt
const prompt = CYPHON_PROMPTS.COMMANDS_CLASSIFICATION.prompt
.replace("{COMMANDS_JSON}", JSON.stringify(cyphonCommands, null, 2))
.replace("{USER_QUERY}", question.trim());
// 3. Send to AI and get response
return genericAI.executePromptRaw(prompt);
}What Happens Inside
- Input: User question like "scan google.com for whois"
- Prompt Building: Combines the question with available commands
- AI Processing: AI analyzes and picks relevant commands
- Output: JSON with matched commands
Part 2: AI Prompts (cyphon.ts)
File: app/server/modules/monitor/src/ai/prompts/cyphon.ts
This file contains the prompts that tell the AI what to do.
COMMANDS_CLASSIFICATION Prompt
export const CYPHON_PROMPTS = {
COMMANDS_CLASSIFICATION: {
prompt: `
You are an intelligent command-discovery assistant.
Your task is to analyze a user query and identify ALL commands
from the available command list that are relevant to the user's intent.
AVAILABLE COMMANDS:
{COMMANDS_JSON}
USER QUERY:
{USER_QUERY}
INSTRUCTIONS:
1. Understand the user's intent.
2. Find all commands that match the intent.
3. Extract required parameters from the user query.
4. Generate a formatted command call.
RESPONSE FORMAT (Return ONLY valid JSON):
{
"relevantCommands": [
{
"id": "command_id",
"formattedOutput": "commandName(extracted_value)"
}
]
}
`,
},
};RESULTS_SUMMARY Prompt
RESULTS_SUMMARY: {
prompt: `
You are a professional security consultant explaining
technical findings to a non-technical user.
The user searched for: "{USER_QUERY}"
Here are the security scan results:
{RESULTS_JSON}
YOUR TASK:
Write a brief, professional overview (2-4 sentences) that:
1. Summarizes what was found in plain English
2. Highlights any important details
3. Mentions if anything looks concerning
4. Uses simple language
RESPONSE FORMAT (Return ONLY valid JSON):
{
"summary": "Your professional summary here...",
"highlights": ["key point 1", "key point 2"]
}
`;
}Part 3: Available Commands (cyphonCommands.ts)
File: app/server/modules/monitor/src/views/cyphonCommands.ts
This defines all commands that Cyphon can execute.
Command Structure
export interface CyphonCommand {
name: string; // "whois"
description: string; // "Retrieves WHOIS information..."
syntax: string; // "whois(domain)"
parameters: Array<{
name: string; // "domain"
type: string; // "string"
required: boolean; // true
description: string; // "Domain name to query"
example?: string; // "example.com"
}>;
keywords: string[]; // ["whois", "domain", "registration"]
example: string; // 'whois("example.com")'
category: string; // "Domain Intelligence"
icon: string; // "mdi:domain"
}Example Command
{
name: "whois",
description: "Retrieves WHOIS information for the provided domain.",
syntax: "whois(domain)",
parameters: [
{
name: "domain",
type: "string",
required: true,
description: "Domain name to query WHOIS information for",
example: "example.com"
}
],
keywords: ["whois", "domain", "registration", "owner", "registrar"],
example: 'whois("example.com")',
category: "Domain Intelligence",
icon: "mdi:domain"
}Part 4: Search Execution (Search.ts)
File: app/server/modules/ai/src/Search.ts
Once we have commands, they need to be executed. The Search class orchestrates this.
How Search Works
class Search {
private sources: BaseSearch<any>[]; // Array of search sources
private mqqueue: MQQueue; // Message queue for jobs
// Start a search job
async runSearch(searchRequest) {
// 1. Parse the search query
searchRequest.parsed = this.parser.parse(searchRequest.query);
// 2. Create a unique job ID
const reqId = `search-${uuidv4()}`;
// 3. Add to queue and return job ID
const job = await this.searchQueue.add(reqId, searchRequest);
return job.id;
}
// Get results for a job
async getJobData(jobId) {
const job = await this.searchQueue.getJob(jobId);
const jobState = await job.getState();
return {
id: jobId,
results: job.data.results,
progress: job.progress,
done: jobState === "completed",
};
}
}The Job Queue Pattern
User Request → Create Job → Add to Queue → Process Async → Poll for ResultsThis allows searches to run in the background while the user waits.
Part 5: Search Sources
File: app/server/modules/monitor/intel.export.ts
Multiple sources are combined to provide comprehensive results:
const sources = [
new AssetsSearch(), // Internal asset database
new DetectionSearch(), // Threat detections
new IntelSearch(), // Intelligence feeds
new RyzenSearch(), // Ryzen data source
new SintelSearch(), // Security intel
new LLMSearch(), // AI-powered search
new RemediationSearch(), // Remediation suggestions
new IncidentDetectionIntel(), // Incident data
new DomainSpoofingSearch(), // Domain spoof checks
new ThreatAIOverview(), // AI threat analysis
new DiscoverSearch(), // Discovery scans
new PlatformSearch(), // Platform data
];How Sources Work
Each source implements a common interface:
interface BaseSearch {
getCapabilities(): object; // What this source can search
search(query): Promise<Results>; // Execute the search
}Part 6: Export Files (API Routes)
discovery.export.ts
// Routes API calls to Discovery service
async executeCyphonQuery(message: Message) {
return discovery.executeCyphonQuery(message.body.question);
}
async summarizeCyphonResults(message: Message) {
return discovery.summarizeCyphonResults(
message.body.userQuery,
message.body.results
);
}intel.export.ts
async search(message: Message) {
// Check feature flag
if (!FeatureFlag.isEnabled("intel_cyphon", message.cx))
throw new Error("Feature disabled.");
let jobId = message.body.id;
// If no job ID, start new search
if (!jobId && message.body.keyword) {
jobId = await searchManager.runSearch({
org: await message.owner.getCurrentWorkspace(),
owner: message.owner,
query: message.body.keyword,
});
}
// Return job results
if (jobId) {
return await searchManager.getJobData(jobId);
}
}Data Flow Summary
Step 1: User Query → Commands
"scan google.com"
│
▼ executeCyphonQuery()
│
▼ AI + COMMANDS_CLASSIFICATION prompt
│
▼
{ relevantCommands: [{ formattedOutput: "whois(\"google.com\")" }] }Step 2: Commands → Results
"whois(\"google.com\")"
│
▼ intelAPI.search()
│
▼ Search.runSearch() → Job Queue
│
▼ Multiple Sources Execute
│
▼
{ results: [{ cardType: "Whois", facts: {...} }] }Step 3: Results → Summary
[{ cardType: "Whois", facts: {...} }]
│
▼ summarizeCyphonResults()
│
▼ AI + RESULTS_SUMMARY prompt
│
▼
{ summary: "Google.com is...", highlights: [...] }Key Concepts
Message Queue (MQQueue)
- Jobs are processed asynchronously
- Provides progress tracking
- Handles failures gracefully
- Supports concurrent processing
Feature Flags
if (!FeatureFlag.isEnabled("intel_cyphon", message.cx))
throw new Error("Feature disabled.");Cyphon can be enabled/disabled per organization.
Organization Scoping
All searches are scoped to the user's organization:
jobId = await searchManager.runSearch({
org: await message.owner.getCurrentWorkspace(),
// ...
});Debugging Backend
Logs to Watch
console.log(`Job ID: ${jobId}`);Look for job IDs in server logs to trace requests.
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Empty commands | AI didn't match any | Check prompt or command keywords |
| Slow results | Sources taking time | Check individual source performance |
| Missing results | Source not returning | Debug specific search source |
| AI errors | Prompt issues | Check CYPHON_PROMPTS format |