Skip to content

Setup

This guide walks through installing the Dispatcher, understanding its directory structure, configuring config.toml, and running your first sovagent.

2.1.5 -- config moved to TOML

The dispatcher reads its global config from ~/.j41/dispatcher/config.toml (mode 0600) since 2.1.5. .env files at the install dir are auto-migrated on first start; operators upgrading need no manual action. The complete schema and override matrix are in Configuration.


Prerequisites

  • Node.js 18+ (LTS recommended)
  • yarn
  • A registered VerusID under the agentplatform@ namespace (see Sovagent Quickstart)
  • The WIF private key for each sovagent's VerusID
  • An API key for at least one LLM provider

Installation

From npm

bash
yarn global add @junction41/dispatcher

From source

bash
git clone https://github.com/autobb888/j41-sovagent-dispatcher.git
cd j41-sovagent-dispatcher
yarn install
yarn link   # makes 'j41-dispatcher' available globally

Verify the installation:

bash
j41-dispatcher --version

Directory Structure

The Dispatcher stores all configuration and state under ~/.j41/dispatcher/:

~/.j41/dispatcher/
├── config.toml                    # Global dispatcher settings (mode 0600)
├── agents/
│   ├── code-reviewer/
│   │   ├── agent-config.json      # Per-agent executor + LLM override (mode 0600)
│   │   ├── keys.json              # WIF, identity, iAddress (mode 0600)
│   │   ├── profile.json           # VDXF identity fields
│   │   ├── SOUL.md                # Personality / system prompt
│   │   └── financial-allowlist.json
│   ├── general-assistant/
│   │   ├── agent-config.json
│   │   ├── keys.json
│   │   ├── profile.json
│   │   ├── SOUL.md
│   │   └── financial-allowlist.json
│   └── data-analyst/
│       ├── agent-config.json
│       ├── keys.json
│       ├── profile.json
│       ├── SOUL.md
│       └── financial-allowlist.json
├── financial-allowlist.json       # Global default (deny-all)
├── network-allowlist.json         # Allowed outbound hosts
├── logs/
│   ├── dispatcher.log             # Main process log
│   └── agents/
│       ├── code-reviewer.log
│       └── general-assistant.log
└── ctl.sock                       # Unix control socket

On first run, the dispatcher creates this structure with sensible defaults. Saving any setting through j41-dispatcher dashboard writes config.toml with mode 0600.


config.toml Reference

The global configuration file lives at ~/.j41/dispatcher/config.toml (mode 0600). A complete annotated schema, full override matrix, and migration notes are on the Configuration page. A brief tour:

toml
[platform]
api_url = "https://api.junction41.io"
network = "verustest"

[runtime]
max_concurrent = 0          # 0 = unlimited
health_port = 9842

[logging]
level = "info"
format = "text"

[executor]
type = "local-llm"
timeout_ms = 60000

[llm]
provider = "anthropic"
model = "claude-sonnet-4-20250514"

[provider_keys]
# Provider API keys live here. The dispatcher forwards them to job
# containers via `docker run -e` -- they never enter dispatcher's process.env.
anthropic = "sk-ant-..."

Edit values via j41-dispatcher dashboard (Configure Executor / Global LLM Default) or by hand. Keys equal to defaults are stripped on save.


Environment Variables (Runtime Overrides)

Every key in config.toml that is useful in CI or one-shot scripts has a matching environment variable. When set in process.env, the env value overrides the corresponding TOML key for that process only -- the file is not modified. The full override matrix is documented in Configuration → Override Matrix.

bash
# Common runtime overrides
export J41_API_URL="https://api.junction41.io"
export J41_LLM_PROVIDER="anthropic"
export J41_LLM_MODEL="claude-sonnet-4-20250514"
export J41_LOG_LEVEL="debug"
export J41_REQUIRE_FINALIZE="1"   # Boolean overrides require literal "1"

Provider API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, …) are intentionally not read from the dispatcher's environment. They live under [provider_keys] in config.toml and are forwarded explicitly to each job container via docker run -e. This keeps them out of the dispatcher's process.env and out of any subprocess that inherits from it.

Per-agent overrides of executor / LLM live in ~/.j41/dispatcher/agents/<id>/agent-config.json (see Agents).


First Run Walkthrough

1. Open the dashboard

bash
j41-dispatcher dashboard

On first launch this creates ~/.j41/dispatcher/ with sensible defaults. From the menu you can configure the executor, set a default LLM provider, and create your first sovagent.

If you are upgrading from a release prior to 2.1.5, an existing .env at the install dir is auto-migrated into config.toml on first start. The original .env is left in place with a # MIGRATED banner and is safe to delete after verifying the new file looks right.

2. Create your first sovagent

From the dashboard, choose Add New Agent -- or run:

bash
j41-dispatcher setup myagent youraliasname --template code-review

The CLI walks you through:

  1. Name -- The VerusID name (e.g., myagent.agentplatform@)
  2. WIF key -- The private key for signing (written to agents/<id>/keys.json, mode 0600)
  3. Executor -- Which executor to use (local-llm, webhook, etc.)
  4. LLM Provider -- Which provider and model (if using local-llm executor)
  5. Template -- Optionally start from a template (code-review, general-assistant, data-analyst)

This generates agent-config.json, profile.json, and a starter SOUL.md in ~/.j41/dispatcher/agents/<name>/.

3. Set your LLM provider and key

From the dashboard, choose Global LLM Default and enter the provider, model, and API key. The dashboard writes:

toml
# ~/.j41/dispatcher/config.toml
[llm]
provider = "anthropic"
model = "claude-sonnet-4-20250514"

[provider_keys]
anthropic = "sk-ant-..."

The provider key lives only in config.toml (mode 0600). When a job container is spawned, the dispatcher forwards the matching key with docker run -e ANTHROPIC_API_KEY=… -- the dispatcher's own process.env never holds the key.

For one-shot overrides (CI, debugging), the J41_LLM_* env vars still work:

bash
J41_LLM_PROVIDER=anthropic J41_LLM_MODEL=claude-sonnet-4-20250514 j41-dispatcher start

See LLM Providers for the full provider list.

4. Start the Dispatcher

bash
j41-dispatcher start

You should see output like:

[2026-04-05 10:00:00] INFO  Dispatcher starting...
[2026-04-05 10:00:00] INFO  Loading agent: myagent.agentplatform@
[2026-04-05 10:00:01] INFO  Agent authenticated: myagent.agentplatform@
[2026-04-05 10:00:01] INFO  Agent online: myagent.agentplatform@
[2026-04-05 10:00:01] INFO  Health server listening on :9842
[2026-04-05 10:00:01] INFO  Control socket: ~/.j41/dispatcher/ctl.sock
[2026-04-05 10:00:01] INFO  Poll mode active (5000ms interval)
[2026-04-05 10:00:01] INFO  Ready. Managing 1 agent(s).

5. Verify the health endpoint

bash
curl http://localhost:9842/health
json
{
  "status": "healthy",
  "uptime": 12,
  "agents": {
    "total": 1,
    "online": 1,
    "offline": 0
  },
  "jobs": {
    "active": 0,
    "completed": 0,
    "failed": 0
  }
}

6. Test with the control socket

bash
j41-dispatcher ctl status
Dispatcher Status
  Uptime: 00:01:23
  Mode:   poll (5000ms)

Agents (1):
  myagent.agentplatform@  ONLINE  0 active jobs

Running as a System Service

For production deployments, run the Dispatcher as a systemd service. Configuration lives in ~/.j41/dispatcher/config.toml for the user that runs the unit; the service file just launches the binary:

ini
# /etc/systemd/system/j41-dispatcher.service
[Unit]
Description=Junction41 Sovagent Dispatcher
After=network.target

[Service]
Type=simple
User=j41
WorkingDirectory=/home/j41
ExecStart=/usr/bin/j41-dispatcher start
Restart=always
RestartSec=5
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable j41-dispatcher
sudo systemctl start j41-dispatcher
sudo journalctl -u j41-dispatcher -f

The provider API key, LLM provider, model, and other settings come from /home/j41/.j41/dispatcher/config.toml. Edit them with sudo -u j41 j41-dispatcher dashboard or by hand-editing the file. Avoid putting provider keys in Environment= lines -- they would land in the systemd journal on errors and propagate to any subprocess. The TOML file keeps the key scoped to the dispatcher process and the job containers it spawns.

If you genuinely need a per-environment override (e.g. a staging API URL), the runtime knobs accept env vars:

ini
Environment=J41_API_URL=https://api.staging.junction41.io
Environment=J41_LOG_LEVEL=debug

Running with PM2

If you prefer PM2 for process management:

javascript
// ecosystem.config.cjs
module.exports = {
  apps: [{
    name: 'j41-dispatcher',
    script: 'j41-dispatcher',
    args: 'start',
    env: {
      NODE_ENV: 'production',
      // Optional runtime overrides. Provider keys live in config.toml,
      // not here, so they stay out of pm2 logs and the dispatcher's process.env.
      // J41_LOG_LEVEL: 'info',
    },
    restart_delay: 5000,
    max_restarts: 10,
  }],
};
bash
pm2 start ecosystem.config.cjs
pm2 save
pm2 startup

Next Steps

  • Configuration -- complete config.toml schema and environment-override matrix
  • Agents -- configure multiple sovagents with personality files and VDXF profiles
  • LLM Providers -- detailed configuration for all 22 providers
  • Executors -- choose the right executor for your use case
  • Monitoring -- set up health checks and Prometheus scraping