Skip to content

Configuration

ResearchLoop is configured via a researchloop.toml file. The config file is searched in this order:

  1. Explicit path via --config / -c CLI flag
  2. researchloop.toml in the current working directory
  3. ~/.config/researchloop/researchloop.toml

Complete reference

# ── Top-level settings ──────────────────────────────────────────

db_path = "researchloop.db"              # SQLite database location
artifact_dir = "artifacts"               # Local directory for uploaded artifacts
shared_secret = "your-secret"            # Auth token for runner-to-orchestrator communication
orchestrator_url = "https://example.com" # Public URL where runners send webhooks
claude_command = ""                      # Override the claude command globally

# Global context included in all sprints (all studies, all clusters)
context = "Always use Python 3.10+ features."
context_paths = ["./global-context.md"]  # Files whose contents are included as context


# ── Cluster configuration ───────────────────────────────────────

[[cluster]]
name = "hpc"                             # Unique cluster identifier
host = "login.cluster.example.com"       # SSH hostname
port = 22                                # SSH port
user = "researcher"                      # SSH username
key_path = "~/.ssh/id_ed25519"           # Path to SSH private key
scheduler_type = "slurm"                 # "slurm", "sge", or "local"
working_dir = "/scratch/user/researchloop"  # Base directory on the cluster
max_concurrent_jobs = 4                  # Max simultaneous jobs on this cluster
# Override the claude invocation for this cluster. Leave unset to use the
# default, which is `claude --dangerously-skip-permissions` plus a set of
# `--disallowedTools` (Task, Monitor, PushNotification, Cron*, AskUserQuestion,
# EnterPlanMode, ExitPlanMode, RemoteTrigger) — interactive/agentic tools that
# don't work under `claude -p`. Set this only if you need a custom invocation
# (e.g. `singularity exec img.sif claude ...`); overrides manage their own flags.
# claude_command = "claude --dangerously-skip-permissions"

# Cluster-specific context (appended after global context)
context = "GPUs are NVIDIA L40. Check CUDA_VISIBLE_DEVICES before running."
context_paths = ["./cluster-notes.md"]

# Environment variables injected into job scripts
[cluster.environment]
# ANTHROPIC_API_KEY = "sk-ant-..."       # Only needed if not using claude login

# SLURM/SGE job options (passed as #SBATCH or #$ directives)
[cluster.job_options]
gres = "gpu:l40:1"
cpus-per-task = "8"
mem = "64G"

# Resource presets: named bundles that appear as a "Preset" dropdown in the
# sprint/loop/tweak forms and prefill the resource fields with one click.
# Add as many [[cluster.preset]] tables as you like per cluster.
[[cluster.preset]]
name = "H100 1 GPU"
gpu = "gpu:h100:1"                       # alias: gres
mem = "128G"
cpus = "16"                              # alias: cpus-per-task
time = "8:00:00"                         # alias: time_limit
extra_options = "--partition=gpu"        # alias: extra

[[cluster.preset]]
name = "CPU only"                        # omitted fields are blanked when applied,
cpus = "8"                               # so this clears the cluster's default GPU
mem = "32G"


# ── Study configuration ─────────────────────────────────────────

[[study]]
name = "my-study"                        # Unique study identifier
cluster = "hpc"                          # Must match a [[cluster]] name
description = "Research into X"          # Human-readable description

# Study context (appended after global + cluster context)
claude_md_path = "./studies/my-study/CLAUDE.md"  # File with study-specific context
context = """
Focus on improving F1 score. Use batch size 1024.
"""

# Sprint settings
sprints_dir = "/scratch/user/my-study"   # Where sprint dirs are created
                                         # Default: <working_dir>/<study_name>
max_sprint_duration_hours = 8            # SLURM --time limit
red_team_max_rounds = 3                  # Number of red-team/fix cycles
allow_loop = true                        # Whether auto-loops are allowed

# Override claude command for this study
claude_command = ""

# Per-study SLURM/SGE overrides (merged with cluster.job_options)
[study.job_options]
gres = "gpu:a100:2"


# ── Notifications ────────────────────────────────────────────────

[ntfy]
url = "https://ntfy.sh"                 # ntfy server URL (default: ntfy.sh)
topic = "researchloop"                   # ntfy topic for push notifications

[slack]
bot_token = ""                           # Slack Bot User OAuth Token (xoxb-...)
signing_secret = ""                      # Slack Signing Secret
channel_id = "C0123456789"               # Channel or user ID for notifications
allowed_user_ids = ["U0123456789"]       # Users allowed to interact with bot
restrict_to_channel = false              # If true, only respond in channel_id
                                         # (DMs are always allowed)


# ── Dashboard ────────────────────────────────────────────────────

[dashboard]
enabled = true                           # Enable the web dashboard
host = "0.0.0.0"                         # Bind address
port = 8080                              # Bind port
password_hash = ""                       # bcrypt hash (prefer env var or setup page)

Context hierarchy

Context is assembled from multiple sources and concatenated in this order:

  1. Global context -- context and files from context_paths at the top level
  2. Cluster context -- context and files from context_paths on the cluster
  3. Study context -- context inline text and the file at claude_md_path

The combined context is: - Included in all sprint research prompts - Written as CLAUDE.md to the sprint directory on the cluster (so Claude CLI picks it up automatically) - Used by the auto-loop idea generator

Environment variable overrides

All secrets and sensitive settings can be set via environment variables with the RESEARCHLOOP_ prefix. Environment variables take precedence over TOML values.

Environment variable Overrides
RESEARCHLOOP_SHARED_SECRET shared_secret
RESEARCHLOOP_ORCHESTRATOR_URL orchestrator_url
RESEARCHLOOP_DB_PATH db_path
RESEARCHLOOP_ARTIFACT_DIR artifact_dir
RESEARCHLOOP_SLACK_BOT_TOKEN slack.bot_token
RESEARCHLOOP_SLACK_SIGNING_SECRET slack.signing_secret
RESEARCHLOOP_SLACK_CHANNEL_ID slack.channel_id
RESEARCHLOOP_SLACK_ALLOWED_USER_IDS slack.allowed_user_ids (comma-separated)
RESEARCHLOOP_NTFY_TOPIC ntfy.topic
RESEARCHLOOP_NTFY_URL ntfy.url
RESEARCHLOOP_DASHBOARD_PASSWORD Auto-hashed with bcrypt on startup
RESEARCHLOOP_DASHBOARD_PASSWORD_HASH dashboard.password_hash
RESEARCHLOOP_DASHBOARD_PORT dashboard.port
RESEARCHLOOP_DASHBOARD_HOST dashboard.host

Recommended approach

Keep your researchloop.toml with only structural config (clusters, studies, context). Put secrets in environment variables or your deployment platform's secret manager.

Study CLAUDE.md

Each study can have a CLAUDE.md file that provides domain-specific context to Claude. This file is included in every sprint's research prompt and written to the sprint directory on the cluster.

Scaffold a starter file:

researchloop study init my-study

This creates studies/my-study/CLAUDE.md with sections for:

  • Overview -- what you're studying
  • Background -- key papers, prior findings, domain knowledge
  • Codebase -- existing code, data formats, infrastructure
  • Goals -- what you're trying to learn or build
  • Constraints -- rules for the sprints to follow

Job options

Job options are merged from three levels (later values override earlier):

  1. cluster.job_options -- defaults for all studies on this cluster
  2. study.job_options -- overrides for a specific study
  3. Per-sprint overrides -- from the dashboard form or API request

Common SLURM options:

Option Example Description
gres gpu:l40:1 GPU resources
mem 64G Memory limit
cpus-per-task 8 CPU cores
partition gpu SLURM partition
qos high Quality of service

Resource presets

Each cluster can define named resource presets via [[cluster.preset]] tables. Presets show up as a Preset dropdown in the sprint, auto-loop, and tweak forms; picking one prefills the GPU, memory, CPU, time-limit, and additional-options fields so you don't have to type common hardware setups by hand (e.g. "H100 1 GPU", "L40S 2 GPUs", "CPU only").

[[cluster.preset]]
name = "H100 1 GPU"
gpu = "gpu:h100:1"            # alias: gres
mem = "128G"
cpus = "16"                  # alias: cpus-per-task
time = "8:00:00"             # alias: time_limit
extra_options = "--partition=gpu"   # alias: extra

[[cluster.preset]]
name = "CPU only"
cpus = "8"
mem = "32G"

Presets are not a merge layer — selecting one simply fills the form fields, exactly as if you had typed the values. Any field you omit is left blank when the preset is applied, so a "CPU only" preset (no gpu) clears the cluster's default GPU. You can still edit the fields afterward; switching the dropdown back to "Custom…" leaves whatever you've typed untouched.