Python VPS starter

HUB documentation · templates/python/README.md

← Documentation library · Download Markdown

Production dashboard / HUB API: https://bamboubot.duckdns.org
Set HUB_URL=https://bamboubot.duckdns.org. Local-development commands and loopback bind addresses below are development/operator reference, not the production API origin. Use the existing hosted HUB; you do not need to deploy a new one.

Introduction

Requires Linux and Python 3.10+. Keep this template inside the repository: it imports the shared agent/ supervisor and streaming backup code, not a copy.

cd templates/python
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
umask 077
cp --no-clobber .env.example .env
mkdir -p runtime
cp --no-clobber config.example.json runtime/config.json
cp --no-clobber proxies.example.txt runtime/proxies.txt
chmod 600 .env runtime/config.json runtime/proxies.txt
# Edit .env locally, then:
python main.py

Use a site-scoped agent token, not an admin token. Public HUB_URL must be an HTTPS origin (HTTP localhost is allowed for development). Redirects are disabled. The token stays in the supervisor environment; it is not written to runtime files or inherited by workers. Protect the local .env with mode 0600.

Actual .env and credential-bearing runtime files are intentionally not shipped. The sample HUB_AGENT_TOKEN is a placeholder: replace it locally. GNU cp --no-clobber leaves existing content untouched. This starter loads the .env beside main.py using python-dotenv (override=False); exported environment values win. Relative paths resolve from the working directory, not .env.

Environment name Required/default
HUB_URL Required HTTPS origin
HUB_AGENT_TOKEN Required site token
HUB_RUNTIME_DIR Optional ./runtime
HUB_STATE_DIR Optional ./agent-state
HUB_WORKER_COMMAND Optional; absolute bundled exampleworker via current Python
HUB_BACKUP_DIR Optional; unset disables archive source

Backup size is a CLI option --backup-max-bytes (default 1073741824), not HUB_BACKUP_MAX_BYTES in Python. CLI flags override environment values.

Local configuration

CLI flags override environment settings. Runtime defaults to ./runtime relative to the current directory. To discover an existing script's config.json and proxies.txt, use python main.py --runtime-dir /opt/script (or HUB_RUNTIME_DIR). That directory becomes mode 0700. Do not choose a shared directory. No automatic directory search is performed; copy examples to the directory you select.

On first enrollment, GET config precedes a revision-guarded POST local-sync, even without local files. Optional JSON fields are ai_api_key, number_of_workers (integer 0–100, not boolean), and ai_preset_id; strings are required for the two AI fields. No JSON key or seed file is required. Empty AI keys and zero workers are valid; ai_preset_id must be nonempty (sample default). revision is managed by the hub. Other fields are ignored for upload and preserved in the local config.json on publication. Proxy import accepts URL lines and ignores blanks/comments. The hub validates the import atomically. Invalid imports preserve local files. An enrollment race refetches and retries once without local data, never overwriting admin edits. An initialized hub prevents deleted proxies being resurrected after checkpoint loss.

Thereafter the hub is authoritative for those fields: manual managed-field edits are overwritten on each 30-second config poll and on start/restart. Runtime contains private mode-0600 config.json, proxies.txt, .sync.json, and backup-config.json (safe schedule, destination IDs and status only; no cloud credentials). Proxy credentials are percent-encoded in canonical URLs. Clearing keys/proxies writes empty values/files. Each file is fsynced and atomically replaced; checkpoint is published last, not a cross-file transaction. Readers should check config revision after reload. Site deletion revokes authentication but does not delete VPS files.

Unrelated settings/secrets can remain in extra JSON keys, but prefer separate automation-settings.json and local .env. Neither is automatically inventoried or uploaded via configuration sync; extras do not create dashboard fields. Invalid/non-object config.json blocks publication without replacing runtime files, including after enrollment. Repair it locally. AI usage key identifiers should be nonsecret aliases, never credentials; there is no automatic AI usage reporting or AI-usage config field in this starter. Optional backups are distinct: they may include these files inside your selected source. Keep secret env files outside the backup source or encrypt backups when needed.

Preserved extras are not automatically injected into the worker's hub config. Load local settings explicitly in your hooks. Python workers inherit the supervisor environment minus hub credentials; approve secret access locally and never add HUB_AGENT_TOKEN to workers. For opt-in supervisor reporting helpers, see the telemetry schemas and examples.

Integration hooks

exampleworker.py is a harmless idle loop, not business logic. Implement AutomationHooks.process_next_task, returning True only after a real task. Use bounded timeouts and cancellation. task_completed() and error() report over a local inherited Unix datagram socket, without a hub token. Error events have fixed sanitized messages; never add secrets to logs. IPC is best effort, bounded, and counters are in-memory, not a durable accounting system. The default placeholder reports zero completed tasks. CPU/RSS come from psutil. AI key/preset and proxy pool are provided in worker environment at spawn; restart workers to apply changed environment values. No captcha integration.

Heartbeat is every 15 seconds, lifecycle polling every 5 seconds. Only start/stop/restart are accepted; the worker command is chosen locally using --command or HUB_WORKER_COMMAND, parsed without a shell. Completed lifecycle commands are persisted before acknowledgment and acknowledgments retried without repeating actions. A crash between action and persistence can repeat it: this is not exactly-once execution. Failed workers require explicit restart.

Opt-in site statistics

Agent.report_statistics(links_sent_successfully, average_messages_per_chat) is an explicit supervisor-side API wrapper. It is never called by the starter loop. The template also exports a function accepting the existing authenticated Agent.request helper. Example from repository-root import context, inside your trusted supervisor integration (arguments come from your real accounting):

from agent.agent import Agent
from templates.python.site_statistics import report_statistics

def publish_site_snapshot(agent: Agent, site_links_sent_successfully,
                          site_average_messages_per_chat):
    return report_statistics(agent.request, site_links_sent_successfully,
                             site_average_messages_per_chat)
    # Equivalent: agent.report_statistics(site_links_sent_successfully,
    #                                     site_average_messages_per_chat)

POST /api/agent/statistics sends exactly links_sent_successfully and average_messages_per_chat, both required. Counter: integer 0–9007199254740991; average: finite number 0–1000000000 or None for unknown/no chats. Booleans, negative, nonfinite and out-of-range values fail before any request. Use one reporter per site. Supply real accumulated whole-site snapshots, not increments or worker/task counters. Every report overwrites both values; retries of the same snapshot do not add counts. You own aggregation, persistence and explicit resets. Nothing sends zero automatically; send zero only if measured. The helper does not define a successful link or chat. Derive a weighted average as total messages / total chats only if those match your definitions; otherwise provide your actual computed average, or None when unknown. Workers have no agent token: report aggregates from the supervisor only (or a trusted custom caller with its own authenticated request helper and token from the environment). Do not add the token to worker environments or payloads.

Backups

Set HUB_BACKUP_DIR or --backup-dir to a local codebase directory. The independent backup thread polls schedule/requests without blocking lifecycle control. Periodic backups default off; minimum interval is 15 minutes. On-demand requests still work while periodic backups are off. BackupRunner.upload(path, source, request_id=None) streams arbitrary files without an extension filter. Archives are bounded (default 1 GiB, --backup-max-bytes), streamed to private temporary storage, canceled/cleaned on shutdown, and never buffered in RAM. Keep state/scratch outside the source when possible; nested agent state and a strictly nested runtime directory are excluded. If runtime equals the source, ordinary codebase files remain included, including managed config/proxy files. Other codebase configuration including .env is included and may contain secrets: review your source tree. Do not store the hub token inside the selected backup source. Symlinks are archived without following them. Use a stable tree or filesystem snapshot for consistency. Last accepted upload time persists; ambiguous responses are reconciled with the request queue instead of falsely reporting failure. Cloud credentials stay on the hub. No dashboard-supplied paths or executable commands are accepted.