Skip to content

Custom Content Deployment

This page describes how to deploy custom Modules, Playbooks, SIEM YAML, and Python dependencies to a Docker Compose installation. Complete Deployment first.

1. Custom directory

The Compose deployment mounts the host custom/ directory into backend containers:

PathPurpose
custom/modules/*.pyCustom Modules.
custom/playbooks/*.pyCustom Playbooks.
custom/data/modules/<module_slug>/raw_alert_*.jsonModule development samples.
custom/data/siem/*.yamlCustom SIEM YAML.
custom/data/playbooks/<playbook_slug>/*.mdCustom Playbook prompts.
custom/requirements.txtExtra Python packages required by Modules, Playbooks, or shared helpers.

init.sh creates the empty directory structure and custom/requirements.txt template. Source examples under backend/custom/ are development references and are not included in release packages.

2. Install Python dependencies

Add dependencies to:

text
custom/requirements.txt

Install them:

bash
docker compose run --rm asp-custom-deps

Dependencies are installed at /opt/asp/custom-packages, persisted in the custom-python-packages Docker named volume, and mounted into all backend services.

To choose a Python package index:

bash
docker compose run --rm asp-custom-deps --index-url https://pypi.org/simple

Arguments after the service name are passed to uv pip install, for example:

  • --extra-index-url https://packages.example.com/simple
  • --upgrade

To pass proxy settings into the container:

bash
docker compose run --rm \
  -e HTTP_PROXY=http://proxy.example:8080 \
  -e HTTPS_PROXY=http://proxy.example:8080 \
  asp-custom-deps

3. Manage custom variables

Admins can manage variables used by custom Modules and Playbooks under Custom > Variables. Keys may contain only uppercase letters, numbers, and underscores, and cannot be renamed after creation.

Every variable requires a Type:

TypePython return typeValue editor
StringstrText input
IntegerintInteger input
FloatfloatNumber input
BooleanboolSwitch
ListlistJSON code editor
DictionarydictJSON code editor

List and Dictionary support nested JSON values. The editor provides line numbers, syntax highlighting, bracket matching, code folding, and Format; JSON syntax and the top-level type are validated when you save. Dictionary key order is not guaranteed. Use a List when order matters.

String cannot be empty. Integer must be a JavaScript safe integer, and Float must be finite. Boolean false, numeric 0, an empty List, and an empty Dictionary are all valid values. A Value may contain up to 65,536 UTF-8 bytes, and List and Dictionary may be nested up to 20 levels.

Only String can be marked as Secret. Secret values are hidden from normal Admin API responses and edit forms; an Admin must use Reveal to view one. To change a Secret to another Type, disable Secret and enter a new Value. Changing the Type of any variable clears the previous Value and requires confirmation.

Read variables through the base class in custom code:

python
class Playbook(BasePlaybook):
    def run(self):
        base_url = self.get_variable("EDR_BASE_URL")
        token = self.get_variable("EDR_API_TOKEN")
        verify_tls = self.get_variable("EDR_VERIFY_TLS")
        headers = self.get_variable("EDR_HEADERS")

        if base_url is None or token is None:
            raise ValueError("EDR custom variables are not configured.")

        if verify_tls is None:
            verify_tls = True
        if headers is None:
            headers = {}

BaseModule uses the same lookup method:

python
class Module(BaseModule):
    NAME = "EDR alert processor"
    STREAM_NAME = "EDR-Alerts"

    def run(self, message):
        base_url = self.get_variable("EDR_BASE_URL")
        token = self.get_variable("EDR_API_TOKEN")
        if base_url is None or token is None:
            raise ValueError("EDR custom variables are not configured.")

        # Process the alert using message, base_url, and token.

BasePlaybook.get_variable() and BaseModule.get_variable() query the current database value on every call and return the native Python type selected by Type. Custom code does not need to parse JSON. They return None when a variable is missing, disabled, or deleted.

Use is None to detect a missing variable

false, 0, [], and {} are valid values. Use value is None to detect a missing variable instead of if not value.

Secrets are not encrypted at rest

The Secret flag only hides a value from normal Admin API responses and lists. The value remains plaintext in the database and can be read by database administrators or anyone with a database backup. Install only trusted custom code, and never write secrets to logs, task summaries, Enrichments, or exceptions.

4. Apply changes

For Module, Playbook, or SIEM YAML changes, use Refresh / Validate in the corresponding Custom Console tab.

After dependency or shared helper changes:

bash
docker compose run --rm asp-custom-deps
docker compose restart asp-web asp-worker-module asp-worker-playbook
./scripts/doctor.sh

5. Compose overrides

init.sh creates compose.override.yaml when it does not exist. Docker Compose merges it with the official compose.yaml.

  • Keep supported settings in .env.
  • Edit compose.override.yaml for service-level volumes, environment variables, commands, or other overrides.
  • Do not edit the official compose.yaml.

For example:

yaml
services:
  asp-web:
    environment:
      EXAMPLE_SETTING: value

Apply the change:

bash
docker compose up -d
./scripts/doctor.sh

Release packages do not contain .env, compose.override.yaml, or custom/, so overlay upgrades preserve them. See Upgrade.

Next Steps