Skip to content

Local Adapter Development Setup

The canonical, published version of this guide lives on pq-web at howto/enable-dev.html. This markdown is the in-repo reference for developers who have the monorepo.

This guide explains how to configure your PQ installation for local adapter development when running your adapter outside Docker Compose (e.g., in Visual Studio or Rider).

Prerequisites

  • PQ installed via Docker Compose (.install/compose/)
  • Your adapter project in Visual Studio / Rider
  • Adapter connects to PQ via NATS and Internal API

The Problem

By default, PQ's Docker Compose configuration does not expose internal services externally:

  • NATS (port 4222) — used for messaging between adapters and server
  • Internal API (/Internal/*) — used by adapters to fetch authorizations, device config, etc.

This is intentional for production security. Adapters running inside Docker Compose access these via internal network. But during development, your adapter runs on the host machine and needs external access.

Solution: Development Profile

PQ includes a dev profile that exposes the necessary ports and allows external API access.

Step 1: Start PQ with Dev Profile

cd .install/compose

# Start with dev profile (exposes NATS port)
INTERNAL_API_ALLOW_EXTERNAL=true docker compose --profile dev up -d

This does two things:

  1. Starts nats-dev service that exposes NATS on localhost:4222
  2. Sets InternalApi__AllowExternal=true to allow /Internal/* API calls from your host machine

Step 2: Configure Your Adapter

Recommended — global dev config (one-time): run the helper script from the repo root:

.\setup-adapter-dev-config.ps1

It writes ~/.pq/adapter-dev.json with the NATS connection (localhost:4222, pq-master / pq-master) and sets OTEL_EXPORTER_OTLP_ENDPOINT so adapter logs flow to Grafana. The framework reads this file as a fallback (AdapterFramework.ApplyMqFallbackConfiguration) for any locally-run adapter that has no complete Mq config from a stronger source — so you configure NATS once instead of per adapter.

Alternative — per-adapter: set it explicitly in your adapter's appsettings.Development.json (this wins over the global fallback):

{
  "Mq": {
    "Host": "localhost:4222",
    "User": "pq-master",
    "Password": "pq-master"
  }
}

The server API URL is automatically provided during adapter registration, so you don't need to configure it manually.

Step 3: Run Your Adapter

Start your adapter from Visual Studio / Rider. It will:

  1. Connect to NATS at localhost:4222
  2. Register with the server
  3. Receive API base URL in registration response
  4. Call /Internal/Adapter/* endpoints for device config, authorizations, etc.

Branch B — Installed Box (No Repo)

If you only have an installer-deployed stack — no monorepo checkout — you don't have the dev compose profile or setup-adapter-dev-config.ps1. Use this path instead.

Step 1: Expose NATS

The deployed compose has no dev profile, so publish NATS with a standalone socat sidecar on the Docker host:

docker run -d --name nats-dev --network pq_default -p 4222:4222 alpine/socat:latest TCP-LISTEN:4222,fork,reuseaddr TCP:nats:4222

This does not survive docker compose down or a redeploy — re-run it afterward. It publishes NATS on 0.0.0.0 (LAN-reachable) using the shared pq-master / pq-master credentials.

Step 2: Internal API — normally nothing to do

A host-run adapter reaches /Internal/* through the nginx front door (port 5443) using the base URL and X-Api-Key it receives automatically at registration. The adapter's HTTP client bypasses TLS validation, so there's no CA setup either. On a standard installed box, /Internal/* is not IP-blocked from the host.

Only as a fallback — if /Internal/* returns HTTP 403 — does an admin need to set InternalApi__AllowExternal=true on the server service and restart it.

Step 3: Configure Your Adapter

There's no repo checkout to run setup-adapter-dev-config.ps1 from, so hand-write ~/.pq/adapter-dev.json:

{
  "Mq": { "Host": "localhost:4222", "User": "pq-master", "Password": "pq-master" }
}

Then set OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 as a persistent user environment variable — the Alloy collector is always running, so no compose profile is needed to turn it on. Adapter logs appear in Grafana at https://localhost:5443/grafana/d/pq-home.

Security Note

The nats-dev sidecar publishes NATS on 0.0.0.0 with the shared pq-master / pq-master credentials — only run this on a development box you control. Never leave InternalApi__AllowExternal=true set outside of active troubleshooting.

Stopping Development Mode

# Stop all services
docker compose --profile dev down

# Or restart in production mode (no external access)
docker compose up -d

Security Notes

  • Never use --profile dev in production — it exposes internal services
  • The INTERNAL_API_ALLOW_EXTERNAL=true flag bypasses IP-based protection on /Internal/* endpoints
  • In production, adapters run inside Docker Compose and access services via internal network

Troubleshooting

NATS Connection Refused

Error: Connection refused to localhost:4222

Make sure you started with --profile dev:

docker compose --profile dev up -d

Verify NATS is exposed:

docker compose ps
# Should show nats-dev service with 0.0.0.0:4222->4222/tcp

403 Forbidden on /Internal/* Endpoints

HTTP 403 Forbidden when calling /Internal/Adapter/...

Make sure you set the environment variable:

INTERNAL_API_ALLOW_EXTERNAL=true docker compose --profile dev up -d

Or restart with the variable:

docker compose --profile dev down
INTERNAL_API_ALLOW_EXTERNAL=true docker compose --profile dev up -d

Adapter Not Receiving Commands

  1. Check NATS connection in adapter logs
  2. Verify adapter registered successfully (look for AdapterConfigurationResponse in logs)
  3. Check that device is started in PQ UI

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│  Docker Compose                                         │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌───────────┐   │
│  │   DB    │  │  NATS   │  │ Server  │  │ nats-dev  │   │
│  │  :5432  │  │ :4222   │  │  :5443  │  │ (profile) │   │
│  └─────────┘  └────┬────┘  └────┬────┘  └─────┬─────┘   │
│                    │            │              │         │
│         internal network        │         ports:4222    │
└────────────────────┼────────────┼──────────────┼────────┘
                     │            │              │
                     │       localhost:5443  localhost:4222
                     │            │              │
              ┌──────┴────────────┴──────────────┴──────┐
              │           Host Machine                  │
              │  ┌─────────────────────────────────┐    │
              │  │  Your Adapter (Visual Studio)  │    │
              │  │  - Connects to localhost:4222  │    │
              │  │  - Calls localhost:5443/Internal│    │
              │  └─────────────────────────────────┘    │
              └─────────────────────────────────────────┘