Skip to content

Python Connector

The Python connector is a PIP package that integrates your Python applications with Patcherly for automated error detection and fixing.

Registry: PyPI — patcherly-connector — unpinned pip install resolves to latest; Python versions independently from other stacks (see Connectors overview — Package registries).

Easiest setup: if you have terminal access, use the Universal Installer — no need to configure anything manually. The steps below are for manual package installation.

Quick Start

Get up and running in minutes:

# 1. Install the package
pip install patcherly-connector

# 2. Pair with your Patcherly target (OAuth Device Authorization Grant)
patcherly login

# 3. Start the connector
python patcherly_agent.py

patcherly login prints a verification_uri and a short user code. Open the URL in any browser, sign in to your Patcherly dashboard, select the website/app (target), and confirm the code. Credentials are saved to ~/.patcherly/credentials.json.

After pairing, the connector uploads environment context for AI analysis. Default is full (unlike WordPress, which defaults to off until you choose in the WP admin).

patcherly context get                 # current tier + source (env|file|default)
patcherly context set full|minimal|off
patcherly context upload              # force collect + upload now
  • Env PATCHERLY_CONTEXT_CONSENT overrides the cache file.
  • Cache file: {PATCHERLY_CACHE_DIR}/context_consent (default .patcherly_cache/context_consent).
  • off skips uploads; minimal sends runtime/OS/framework only.

The connector will automatically: - Read OAuth credentials from ~/.patcherly/credentials.json - Start monitoring your application logs and report new errors to the dashboard - Sign requests with the per-token HMAC secret from the credential file

Note: the Python connector reports errors to Patcherly and applies approved fixes back to your code. Analysis and approval happen in the Patcherly dashboard, not on the connector itself.

Installation

Step 1: Install the Package

pip install patcherly-connector

Or using pipenv:

pipenv install patcherly-connector

Step 2: Pair with a Target (OAuth)

Run the login command once per target server:

patcherly login

Follow the prompt: open the printed URL in any browser, sign in, and confirm the user code. Credentials are saved to ~/.patcherly/credentials.json. To log out and revoke the token, run patcherly logout — Patcherly also flips the target row on the dashboard from healthy/stale to inactive immediately on the same call (no more "still says healthy three days after I logged out" surprises).

If you want to customise paths, create a .env file:

# OPTIONAL: Custom paths
PATCHERLY_BACKUP_ROOT=.patcherly_backups
PATCHERLY_QUEUE_PATH=patcherly_queue.jsonl
PATCHERLY_IDS_PATH=patcherly_ids.json

Step 3: Initialize the Agent

In your application's main file:

from patcherly_agent import PatcherlyAgent

# Initialize the agent — credentials are read from ~/.patcherly/credentials.json
agent = PatcherlyAgent(
    log_file='logs/error.log'
)

# Start monitoring
await agent.start()

Alternative Installation: Direct File Upload

If you prefer to upload the connector files directly to your environment instead of using pip:

Step 1: Download and Upload Connector Files

  1. Download the Python Connector Files
  2. Download python-connector.tar.gz from the latest connector release.
  3. Extract it into your install directory — the universal installer's default is /opt/patcherly-connector/ on Linux/macOS and %USERPROFILE%\patcherly-connector\ on Windows; you can pick any path as long as you reference the same one in the start commands and systemd unit below. Use all files from the archive — the agent needs every module (OAuth client, credential store, patcherly CLI, etc.) to pair and run; missing files break login or apply.

Tip: Most installs go through the universal installer instead (curl https://api.patcherly.com/install | bash) which downloads and extracts the right archive for you. The manual path above is for environments where the installer can't run.

  1. Install Required Dependencies
  2. The connector requires: httpx, asyncio (built-in)
  3. Install dependencies: pip install httpx
  4. Optional (for local approvals UI): pip install flask
  5. Optional (for automatic .env file loading): pip install python-dotenv
    • Note: If python-dotenv is not installed, the connector will manually parse .env files

Step 2: Pair with a Target and Configure

Run patcherly login (from the extracted connector directory) to complete OAuth pairing. Optionally, create a .env file for custom paths:

# OPTIONAL: Custom paths
PATCHERLY_BACKUP_ROOT=.patcherly_backups
PATCHERLY_QUEUE_PATH=patcherly_queue.jsonl
PATCHERLY_IDS_PATH=patcherly_ids.json

Step 3: Start the Agent

Option A: Run as Standalone Script (Recommended)

The connector can be run directly as a Python script:

# From the connector directory
cd /opt/patcherly-connector
python patcherly_agent.py

# Or with command-line options
python patcherly_agent.py --server https://api.patcherly.com --log logs/error.log --interval 10

# Or from your project root
python /opt/patcherly-connector/patcherly_agent.py

Available Command-Line Options: - --log: Log file to monitor (default: agent_logs.txt) - --interval: Polling interval in seconds (default: 10) - --local-approvals: Enable local approvals UI (optional). Binds to 127.0.0.1 only and requires Authorization: Bearer <oauth-access-token> on /approvals, /approve, /reject-patch, and /api/file-content (only /status is unauthenticated). The error_id payload on /approve and /reject-patch must match ^[A-Za-z0-9_-]{1,128}$. /reject-patch requires a resolution body (manual_suggestion, manual_own, or not_needed). - --approvals-port: Port for local approvals UI (default: 8081) - --project-root: Constrain /api/file-content reads to this directory (default: the directory the connector was launched from). This is a defense-in-depth boundary so the connector cannot be turned into an arbitrary-file reader on the host. Requests must also include error_id in the signed JSON body.

Option B: Import and Run in Your Application

import sys
import os
import asyncio

# Add connector directory to Python path
connector_path = os.path.join(os.path.dirname(__file__), 'patcherly-connector')
if connector_path not in sys.path:
    sys.path.insert(0, connector_path)

from patcherly_agent import PatcherlyAgent

async def main():
    # Credentials are read from ~/.patcherly/credentials.json (created by patcherly login)
    agent = PatcherlyAgent(
        log_file='logs/error.log'
    )
    await agent.run(poll_interval=10)  # Run with 10-second polling interval

if __name__ == "__main__":
    asyncio.run(main())

Option C: Run as Background Process

# Run in background
cd /opt/patcherly-connector
nohup python patcherly_agent.py > ../logs/agent.log 2>&1 &

# Or using screen/tmux
screen -S patcherly-connector
cd /opt/patcherly-connector
python patcherly_agent.py
# Press Ctrl+A then D to detach

Option D: Run as Systemd Service (Linux)

Create /etc/systemd/system/patcherly-connector.service:

[Unit]
Description=Patcherly Python Agent
After=network.target

[Service]
Type=simple
User=your_user
WorkingDirectory=/opt/patcherly-connector
EnvironmentFile=/opt/patcherly-connector/.env
ExecStart=/usr/bin/python3 /opt/patcherly-connector/patcherly_agent.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Then enable and start:

sudo systemctl enable patcherly-connector
sudo systemctl start patcherly-connector

Configuration

Credentials (OAuth)

The connector reads authentication credentials from ~/.patcherly/credentials.json, created when you run patcherly login.

Optional Settings

  • SERVER_URL or PATCHERLY_API_BASE: Leave unset for normal use (https://api.patcherly.com). Only set one of these if Patcherly Support gives you a specific API host — use the same value for patcherly login --api-base and the running agent.
  • PATCHERLY_BACKUP_ROOT: Backup directory (default: .patcherly_backups)
  • PATCHERLY_QUEUE_PATH: Queue file path (default: patcherly_queue.jsonl)
  • PATCHERLY_IDS_PATH: Workspace/target IDs cache (default: patcherly_ids.json)

HMAC Signing

HMAC signing uses the per-token secret bundled in ~/.patcherly/credentials.json at OAuth issuance. You do not need to configure a secret manually: - Each OAuth token includes its own unique HMAC signing secret - The connector reads it from the credential file automatically - HMAC verification is always mandatory for fix payloads

Supported Applications & File Types

✅ Supported Applications

The Python connector is designed for Python web applications and services:

  • Django - High-level Python web framework
  • Flask - Lightweight WSGI web application framework
  • FastAPI - Modern, fast web framework for building APIs
  • Pyramid - Flexible Python web framework
  • Any Python application - Custom Python servers, APIs, scripts, services

The connector automatically detects which framework you're using and includes this information in error context.

✅ Supported File Types

The connector can patch and validate syntax for:

  • .py - Python source files

❌ Not Supported

Other file types: - Python bytecode files (.pyc, .pyo) - Compiled extensions (.so, .pyd) - Jupyter notebooks (.ipynb) - Other languages (JavaScript, PHP, etc.)

Note: The connector focuses on Python source code files. It validates syntax using Python's ast.parse module before and after applying patches.

Features

Automatic Error Detection

The connector automatically:

  • Monitors log files for errors
  • Detects new error entries
  • Sends error context to Patcherly
  • Tracks error signatures and occurrences

Did you know? The Python agent runs separately from your app and only detects lines from log files it watches. If errors are missing in Patcherly, confirm your app writes failures to a monitored file and add any custom path under Targets → Log paths. See Custom log paths and the connectors overview.

Fix Application

When a fix is approved in the dashboard:

  • The agent polls for approved fixes on its next loop (about every 10 seconds by default)
  • Downloads the fix payload with HMAC signature verification
  • Creates a backup before applying
  • Applies the fix using unified diff patches
  • Validates Python syntax before and after application
  • Supports rollback if fix fails

Queue Management

  • Offline Queue: Stores errors when server is unavailable
  • Automatic Retry: Retries failed submissions with exponential backoff
  • Queue Limits: Maximum 1000 entries (oldest-first eviction)
  • Dead Letter Queue: Tracks permanent failures

Backup System

  • Automatic Backups: Created before applying fixes (copies of all existing paths in the patch are written to your PATCHERLY_BACKUP_ROOT directory)
  • Versioned Backups: Timestamped and error ID tagged for restoring historical versions (kept until you delete them)
  • Compression: Gzip compression for storage efficiency
  • Integrity Checks: SHA256 checksum verification

Usage

Basic Error Monitoring

from patcherly_agent import PatcherlyAgent
import asyncio

async def main():
    # Credentials are read from ~/.patcherly/credentials.json
    agent = PatcherlyAgent()

    # Monitor a log file
    await agent.monitor_logs('logs/application.log')

asyncio.run(main())

Manual Error Submission

# Submit an error manually
await agent.process_error({
    'error_type': 'ValueError',
    'error_message': 'Invalid input value',
    'log_line': 'File "app.py", line 42, in process_data',
    'traceback': '...',
    'severity': 'high'
})

Check Connection Status

# Check connection status
status = await agent.check_connection()
print(f"Connected: {status['connected']}")
print(f"Workspace ID: {status['tenant_id']}")
print(f"Target ID: {status['target_id']}")

Security

HMAC Signing

Each OAuth token carries its own per-token HMAC signing secret, returned at issuance and stored in ~/.patcherly/credentials.json. The connector uses this secret to sign every request with X-Patcherly-Timestamp + X-Patcherly-Signature:

  • Per-token secret: bound to the OAuth credential — no global shared secret
  • Mandatory verification: HMAC verification is always required for fix payloads
  • No manual configuration: The secret is bundled into the credential file at OAuth issuance

To rotate, run patcherly logout then patcherly login to issue a new token and secret.

OAuth Credential Management

  • Credentials are stored in ~/.patcherly/credentials.json
  • Never commit this file to version control
  • To revoke: patcherly logout (revokes the token server-side)
  • To re-pair: patcherly login

Verify detection end-to-end with patcherly send-test

After pairing, you can prove the whole pipeline (connector → API → dashboard error feed) works without waiting for a real exception. Test events only flow while you've explicitly opened a Test Mode window from your Patcherly dashboard — this gate prevents an exposed connector credential, a curious script, or a CI run from spraying synthetic traffic into your real error feed and triggering notifications. The window is per-target and lasts 30 minutes; you stay in control.

  1. In your Patcherly dashboard, open Targets, click your target, and flip the Test Mode toggle ON — this opens a 30-minute window for this target only.
  2. On the server where you ran patcherly login, run:
    patcherly send-test
    
  3. The synthetic event lands in your dashboard under Errors, flagged as a sample so it does not affect your metrics or fire notifications.

The CLI auto-checks the Test Mode window (GET /v1/targets/connector-status) before sending. If the window is closed, it prints a clear "Test error window is not open for this target" message plus the exact dashboard URL where you can enable it — no traffic is sent. Pass --no-preflight to skip that check (used in tests).

Keep the connection alive on quiet hosts (patcherly heartbeat)

Once you've paired with patcherly login, the connection is designed to stay alive without you having to reconnect — even if the host sits idle for weeks. Every time the connector talks to Patcherly (sending an error, fetching a fix, polling status), the connection's credentials are quietly refreshed in the background.

If the host you paired is paired but rarely runs the connector (low-traffic site, weekend job server, etc.), wire a tiny once-a-day heartbeat into cron so the credentials never age out:

0 6 * * *  /usr/local/bin/patcherly heartbeat

That one daily call (a) refreshes your credentials long before they expire, and (b) tells the dashboard your connector is still alive (so the "Connector is healthy" check in the onboarding panel stays green). Exits 0 on success, 2 if the host isn't paired yet, 1 on any HTTP / network failure — so cron will email you only when something is actually wrong.

App restart automation

For Python website/app (target) types on plans that include app restart automation, the connector can run post-apply shell steps after a successful patch (e.g. systemctl reload, pm2-style commands on the same host). Configuration is only in the dashboard (Targets → App restart On/Off toggle + Configure); pause without deleting the YAML via the toggle. The command list is delivered over the connector API and verified before execution.

  • Guide: App restart automation
  • Command forms: prefer array-form run: ['python', 'scripts/reload.py'] when you need semicolons inside -c bodies; string-form run: still tokenises and rejects ;, &&, etc. argv[0] must be allowlisted — defaults include python / systemctl / pm2 / supervisorctl.
  • Optional environment variables on the agent host (for example PATCHERLY_POST_APPLY_DRY_RUN, PATCHERLY_WORKFLOW_LOCK_WAIT_SEC): use only if your connector README or support has you set them.

Troubleshooting

Connection Issues

"Cannot connect to server" - Check network connectivity and firewall (outbound HTTPS to api.patcherly.com) - Verify Patcherly service is running

"Authentication failed" / credentials missing - Check that ~/.patcherly/credentials.json exists on the target server. - If it is missing or expired, re-pair with patcherly login.

"HMAC signature mismatch" - The per-token HMAC secret is in ~/.patcherly/credentials.json; re-pair with patcherly login if the credential file is corrupted - Ensure timestamp is synchronized (check system clock / NTP) - Restart the connector after re-pairing

Error Detection Issues

"No errors detected" - Verify log file path is correct - Check log file has read permissions - Ensure errors are being logged - Check log format matches expected format

"Errors not submitting" - Check queue file for pending errors - Verify network connectivity - Check server logs for errors - Review connector logs

Fix Application Issues

"Fix application failed" - Check backup was created - Verify file permissions - Check syntax validation (Python ast.parse) - Review patch format

"Rollback failed" - Check backup integrity - Verify backup file exists - Check file permissions - Review rollback logs

Best Practices

Credentials

  • Never commit ~/.patcherly/credentials.json or .env files to version control
  • Use separate OAuth pairings for different environments (each patcherly login issues a distinct token)
  • To rotate credentials, run patcherly logout then patcherly login

Error Handling

  • Monitor connector logs regularly
  • Set up alerts for connection failures
  • Review error queue periodically
  • Test fix application in staging first

Performance

  • Use appropriate log file monitoring intervals
  • Configure queue limits appropriately
  • Clean up old backups regularly (delete them manually when you no longer need version history)
  • Monitor disk space usage

API Reference

PatcherlyAgent Class

class PatcherlyAgent:
    def __init__(self, log_file: str = 'agent_logs.txt')
    async def start()
    async def stop()
    async def monitor_logs(log_file: str)
    async def process_error(error_context: dict)
    async def check_connection() -> dict

Credentials are read from ~/.patcherly/credentials.json (no constructor parameter for keys).

Methods

  • start(): Start the agent and begin monitoring
  • stop(): Stop monitoring and cleanup
  • monitor_logs(log_file): Monitor a specific log file
  • process_error(error_context): Submit an error manually
  • check_connection(): Check connection status

Next Steps