How to Configure Custom Output Directories for Audit Logs and Reports in Shannon
Use the OUTPUT= flag when running the Shannon CLI to specify a custom host directory, which the system then bind-mounts to /app/output inside the Docker worker and propagates through the Temporal workflow to all audit-logging agents.
Shannon, the automated penetration-testing framework by KeygraphHQ/shannon, generates extensive artefacts during every engagement—including raw tool output, agent logs, and final PDF reports. By default, the CLI writes these files to ./audit-logs/ at the repository root. However, production deployments often require redirecting this data to mounted network storage, CI/CD artefacts directories, or organised archival paths.
Using the OUTPUT Flag in the Shannon CLI
The fastest way to configure a custom output directory is to append the OUTPUT= argument when invoking the Bash wrapper. The script validates the path, creates it if necessary, and ensures the Docker worker can write to it.
# Run a pentest and store all artefacts under ./my-reports/
./shannon start URL=https://example.com REPO=my-app OUTPUT=./my-reports
The shannon script performs three critical actions with this value:
- Creates the host directory with
mkdir -p "$OUTPUT"and sets permissions (chmod 777) so the container process can write to it. - Exports
OUTPUT_DIRto the environment so Docker Compose can reference it. - Mounts the volume via
-v "$OUTPUT_DIR":/app/outputwhen starting the worker container.
After the workflow completes, the CLI prints the host-side path so you can locate the generated reports immediately:
Reports: ./my-reports/host-example.com_shannon-1708153456789
How Custom Output Paths Flow Through the System
Understanding the data flow helps when debugging permission issues or when invoking components directly without the Bash wrapper. The path travels from the host CLI through Docker, into the Temporal workflow, and finally to the audit-logging utilities.
CLI Argument Parsing and Docker Volume Mounting
In the shannon entrypoint script (lines 73‑78), the flag is parsed and stored in the OUTPUT variable. Lines 70‑74 and 92‑96 handle the Docker volume export:
# From shannon (lines 73-78)
OUTPUT=*)
OUTPUT="${1#*=}"
shift
;;
The script then ensures the directory exists and exports it for Docker Compose:
mkdir -p "$OUTPUT"
chmod 777 "$OUTPUT"
export OUTPUT_DIR="$OUTPUT"
Temporal Client and Workflow Input
The Temporal client at src/temporal/client.ts (lines 90‑96, 122‑126, and 148‑152) receives the host path via --display-output and the container-internal path via --output. It constructs a PipelineInput object defined in src/temporal/shared.ts (line 9):
// From src/temporal/client.ts
if (outputPath) {
// /app/output is the path inside the container
ARGS += ` --output /app/output --display-output ${outputPath}`;
}
The PipelineInput interface includes the optional outputPath field:
// From src/temporal/shared.ts (line 9)
interface PipelineInput {
outputPath?: string;
// ... other fields
}
Audit Log Resolution in the Worker
Inside the workflow, agents invoke getSessionDir from src/audit/utils.ts (line 23) to determine where to write logs. This function prefers the custom outputPath if provided, otherwise falling back to the default AUDIT_LOGS_DIR (./audit-logs):
// From src/audit/utils.ts
export function getSessionDir(sessionMetadata: { outputPath?: string }) {
// Prefer the custom path supplied by the workflow
const baseDir = sessionMetadata.outputPath || AUDIT_LOGS_DIR;
return path.join(baseDir, sessionMetadata.workflowId);
}
Because the Docker container bind-mounted the host directory to /app/output, and the workflow received /app/output as its outputPath, all file writes are immediately persisted to the host filesystem.
Direct Temporal Client Invocation
For advanced use cases—such as integrating Shannon into existing CI pipelines—you may bypass the Bash wrapper and call the Temporal client directly. Ensure the Docker volume is already mounted before running this command:
# Equivalent call without the Bash wrapper
node dist/temporal/client.js \
https://example.com /repos/my-app \
--output /app/output \
--display-output ./my-reports \
--pipeline-testing
The --output flag must match the container-internal mount point (/app/output), while --display-output should reflect the host-side path for user feedback in logs.
Key Implementation Files
| File | Purpose | Relevant lines |
|---|---|---|
shannon (Bash CLI) |
Parses OUTPUT= flag, creates host directory, exports OUTPUT_DIR, starts containers, forwards flags to client. |
73‑78, 90‑96, 122‑126, 148‑152 |
src/temporal/client.ts |
Temporal client that receives --output and --display-output, builds PipelineInput for the workflow. |
90‑96, 122‑126, 148‑152 |
src/temporal/shared.ts |
Defines PipelineInput.outputPath which propagates through the workflow. |
9‑10 |
src/audit/utils.ts |
Resolves the final output directory, using the custom outputPath if present. |
23‑27 |
src/cli/ui.ts |
Prints usage help that mentions the --output flag. |
23‑25 |
Summary
- Default behavior: Shannon writes all artefacts to
./audit-logs/at the repository root. - Custom configuration: Append
OUTPUT=<host-path>to the./shannon startcommand to redirect output anywhere on the host filesystem. - Mechanism: The Bash wrapper bind-mounts your directory to
/app/outputinside the Docker worker, then passes that path through the Temporal workflow tosrc/audit/utils.ts, which uses it as the base directory for all session logs and reports. - Direct usage: When calling
src/temporal/client.tsdirectly, use--output /app/output(container path) and--display-output <host-path>(user feedback).
Frequently Asked Questions
What is the default output directory if I don't specify OUTPUT=?
If you omit the OUTPUT= flag, Shannon defaults to ./audit-logs/ relative to the repository root. This path is defined as AUDIT_LOGS_DIR in src/audit/utils.ts and is used whenever sessionMetadata.outputPath is undefined.
Can I use absolute paths with the OUTPUT flag?
Yes. The shannon script accepts both relative and absolute paths. It runs mkdir -p "$OUTPUT" to ensure the directory exists, then exports it as OUTPUT_DIR for the Docker volume mount. Absolute paths work identically to relative ones, making them ideal for CI/CD environments where the working directory may vary.
How do I ensure the Docker container can write to my custom directory?
The shannon wrapper automatically sets chmod 777 "$OUTPUT" on the host directory before mounting it. This ensures the container process (which may run as a different UID) has write access. If you invoke the Temporal client directly without the wrapper, you must manually ensure the directory permissions allow the container user to write to the bind-mounted path.
Is the output path configuration persistent across Shannon runs?
No. The OUTPUT= flag is ephemeral to the specific invocation. Each time you run ./shannon start, you must specify the desired output directory. For persistent configuration, you can create a shell alias or wrapper script that always appends your preferred OUTPUT= value to the command, or you can modify the default constant AUDIT_LOGS_DIR in src/audit/utils.ts before building the project.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →