Skip to content
Liam-magicbookPublic

About

Super control pad on adding new server agent node

Resources

Stars

28 stars

Watchers

1 watching

Forks

Repository files navigation

PAD

English · 中文 · Français · 日本語 · Русский

One brain, commanding work across many machines. The state sits in the middle, not in anyone's head.

One process, one SQLite file, two doors.

Four panels: quickstart, fleet digest, MCP tool surface, task report

All four panels are frames from a real run, generated by docs/make_preview.py from actual output — not a mockup.


PAD is not an agent

Get this boundary straight first, or the rest reads wrong.

brain (ChatGPT / Claude / a human CLI / a timer)  →  sends goal / constraints / acceptance
PAD                                              →  task state, routing, evidence ledger
node daemons × N                                 →  claim work, execute, report
work agent (opencode / claude / codex)           →  actually does the job

PAD is persistent state plus routing, with no intelligence inside. It does three things:

  • Tracks task lifecycle and current progress across all machines.
  • Indexes node capabilities and matches incoming work to nodes ready to claim it.
  • Preserves complete execution reports, output artifacts, and criterion evidence in an append-only ledger.

The intelligence is all external: ChatGPT or Claude over MCP, a timer, or an operator at the command line. A session is stateless and volatile — it does not remember yesterday and cannot see what a machine is doing right now. PAD lifts that state out and adds a routing layer, so the brain only has to handle intent, judgment and acceptance.

Why that matters

An ordinary agent ties its state to one conversation. The moment work spans several machines, awkward prerequisites or long time horizons, that shape breaks:

  • Session volatility: Resetting a conversation erases memory, forcing operators to restate context, constraints, and project history from scratch.
  • Node blindness: A model session cannot inspect what tools, dependencies, or access credentials exist on remote physical hosts.
  • Context saturation: Packing intermediate logs, environment outputs, and scratch files into the prompt degrades reasoning and eventually exceeds the context window.

PAD moves that state into SQLite instead:

  • Zero-knowledge orchestration: The brain issues work against declared capabilities rather than physical hostnames. Replacing, rebooting, or adding a machine requires zero adjustments to the model's instructions.
  • Coordinated execution without drift: Every node claims work against the same structured goal, constraints, and acceptance criteria, reporting verification back into a unified ledger.
  • Fleet-wide observability: A single call to get_digest returns a comprehensive view across every node, replacing narrow session-level guesses with verified system state.

PAD's four layers

One brain can govern many kinds of work

The usual multi-agent setup gives each kind of work its own agent. Nothing is shared, so a person ends up relaying between them by hand — carrying outputs, constraints and progress from one window to the next. The more workflows, the more relaying, until the relaying itself is the bottleneck.

PAD keys routing on capability instead — not on a hostname, not on an agent's identity:

              hub: tasks / state / evidence / history
                            │
        ┌───────────────────┼───────────────────┐
   capability A        capability B        capability C
   node 1, node 2      node 3              node 4, node 5
   high concurrency    exclusive resource  long background

One brain, the same nine MCP tools, driving very different workloads at once. It never learns which machine runs what or with which tool; it states the outcome, and PAD hands the task to a node that declared the capability.

The gain is not concurrency. What changes is how many kinds of work one brain can govern, with the state still in a single place. Each task runs in the private environment of whichever node claimed it, so a broken toolchain or a timeout on one box never leaks into another's state.

One hub, two ways to use it

PAD supports two operational modes through the exact same system interface:

Governed Autonomous
Who issues orders a human names the capability and goal on the command line the brain decides how many tasks and which capabilities
Constraint every field pinned by a human capability list declared by a human, the rest left to the brain
Good for clear results, strict acceptance research, decomposition, multi-round work
Cost to switch one line of YAML one line of YAML

Both modes write to the same task schema, pass through the same acceptance criteria validation, and populate the same digest. Switching between operator-governed execution and agent-driven autonomy requires no schema migration and preserves a continuous audit trail. Operators retain ultimate authority by explicitly declaring the envelope of available capabilities, retaining the ability to narrow or revoke autonomy at any point.

One hub, two months in production, three kinds of brain:

Brain driven Human command line Scheduler
tasks 854 39 36
evidence coverage 96% 47% 100%
median duration 14.6 min 0.7 min 341 min
machine-hours 632.9 6.9 194.4
median goal length 301 ch 131 ch 611 ch
with acceptance criteria 98% 79% 100%
capabilities touched 12 8 1

The three profiles separate cleanly. Work handed to the brain is specified: 301-character goals, 98% carrying acceptance criteria, spread over 12 capabilities. Work typed by hand is directed: 131 characters, median under a minute. The scheduler sits at the other extreme — one capability, the longest goals, a 47% failure rate, and nobody watching to notice its target has moved.

What it solves

Problem PAD's answer
New session loses everything; context will not hold it State lives in SQLite, not the session. The brain just re-reads it
The brain does not know what a machine has or can do A capability table. The brain declares what is needed; PAD routes
Adding a machine means editing prompts and code Adding a machine is one YAML registration; zero change on the brain side
Many agents drift apart One task definition in, one ledger out
"Done" with no evidence Completion must answer every acceptance criterion, or it is marked unanswered

How the three modes differ

Personal agents and work agents represent distinct interaction models rather than mutually exclusive product categories:

Dimension Personal Agent Work Agent PAD
Role assists a person continuously completes specific work runs a working system
Main input preferences, feeds, events explicit task and material information, goals, tasks, feedback
Initiative high task-centric proactive intake, governed execution
Execution follows up autonomously one or multi-step run many agents, many nodes
Context personal long-term memory task context task state and project history
Decision agent judges a lot user defines the goal human holds direction, agent assists
Value you miss nothing the work gets done turning information into output, continuously
Information · Personal Agent
  continuously collects, filters and ranks what matters
        ↓  "here are a few things worth your attention"
Decision · you + PAD
  judge what is worth doing; set goals and priority
        ↓  "make it look like this; these are the acceptance criteria"
Execution · Work Agents
  development, content, research, deployment, distribution
        ↓  "done, here is the evidence, here is where it got stuck"
Feedback · task state and artifacts
  run records, results, problems, next round of adjustment

The load-bearing part is the line in the middle. A fully autonomous agent collapses "this looks worth doing" and "do it now" into one step: it spots something new, patches the code, adds a feature, ships it, and leaves behind a pile nobody asked for. PAD separates the two in the architecture itself, not with a prompt asking it to check first.

Three more things

Tasks are durable objects, not one exchange. A normal agent interaction is state a task, run it, return, done — and the session closing takes it all with it. PAD takes the task out of the session: dispatch, progress, state transitions and the final result live in the hub. What you see is a continuous stream of work, not a heap of severed conversations.

Exploration and production need different rules. Exploration wants reach and new information; production wants a bounded scope and a result you can rely on. Mix them and you get the familiar runaway — a fresh idea mid-run, more rules, more features, and the original goal drifting out of sight. PAD gives each its own entry point, node configuration and acceptance criteria.

What accumulates is the working system. An ordinary agent's capability comes from the model, and changing the model resets it. What PAD builds up is how nodes divide the work, how tasks actually ran, where things broke, and which execution patterns are worth reusing. The model is the engine; the system is the car.


Now run it

Quickstart

git clone https://github.com/Liam-magicbook/pad.git && cd pad
./quickstart.sh

Under three minutes, the script creates a Python virtual environment, installs dependencies, generates an access key, launches the server, boots a local node daemon, executes an end-to-end task, and displays the resulting report.

Quickstart

The script outputs an MCP endpoint URL ready to be configured in ChatGPT or Claude. Stop the background processes at any time with ./quickstart.sh --stop.

Requirements: Python 3.10+ with standard virtual environment tooling. No Docker daemon or external database service required.

What you get

The server exposes two distinct interfaces from a single process:

/pad/<KEY>/mcp        for the brain (ChatGPT / Claude) — 9 task tools
/pad/<KEY>/api/...    for each machine's node daemon — register / claim / report

Large language models cannot and should not retain fleet topology or task history in their prompt context. PAD maintains authoritative state in SQLite. When an operator requests an update, the model issues a single get_digest call to inspect the fleet:

nodes:
  mac-1       running 2 [build:12min, test:0min]
    caps: build.heavy, test.suite
  vps-1       idle
    caps: scrape.batch
active (2):
  t-20261102-a3f2   running   build.heavy   6 targets rebuilt
done today: 7

If the brain needs to schedule batch scraping, it submits a task targeting scrape.batch. Node vps-1 claims the task automatically because it declared matching capability and has capacity — the model never needs to know the machine name.

digest


Technical detail

Connecting a brain

The MCP endpoint is PAD's sole external control interface. The brain accesses nine focused task tools, with no direct access to shells, filesystems, or host identifiers:

get_digest(recent_limit=10)          fleet state: who is up, what is running, done today
submit_task(capability, goal, …)     dispatch a task (files optional)
get_task(task_id)                    one task: status + report stream + artifacts
get_artifact(task_id, name, offset)  paged read of an artifact
get_input_file(task_id, name)        open a file dispatched with the task
close_task(task_id, reason)          cancel (kills the local process group)
probe_file_upload(files)             end-to-end attachment path check
get_artifact_delivery / observe      signed link and frame sampling (optional)

MCP tool surface

ChatGPT: Navigate to Settings → Connectors (Developer mode) → Create. Set the MCP Server URL to http://<host>:8790/pad/<KEY>/mcp, trigger Scan tools, and verify that the nine tools appear. Enable PAD within a new conversation.

Claude Code / Standard MCP Clients:

{
  "mcpServers": {
    "pad": { "type": "http", "url": "http://127.0.0.1:8790/pad/<KEY>/mcp" }
  }
}

Local loopback connectivity does not imply public reachability. To connect remote or cloud clients, see Deploying.

One PAD, three brains

The role of the brain is swappable. Any client adhering to the MCP contract can drive work through the same centralized interface:

Who sits in the slot Authority Entry point
Chat model Delegating: you ask in prose, the model decides how many tasks and which capabilities MCP /mcp, created_by=chatgpt
You Direct: you name a capability and a goal on the command line padctl submit, created_by=human
Scheduler Periodic: submits the same pipeline every day internal timer, created_by=daily-auto-timer

Every task submission invokes the same submit_task route, writes to the same tasks table, enforces the same acceptance gate, and appears in the same digest. Downstream execution does not branch on caller identity.

Production data across 46 active days on a live deployment demonstrates how authority naturally distributes across brains:

Chat model You Scheduler
tasks 854 39 36
completed 680 36 19
failed 111 2 17
evidence coverage 96% 47% 100%
median duration 14.6 min 0.7 min 341 min
machine-hours 632.9 6.9 194.4
median goal length 301 ch 131 ch 611 ch
with acceptance criteria 98% 79% 100%
capabilities touched 12 8 1

The measured workloads reflect distinct operational characteristics:

  • Model-delegated tasks are systematically specified: goals average 301 characters, 98% include explicit criteria, and execution spans 12 distinct capabilities with high evidence verification.
  • Operator-submitted tasks are concise and direct: goals average 131 characters with a median duration of 0.7 minutes, accumulating only 6.9 machine-hours across two months.
  • Scheduled pipelines execute static, repetitive goals with extensive durations (median 341 minutes) and a 47% failure rate, highlighting the brittleness of unattended routines when environmental targets shift.

Because switching requires zero operational overhead, 10 of the 46 active days feature interleaved submissions from both the chat model and human operators. Deterministic tasks execute via direct CLI invocation, while ambiguous problems requiring decomposition are delegated to the model.

Extending or restricting autonomy does not alter the hub's architecture; it simply involves editing which capabilities are declared. Keeping authorization anchored to explicit capability registrations guarantees that permissions remain auditable and immediately revocable.

Multi-machine coordination without a coordinator

PAD couples centralized state management with decentralized task execution. This architectural separation enables reliable multi-machine coordination without running an active scheduler daemon.

The hub maintains no queue workers and runs no local task code. When node daemons poll the server, the hub answers two deterministic queries: matching pending tasks against declared capabilities, and updating active leases. Each node daemon polls independently and claims work only when local concurrency permits.

Tasks are claimed by nodes rather than dispatched by a central coordinator. This decentralized pull model yields four concrete operational benefits:

  • Zero scheduler complexity: Task claims resolve against capability indexes in SQLite, removing the need for distributed consensus protocols or global load monitors. Registering a node requires a single HTTP request; unregistering occurs automatically on heartbeat timeout.
  • Inherent backpressure: A node configured with concurrency: N halts polling once N tasks are active. Surplus tasks wait transparently in SQLite where queue depth is globally observable.
  • Contained node failures: If a node crashes mid-execution, the task remains marked as running until its lease window elapses, allowing subsequent recovery from recorded checkpoints. Reports and artifacts remain append-only and idempotent.
  • Unified audit trail: Every claim transaction, execution log, intermediate artifact, and completion report is stored directly in database rows, eliminating separate tracing infrastructure.

For small to medium fleets operated by a single team, trading distributed scheduling complexity for a single SQLite-backed hub provides high operational stability with minimal maintenance overhead.

Adding a machine

Each node requires only a configuration file and a lightweight daemon process. Incorporating a machine requires zero code changes to the PAD hub:

# on the new machine
export PAD_HOME=$HOME/pad-ld
BASE=http://<host>:8790/pad/<KEY>

mkdir -p "$PAD_HOME"
curl -fsS "$BASE/agent.bundle.tgz" | tar xz -C "$PAD_HOME"
cd "$PAD_HOME"
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt

cp ld_config.node.yaml.example ld_config.yaml
vi ld_config.yaml                 # node_id / capabilities / pad_url
.venv/bin/python ld_agent.py ld_config.yaml

Fetching agent.bundle.tgz delivers the exact daemon runtime and prompt templates currently running on the server, preventing version drift across machines. If the server runtime updates, node daemons flag the fingerprint mismatch upon registration and can re-fetch the bundle without overwriting local configurations. Detailed onboarding steps, self-checks, and operational guidelines are documented in JOIN.md.

Two ways to declare a capability

Node capabilities support both deterministic scripts and autonomous agent runtimes:

capabilities:

  # 1. deterministic: call a command
  scrape.batch:
    desc: "batch scrape, read-only"
    cmd: "cd ~/projects/scraper && python3 crawl.py --out $PAD_WORK_DIR/data.json"
    timeout_min: 120

  # 2. needs judgment: hand it to a local agent CLI, headless
  build.heavy:
    desc: "heavy code tasks"
    cmd: "python3 /opt/pad-ld/harness/run_harness.py opencode"
    timeout_min: 240
    concurrency: 2

run_harness.py adapts task descriptions into headless CLI invocations across different agent runtimes, allowing operators to switch backends (opencode, claude, or codex) via a single command argument. PAD dispatches requirements without coupling to specific model providers, enabling heterogeneous environments across machines.

Regardless of execution method, all tasks conform to the same completion protocol: the worker writes a structured $PAD_WORK_DIR/result.json specifying done or failed, an explanatory narrative, and per-criterion verification evidence. In the absence of result.json, the task is marked as failed regardless of a zero process exit code.

Ask this before adding a capability

Before declaring a capability across multiple nodes, evaluate resource contention:

If two machines execute this task concurrently, will they collide?

Answer What to do
Yes (shared login state, same output file, same resource) Only one may declare it
No (scraping, tests, pure reads) Declare the same name on many nodes — that is horizontal scale

Never rename conflicting capabilities simply to bypass contention limits. The hub treats distinct capability keys as independent queues, resulting in conflicting parallel dispatches.

Design principles: what failure each choice prevents

Each of these principles exists because a specific failure was actually observed:

  • State must outlive the process that produced it. Both context windows and executor processes eventually terminate. Treating context windows as permanent storage introduces truncation errors without providing indexing, schema validation, or multi-node access. Durable facts reside in an append-only SQLite database backed by a single persistent server process.
  • The interface must be narrower than the capability. Expanding tool interfaces inflates prompt overhead and increases combinatorial failure modes. PAD restricts the brain to nine task management primitives, delegating all environment actions—shells, filesystem modifications, browsers, and network calls—to node-level executors.
  • Route on what, never where. Specifying target hosts couples dispatchers to fleet topology, breaks dynamic scaling, and pollutes audit ledgers with transient machine names instead of intent.
  • The edge knows its environment better than the center. Centralized dispatchers lack direct visibility into local execution details, including repository cleanliness, package manager paths, authentication cookies, and available storage. Tasks declare target goals and constraints, leaving execution planning to the node.
  • Evidence is an artifact, not an assertion. Process return codes fail to convey whether semantic goals were satisfied. Completed tasks must present concrete, verifiable artifacts mapped against every declared acceptance criterion.
  • Failure is a first-class outcome. Long-running executions that uncover unachievable constraints produce valuable findings. Dropping failed runs as exceptions discards analytical insights; failures are recorded alongside structured evidence.
  • Deliberate non-goals: PAD intentionally excludes central automated planners, message brokers, vector databases, interactive mid-task chat loops, multi-tenant partitioning, and language models in the core routing path.

Deploying

Exposing the server beyond local loopback requires TLS termination and reverse proxying:

internet → reverse proxy (nginx/caddy, TLS) → 127.0.0.1:8790 (uvicorn)

Key operational considerations when configuring the proxy:

Item Note
PAD_ALLOWED_HOSTS MCP DNS-rebinding allowlist, matched literally against the Host header including port. A host missing from it returns 421 while REST still returns 200 — brutal to diagnose
PAD_KEY This is the auth. It sits in the URL, so treat it as a long-lived revocable secret. Do not paste it in public
Proxy body limit Defaults are small; tasks with files 413. Set client_max_body_size 32m on the /pad/ location
Do not restart the server systemctl restart pad kills in-flight task process groups. Restart in a quiet window

Production startup script:

# server
PAD_KEY=$(cat data/.pad_key) \
PAD_DB=/var/lib/pad/pad.db \
PAD_ALLOWED_HOSTS=pad.example.com,localhost,127.0.0.1 \
.venv/bin/python -m uvicorn server:app --host 127.0.0.1 --port 8790

Service definitions are provided in deploy/pad.service and deploy/pad-node.service for systemd, and deploy/com.pad.ld.plist for launchd on macOS.

Dispatching files with a task

Tasks frequently require reference assets without exhausting prompt context windows. PAD supports two file intake pathways:

  1. files (ChatGPT attachment handling): Users upload attachments directly in chat. ChatGPT forwards download descriptors via _meta["openai/fileParams"] containing {download_url, file_id, mime_type, file_name}. PAD streams the payload directly to disk within the active tool call, preventing raw bytes from entering model context.
  2. input_files (Direct byte payloads): Automated callers supply file payloads formatted as {name, mime_type, data_base64}.

Both channels enforce uniform constraints: at most 8 files per task, at most 10 MiB per file, and an aggregate payload cap of 15 MiB. Uploaded files land in the node's local inputs/ directory alongside an authoritative manifest.json containing SHA-256 digests. Supported formats include images, PDF documents, Office spreadsheets, CSV/JSON data, tar/zip archives, audio, video, and subtitle tracks.

Using the command line client:

./padctl.py submit --capability build.heavy --goal "rework the plan per this report" \
  --file ~/Downloads/quarterly-report.pdf --file ~/Downloads/data.xlsx
./padctl.py inputs t-20261102-xxxxxx          # which files this task carries
./padctl.py input  t-20261102-xxxxxx --out .  # download and verify

Because attachment download_url tokens are short-lived, PAD ingests files synchronously. Download requests are hardened against SSRF: connections require HTTPS, IP targets are resolved and checked to block private, loopback, and reserved ranges, and redirections are re-evaluated at every hop with strict size accounting.

Repository layout

File Purpose
server.py Server (FastAPI + MCPServer + SQLite); both doors
db.py / schema.sql State layer
ld_agent.py Node daemon running on each machine
harness/run_harness.py Translates a task into a headless opencode / claude / codex call
harness/run_script.py Wraps a command into the PAD contract (for deterministic work)
harness/prompt.md.tmpl Executor prompt. Change the rules only here
padctl.py Human CLI (also handles --file)
pad_mcp.py Debug client over MCP (same path as the brain)
quickstart.sh One-command start
contract.md The contract: dispatch basis, report format, execution discipline
JOIN.md Node join protocol + self-checks + trap list
CHANGELOG.md Behavior changes; check here first when debugging
docs/make_preview.py Regenerate the hero GIF (real output → frames → ffmpeg)
tools/collect_stats.py Pull task details from a live PAD to reproduce the numbers below
README.{zh,fr,ja,ru}.md Chinese / French / Japanese / Russian versions

Development

Run local test suites:

.venv/bin/python -m unittest discover -s tests -v   # unit tests
bash tests/e2e_input_files.sh                       # end-to-end: temp server + node, real path

Preview graphics are generated directly from live system executions rather than synthetic mockups. Re-render previews when altering output styling:

./quickstart.sh          # a local PAD must be running
.venv/bin/python docs/make_preview.py

Measured

Two months of production logs on live infrastructure:

Metric Value
Nodes / capabilities 6 machines, 20 capabilities, 3 vendors' agent CLIs
Real tasks (excluding self-checks) 938, 835 machine-hours
Long tasks 18.7% over 1 hour, 25 over 6 hours, longest 42 hours
Per-criterion evidence on completed tasks 93%
Failed tasks still delivering a structured report 42%
Dominant failure cause 64/132 are executor-environment faults (not the model failing)

That last line is the whole design argument: most long-horizon failures happen in the environment, not in the reasoning. A single-loop harness registers them as a generic tool-call error. PAD sees "this capability on this machine is broken", which is the version you can actually fix.

tools/collect_stats.py pulls task details from a running PAD and reproduces every number above.

Known gaps

  • Authentication relies on pre-shared URL tokens without OAuth support. Introducing OAuth requires inserting an authentication layer at the reverse proxy boundary without altering core protocols.
  • Tasks that terminate unexpectedly during execution without delivering terminal reports require external lease sweeping to trigger automated reassignment.
  • The 10 MiB individual file size limit stems from buffering payloads in server memory; handling larger files requires implementing chunked disk streaming.
  • Structured file parsing (such as PDF text extraction or document conversion) is omitted within the PAD hub, leaving interpretation to node-local tools.
  • Remote integration currently focuses on personal developer mode; enterprise workspace publishing paths are not implemented.

MIT License

About

Super control pad on adding new server agent node

Resources

Stars

28 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages