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:

  1. Creates the host directory with mkdir -p "$OUTPUT" and sets permissions (chmod 777) so the container process can write to it.
  2. Exports OUTPUT_DIR to the environment so Docker Compose can reference it.
  3. Mounts the volume via -v "$OUTPUT_DIR":/app/output when 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 start command to redirect output anywhere on the host filesystem.
  • Mechanism: The Bash wrapper bind-mounts your directory to /app/output inside the Docker worker, then passes that path through the Temporal workflow to src/audit/utils.ts, which uses it as the base directory for all session logs and reports.
  • Direct usage: When calling src/temporal/client.ts directly, 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →