A control plane for managed coding-agent work. Self-hosted, privacy-first, and standalone — nothing else is required to run it. It can optionally integrate with BrainConnect for trusted long-term memory.
AgentConnect is a task, artifact, decision, review, routing, and handoff backplane for coding agents. It provides a managed launch / shell workflow for tools like Codex or Claude Code, records agent work in an operator ledger, injects bounded context into workers, supports review and audit, and prevents normal managed-agent sessions from completing their own tasks. It is a compliance/control layer, not a security sandbox.
Agents may think and work inside their own harness. But durable work must enter AgentConnect. If it is not recorded in AgentConnect, it did not happen.
If you are installing AgentConnect to run a coding agent — Codex, Claude Code, or anything else — start with docs/OPERATOR_GUIDE.md. It walks the whole loop with the exact commands, and its failure-modes section lists what actually goes wrong.
The short version:
git clone <this-repo> && cd AgentConnect
python -m venv .venv && source .venv/bin/activate
# The CLI is required. Core alone gives you the library, not the command.
pip install -e packages/agentconnect-core -e packages/agentconnect-cli
export AGENTCONNECT_DB_PATH=/srv/agentconnect/agentconnect.db
agentconnect --help # must work, and must be on PATH inside the agent's shellThen:
agentconnect tasks create --title "…" --goal "…" --by you # 1. create a task
agentconnect launch codex --task "$TASK" --claim --repo . # 2. workspace, claim, scoped token
agentconnect shell --task "$TASK" -- <agent-command> # 3. the agent runs here
# inside, the agent records its work:
# agentconnect tasks context-pack "$AGENTCONNECT_TASK_ID"
# agentconnect attempts add … --actor "$AGENTCONNECT_MANAGER_ID" --summary "…"
# agentconnect decisions add … --decision "…"
# agentconnect subtasks submit … --instructions "…"
agentconnect audit "$TASK" # 4. where is it in the ledger?
agentconnect complete "$TASK" --by you # 5. operator onlyagentconnect launch prepares a workspace, a git worktree, generated agent instructions,
and a short-lived scoped session token. agentconnect shell runs the agent in a sanitized
environment. The audit reads the ledger without writing to it. Completion updates
AgentConnect first, and only then any tracker.
Installing only agentconnect-core, agentconnect-router, and
agentconnect-model-manager is not enough for this loop — you will not have the
agentconnect command. Install agentconnect-cli.
AgentConnect is not a sandbox. It is a compliance and control layer. It makes
AgentConnect the normal path and makes bypasses visible; it does not contain a hostile
process. AGENTCONNECT_DB_PATH is forwarded into the agent's environment on purpose (so
the agent's tools reach the operator's ledger rather than a private fallback), backend
credentials never are, and AGENTCONNECT_MODE restricts what the CLI will do inside a
managed session. Direct SQLite, filesystem, and environment tampering are out of scope
and would need OS-level isolation.
The HTTP and MCP transports authenticate: every route but GET /health and every MCP
tool call resolves the agent's scoped act_ token through one authorize() rule. A
managed agent cannot complete its own task, promote memory, or act on a task it was not
launched for. Ordinary completion cannot skip the audit; an administrative override is a
separate operator-only endpoint that demands a written reason and records it. The CLI
is the exception — it consults no token, and AGENTCONNECT_MODE is all that stands
between an agent and agentconnect complete.
AgentConnect does include local content scanning at two surfaces it owns: artifacts are scanned on ingest (probable secrets redacted before the body is written), and context packs are scanned before an agent receives them (secrets redacted, high-risk prompt injection withheld, always with a warning — never silently). AgentConnect owns the policy; detection is modular. The default install runs a standard-library baseline engine — no service, no LLM, no dependency — and maintained engines (detect-secrets, TruffleHog, Gitleaks, Presidio) are opt-in extras. There is no PII detection unless you enable Presidio, and none of it stops direct SQLite, file, or environment tampering. See docs/SAFETY.md.
The full boundary — what the layer buys you and what it explicitly does not — is at the top of docs/OPERATOR_GUIDE.md.
See docs/STATUS.md for the verified checkpoint, the gate count, and an honest account of what the test suite does not prove.
The dogfood run behind the current checkpoint validated the managed-agent loop using
DirectExecutionBackend, with no Temporal, no Linear, and no configured external memory
backends. Adapters for BrainConnect, Cognee, and Graphiti exist and are exercised
against transport doubles and in-process semantics; no real service has answered over the
network.
Independently-installable packages in one repo (a PEP 420 namespace, so every import path
stays agentconnect.*):
| Package | Role |
|---|---|
agentconnect-core |
The ledger, service layer, audit, workspace, sessions. Required. |
agentconnect-cli |
The agentconnect command. Required for the managed loop. |
agentconnect-mcp |
MCP adapter — the tools a managed agent sees. |
agentconnect-api |
HTTP adapter. |
agentconnect-linear |
Linear mirror (optional). |
agentconnect-temporal |
Durable execution backend (optional). |
agentconnect-router |
Advanced: deterministic model/worker routing control plane. |
agentconnect-model-manager |
Advanced: local inference appliance. |
agentconnect-runtime |
Advanced: LangGraph act/tool worker runtime. |
AgentConnect is standalone. None of these is required, and none is configured by default.
BrainConnect is an optional trusted-memory ledger integration. AgentConnect works
without it, using local task state and the default no-op memory adapter. Also optional:
Linear (tracker mirror), Temporal (durable workflows), Cognee (broad retrieval), Graphiti
(temporal graph), and a local model manager. Real BrainConnect over real HTTP is served
by brainconnect serve, which ships in the BrainConnect repository.
Two sibling projects ship v0.1.0 runtimes in their own repositories, and AgentConnect consumes each of them optionally. It runs, today, with neither:
- ComputeConnect owns model providers, local inference, and execution backends. AgentConnect consumes it through an HTTP local-compute provider, and ships its own execution, routing, and model seams as fallbacks. The contract is docs/COMPUTECONNECT_CONTRACT.md.
- ToolConnect owns tool discovery, permission, and invocation. AgentConnect consumes it through a fail-closed governor client, and ships a fixed MCP tool set as fallback. The contract is docs/TOOLCONNECT_CONTRACT.md.
Neither is a Python dependency, neither is configured by default, and nothing in this repository requires them.
BrainConnect was renamed from WikiBrain, and the rename has since reached its code:
the sibling's package and CLI are brainconnect (there is no wiki CLI). AgentConnect's
own identifiers still say wikibrain — the adapter class WikiBrainMemoryAdapter and
the environment variables WIKIBRAIN_URL / WIKIBRAIN_TOKEN.
Those names are load-bearing — do not "correct" them in a config file. This
documentation says BrainConnect when it means the product and wikibrain when it means
an identifier you must type. The env-var naming reconciliation is done, not pending:
bootstrap registers both spellings as first-class backends of the same service
(wikibrain under WIKIBRAIN_URL / WIKIBRAIN_TOKEN, brainconnect under
BRAINCONNECT_URL / BRAINCONNECT_TOKEN; configure one, not both). The Connect
ecosystem deployment wires BRAINCONNECT_*, which is the preferred spelling for new
deployments; WIKIBRAIN_* remains supported, with no deprecation scheduled.
Everything below documents the deterministic router and local model manager: a two-service path where an agent hands tasks off over MCP, a router classifies each task, keeps sensitive context out of the wrong models, routes to local GPU / free / paid cloud, and returns compact summaries plus artifact references. It runs with no GPU (a built-in stub model); plug in one model server for real output.
This stack is orthogonal to the managed coding-agent loop above. If you only want to run a coding agent under AgentConnect, you do not need any of it.
pip install -e packages/agentconnect-core \
-e packages/agentconnect-router \
-e packages/agentconnect-model-manager \
-e packages/agentconnect-runtime
pytest -q # fully offline; see docs/STATUS.md for the gate count
python examples/demo.py # submit tasks; watch classify → route → summarize
python examples/federation_demo.py # a friend's box drains a shared queue, privacy enforcedWire the router into Claude Code by adding to your .mcp.json:
{ "mcpServers": { "agentconnect": { "command": "agentconnect-router" } } }Claude Code now has tools like submit_task, queue_add, and read_artifact_chunk
(full list below). Out of the box, tasks route to a built-in stub
model (deterministic echo), so the pipeline works with zero infrastructure.
Get real model output — point the Model Manager at any OpenAI-compatible server (Ollama, vLLM, llama.cpp, SGLang). On a single box the Router embeds the manager, so no TLS/second process is needed:
export MODEL_MANAGER_BACKEND=openai
export MODEL_BACKEND_URL=http://localhost:11434/v1 # e.g. Ollama
agentconnect-routerThat model server is the only external thing you supply. Free/paid cloud providers, a separate GPU box over mutual TLS, and remote-worker dispatch are all optional — see Production and the sections below.
The point is not to bypass token costs. It is hierarchical context management: the manager agent receives compact summaries and artifact references, and reads full logs / diffs / outputs only when it explicitly needs to — everything large lives in shared memory (see context virtualization).
┌──────────────────────────────────────────────┐
Claude Code │ Agent Router MCP (Machine A) │
/ manager ───▶│ classify → privacy/redact → eligible → │
agent │ score → select → reserve → dispatch → store │
│ (task DB · shared memory · quota · policy) │
└───────────────┬───────────────┬──────────────┘
│ │
secret_ref│resolved │ HTTP /status /generate …
at call │ time ▼
┌───────────────▼──┐ ┌────────────────────────────────┐
│ Secrets Manager │ │ Local Model Manager (Machine B)│
│ (1Password/Vault) │ │ residency · admission · GPU │
└───────────────────┘ │ vLLM / llama.cpp / Ollama │
└────────────────────────────────┘
Router owns decisions. Model Manager owns local execution. Secrets Manager owns credentials. Agents own task work. Shared Memory owns state and artifacts. Claude owns high-level management and selective inspection.
Concretely: the inference machine never becomes the global policy engine, agents never make infrastructure decisions, and secrets never enter model-visible context.
The Agent Router and the Local Model Manager are different processes with different jobs, and they usually run on different machines:
| Service | Job | Typically runs on |
|---|---|---|
| Agent Router (control plane) | Makes all routing decisions — classify → privacy/redact → eligible providers → score → select → reserve quota/budget | the manager box, next to Claude Code |
| Local Model Manager (inference appliance) | Executes generations; manages GPU residency/admission | your GPU / inference box |
All routing logic lives in the Router, never on the inference box — the inference box is a trusted-but-dumb executor. There is one deliberate split:
- Global routing (which provider/model/tier, given privacy, budget, quota, capability) is decided entirely by the Router.
- Local admission (can this GPU node load/serve that model right now) is
decided by the Model Manager and surfaced via its
/status+can_accept; the Router reads that live status but the decision is still the Router's.
So the policy engine, task DB, shared memory, privacy classification, budgets, and quotas all live with the Router. If the link to the inference box drops, routing still happens — it just reports "no local node" and can fall back to cloud (if allowed) or queue the work.
The routing-stack packages (see Packages above for the full set):
config/ # policy & registry (edit these, not code)
providers.yaml # §6 provider & node registry (local nodes = mTLS, no secret)
profiles.yaml # §17 capability profiles + agent defaults
routing.yaml # §9,§12,§13,§16 privacy tiers, scoring, residency, rental
secrets.example.yaml # §7 CLOUD secret_ref → resolver (local nodes carry none)
packages/
agentconnect-core/ # shared, framework-free (pydantic + pyyaml + httpx only)
…/common/{schemas,state,memory,quota,privacy,providers,secrets,config,tokens,workqueue}.py
agentconnect-router/ # the PRIMARY product — Agent Router MCP control plane
…/router/{routing,gateway,service,mcp_server,local_client,provisioning}.py
agentconnect-model-manager/# optional satellite — local inference appliance
…/model_manager/{residency,backends,app,tls}.py
agentconnect-runtime/ # worker runtime — LangGraph act/tool execution loop
…/runtime/{agent,graph,tools,workspace,prompts,state,results,transport}.py
tests/ # unit + e2e tests, run offline (stub backend + mTLS)
examples/demo.py # end-to-end router walkthrough, no GPU required
examples/federation_demo.py # federated work queue: friend contributes compute, privacy enforced
docs/ARCHITECTURE.md # detailed design notes + section map
docs/WORK_QUEUE.md # federated pull-based work queue design
docs/REMOTE_DISPATCH.md # router-driven remote-worker push dispatch
docs/MULTI_HARNESS.md # interchangeable manager harnesses over one control plane
For a real deployment you split the Router (Machine A, decisions) from the Local Model Manager (Machine B, a GPU inference appliance). They authenticate by client certificate — no bearer token or shared secret crosses the wire:
# Machine B — Local Model Manager (serves HTTPS + requires a client cert)
export MODEL_MANAGER_TLS_CERT=/certs/server.crt
export MODEL_MANAGER_TLS_KEY=/certs/server.key
export MODEL_MANAGER_TLS_CA=/certs/ca.crt # trust anchor for router client certs
export MODEL_MANAGER_ALLOWED_CLIENTS=agentconnect-router-01 # optional identity allowlist
export MODEL_MANAGER_BACKEND=openai # front a real vLLM/llama.cpp/Ollama server
export MODEL_BACKEND_URL=http://localhost:8000/v1
agentconnect-model-manager # serves https://0.0.0.0:8443 (mTLS)
# Machine A — Agent Router MCP (stdio transport for Claude Code)
export MODEL_MANAGER_URL=https://machine-b:8443
export AGENTCONNECT_LOCAL_CA=/certs/ca.crt
export AGENTCONNECT_LOCAL_CLIENT_CERT=/certs/router.crt
export AGENTCONNECT_LOCAL_CLIENT_KEY=/certs/router.key
agentconnect-routerThe Router also runs cloud-only, without the Model Manager at all — install
just agentconnect-core + agentconnect-router; local-only tasks then report
"no local node" and everything else routes to cloud providers.
One control-plane instance can be driven by many interchangeable manager harnesses
(Claude Code, Codex, Cursor, opencode…) over the same subagents — swap harness even
mid-task and work keeps flowing, because subagents drain a shared work queue and don't
care who submitted. Two shapes: Model A (a stdio router per harness, all pointed at
the same AGENTCONNECT_DB — zero code, works today) and Model B (one shared
SSE/streamable-HTTP server every harness connects to — coherent single instance, remote
harnesses):
# Model B — one shared HTTP instance
AGENTCONNECT_MCP_TRANSPORT=streamable-http AGENTCONNECT_MCP_PORT=8760 agentconnect-router
# harnesses connect to http://127.0.0.1:8760/mcp (bind loopback; add TLS/auth before exposing)See docs/MULTI_HARNESS.md for setup, config snippets, and the A-vs-B tradeoffs.
| Tool | Returns |
|---|---|
submit_task(task, agent_type, profile, privacy_class, …) |
compact TaskSummary + artifact refs |
queue_add(task, privacy_class, …) |
ticket + status |
queue_next(worker_id, capabilities) |
list of claimable tickets |
queue_claim(worker_id, ticket_id) |
claimed ticket or error |
queue_update(worker_id, ticket_id, lease_token, extend_seconds) |
renewed lease, or refusal if the holder/token doesn't match or the lease expired |
queue_report(worker_id, ticket_id, lease_token, result) |
ticket/result status |
queue_link(ticket_id, depends_on) |
dependency edge (privacy monotonicity enforced) |
queue_status(ticket_id, …) |
ticket metadata + audit trail |
queue_approve(reviewer_id, ticket_id) |
in_review ticket promoted to done (local_only reviewer only) |
queue_reject(reviewer_id, ticket_id, reason) |
ticket requeued while attempts remain, else failed (local_only reviewer only) |
queue_list(status, privacy_class, …) |
payload-free ticket listing (operator view) |
queue_pending(limit) |
in_review backlog (operator view) |
queue_stats() |
queue counts + capability requirements (operator view) |
get_task_status(task_id) |
status summary |
get_task_artifacts(task_id) |
{kind: artifact_id} |
read_artifact_chunk(artifact_id, offset, max_chars) |
bounded chunk + next_offset |
get_log_slice(task_id, level, query, max_lines) |
bounded log slice |
search_memory(query, scope, limit) |
snippets (never full bodies) |
get_router_status() / get_provider_status() |
policy + live provider/quota state |
get_provider_scorecards() |
learned per-provider quality/latency (Phase 6) |
set_budget(amount_usd, period) / get_budget_status() |
global spend budget + pacing |
promote_task(task_id) / cancel_task(task_id) |
control |
enqueue_task(task, privacy_class, …) |
ticket: classify/redact + queue in one call, routing decision recorded as an advisory assignee hint |
Workers write full output (patches, logs, traces) to shared memory and return
only status + summary + refs + risks + next action. The router's default output
policy (config/routing.yaml) caps MCP payloads and forbids returning full logs,
full repo files, or full traces inline — the manager pulls detail on demand with
read_artifact_chunk / get_log_slice.
This does not make a task use fewer tokens — it keeps tokens out of the expensive place (your manager agent's context) and routes the worker's tokens to the cheapest capable provider. Two separate effects:
- Manager side (the Claude context you pay API rates for) — the real saving.
Delegating via
submit_taskreturns a compactTaskSummary(status, one-line summary, artifact refs, risks, next-action), not the full output or tool transcript. Claude pulls detail only on demand. A big multi-file task that would otherwise balloon Claude's window to tens of thousands of tokens (re-sent every turn) becomes a submit + a few-hundred-token summary. The saving scales with how bulky the delegated work is. - Worker side (the model doing the work) — cost, not token count. The task's own tokens don't shrink; they're routed to the cheapest capable tier — local GPU first (effectively free once the box is up), then free cloud, then paid — under quota + budget guards so paid tokens are never spent silently.
Honest caveats: the MCP tool schemas add a fixed per-session context overhead (order of a few thousand tokens); each delegation still costs its summary + any follow-up chunk reads; and for small tasks, delegating can cost more manager tokens than doing the work inline. The win is for large, context-heavy, or repetitive work — exactly where an inline agent's context explodes. A flat Claude subscription mostly hides the manager-side cost; without one, that manager-side compaction is where this design pays for itself.
Beyond routing, AgentConnect offers a pull-based work queue where separate agents and untrusted external compute can discover and claim work — all while respecting the trust × privacy boundary. Workers pull tickets for the privacy class they are authorized for; results from untrusted workers land in a review gate before becoming truth.
| Tier | Who | Can claim | Auto-trusted |
|---|---|---|---|
local_only |
your box | all classes | yes — auto-approve results |
private_rented |
rented GPU, your model | public, low_sensitive | no — results require review |
external |
free cloud API | public, low_sensitive (redacted) | no — results require review |
external_paid |
paid cloud API | public, low_sensitive (redacted) | no — results require review |
- Atomicity & fencing: One shared SQLite store; claim is a single guarded
UPDATE. Leases expire; the reaper requeues, and fresh tokens prevent stale workers from submitting results. - Result verification: Results from untrusted tiers land
in_reviewuntil alocal_onlyreviewer approves. - Dependencies: Tickets can depend on other tickets. A child is claimable only when all parents are
done. Privacy monotonicity is enforced: a lower-class ticket cannot depend on a higher-class parent (prevents laundering). - Idempotency: Deduplicated by key; terminal states (done, failed) are never reopened. Already-reported tickets are refused.
See docs/WORK_QUEUE.md for the full design, MCP API, and examples.
A rented GPU running your open-weights model is a distinct tier — private inference for very large models without owning the hardware. It plugs in as just another Model Manager node, reached over the same mTLS transport:
| Tier | Hardware | Model | Privacy tier | Cost |
|---|---|---|---|---|
| Local node | your box | small/mid, yours | local_only |
free (owned) |
| Rented node | rented GPU | very large, yours | private_rented |
hourly rent + spin-up |
| Cloud API | provider's | their model | external / external_paid |
per-token |
Renting has two credential planes: inference traffic is mTLS (no secret); the
rental vendor's control-plane API key is the only secret and lives in the secrets
manager, used solely to rent/terminate the box. A repo_sensitive task may run on
a rented node only with explicit opt-in (allow_rented) and when the node
meets its trust policy (ephemeral, encrypted, your image, no external logging).
execution="agentic" runs the worker runtime's act/tool loop in-process, feeding
each tool observation back to the model. Those observations must never reach an
untrusted external model, so agentic runs only on (a) an owned-local resident
model, or (b) a trusted, opted-in rented private node — a box running your
weights ephemerally with no external logging (exactly the rented tier's purpose).
Cloud (external/external_paid) agentic is always rejected; secret_sensitive
never routes at all. The guard fails closed even if routing selected rented for a
public task without allow_rented or without a trust-satisfying node.
On the rented tier the loop dispatches every step through the rented path, not the
gateway: the node is acquired once before the loop, reused across all steps,
its rental window billed exactly once at spin-up, and it is released (for
the idle reaper) in a finally. Token usage is summed across steps for the
evaluation record only — the single window bill is the whole money path (a rented
node is type="local", so no cloud quota is reserved or reconciled).
Instead of running the agentic loop in-process, the router can PUSH the whole task
to a registered remote worker over mutual TLS (HttpAgentRuntime → POST /run),
automatically preferring any worker whose attested tier is trusted for the task's
privacy class (the same fail-closed WorkQueue.may_claim predicate the pull queue
uses) and that reports capacity — else it falls back to in-process. The worker runs
its own model and self-reports token usage; there is no router-side spend. Register
workers in config/remote_workers.yaml (ships empty → feature off). See
docs/REMOTE_DISPATCH.md.
Set one number and a period — set_budget(amount_usd, "daily"|"weekly"|"monthly") —
and the router paces spend against it: as cumulative spend runs ahead of the
even-burn line (or nears the cap) it steers toward free/local via a scoring penalty,
and it hard-blocks paid/rented when the period budget is exhausted. Spend is metered
across all real money (paid cloud + rented GPU) from the one quota_records ledger.
Two deliberate safety properties:
- Mandatory, no silent default. There is no default amount. Until the user explicitly sets a budget, paid cloud and rented GPU are ineligible — the system still runs fully on free-tier/owned-local, but real money is off until asked for.
- Deterministic human gate on every charge. Money never depends on the stochastic
agent. The router calls a
SpendAuthorizerdirectly:request_budgetprompts the user to set a budget when none exists, andconfirm_chargeasks the user to approve each paid/rented charge before it happens. The default isDeny(fail-closed); wireCallbackSpendAuthorizerto your app's native confirmation UI, or useConsole/AutoApprovefor CLI/trusted automation. This bounds the "stochastic blast zone": the agent can propose work, but the user approves the spend.
Don't want to build a confirmation channel? Turn on the reference one and you get a browser approvals page plus (optionally) phone push. There are three levels — pick one.
Level 1 — browser only (local machine):
pip install "agentconnect-router[web]"
export AGENTCONNECT_SPEND_AUTHORIZER=web
agentconnect-routerOpen http://127.0.0.1:8770/. When the agent tries a paid/rented task, the call blocks,
a SPEND APPROVAL NEEDED … line is logged with a link, and the page shows Approve /
Deny buttons (or, if no budget is set yet, an amount box). Click it and the task
proceeds. No response within 5 minutes = denied.
Level 2 — phone push with one-tap approve (recommended): ntfy. Three steps:
-
Install the free ntfy app (iOS/Android) and subscribe to a topic name you choose, e.g.
agentconnect-yourname(any hard-to-guess string). -
Make the approval server reachable from your phone. On a laptop the quickest way is a tunnel, e.g.
cloudflared tunnel --url http://localhost:8770— copy the publichttps://…URL it prints. -
Run the router pointed at both:
pip install "agentconnect-router[web]" export AGENTCONNECT_SPEND_AUTHORIZER=web export AGENTCONNECT_APPROVAL_URL=https://YOUR-TUNNEL.trycloudflare.com # from step 2 export AGENTCONNECT_NOTIFY=ntfy export AGENTCONNECT_NTFY_URL=https://ntfy.sh/agentconnect-yourname # your topic agentconnect-router
Now every paid/rented charge pushes a notification to your phone with Approve and Deny buttons — tapping one POSTs straight to the server. (One-tap works because the ntfy app makes the call; that's why the server must be reachable at
AGENTCONNECT_APPROVAL_URL.)
Level 3 — Slack / Discord. Set AGENTCONNECT_NOTIFY=slack (or discord) with
AGENTCONNECT_SLACK_WEBHOOK / AGENTCONNECT_DISCORD_WEBHOOK. You get a rich message with
a one-tap link to a per-item page where you confirm (incoming webhooks can't do true
in-message buttons — only ntfy can). Combine channels with a comma:
AGENTCONNECT_NOTIFY=ntfy,slack.
All environment variables (mode is set by AGENTCONNECT_SPEND_AUTHORIZER):
| Variable | Default | Meaning |
|---|---|---|
AGENTCONNECT_SPEND_AUTHORIZER |
deny |
deny (fail-closed) · web · console · auto (trusted/tests) |
AGENTCONNECT_APPROVAL_HOST / _PORT |
127.0.0.1 / 8770 |
where the approvals server binds |
AGENTCONNECT_APPROVAL_URL |
http://host:port |
public base URL used in notifications (set to your tunnel) |
AGENTCONNECT_APPROVAL_TOKEN |
(none) | optional bearer token required on /api/* |
AGENTCONNECT_APPROVAL_TIMEOUT |
300 |
seconds to wait before failing closed (deny) |
AGENTCONNECT_NOTIFY |
(none) | comma list: ntfy · slack · discord · webhook |
AGENTCONNECT_NTFY_URL |
— | your ntfy topic URL, e.g. https://ntfy.sh/… |
AGENTCONNECT_SLACK_WEBHOOK / _DISCORD_WEBHOOK |
— | incoming-webhook URLs |
AGENTCONNECT_APPROVAL_WEBHOOK |
— | raw JSON POST target (for webhook mode) |
Security: the approvals endpoint controls money. It binds loopback by default; before
exposing it (e.g. via a tunnel) set AGENTCONNECT_APPROVAL_TOKEN and keep it over HTTPS.
Without the [web] extra installed, web mode logs a warning and falls back to deny.
- Tasks are classified into
public / low_sensitive / repo_sensitive / secret_sensitive / restricted(§13). A redaction pass (§14) scrubs keys/JWTs/DB-URLs/PII before any external call. secret_sensitivecontent is blocked from every LLM/node — the router rejects it before routing.- Local + rented inference nodes carry no secret at all — they authenticate via mutual TLS (identity = client certificate). Only the third-party secrets manager holds secrets, and only cloud API keys + rental control-plane keys.
- Cloud provider configs hold secret references only. The gateway is the sole component that resolves a cloud secret, at call time, and never returns/logs it.
A global spend budget with even-burn pacing and a direct-to-user spend authorizer (mandatory budget, per-charge confirmation — money never rides on the agent) sit on top of all six phases.
All six phases implemented end-to-end (offline, tested): the
deterministic router (Phases 1 & 5), shared memory + context virtualization
(Phase 2), residency + real concurrency admission (Phase 3), the provider
gateway + secrets + quota ledger + privacy/redaction (Phase 4), and
evaluation + learning (Phase 6): outcomes are recorded per provider and a
bounded learned-quality signal tilts future routing. Plus the cross-cutting work:
mutual-TLS inter-service transport, the three-package split (Router
installs without the Manager), the rented-GPU node tier with a RunPod vendor
adapter, warm-node reuse + idle reaping, and a real OpenAI-compatible inference
backend (vLLM / llama.cpp / Ollama). New in this phase: agentic execution on
trusted rented private nodes (opt-in, acquire-once-bill-once), capability matching
(ticket-to-worker filter, not a gate), and broker-side operator view (queue
visibility + in_review backlog with approve/reject web host). Cloud calls and the
local backend degrade to deterministic stubs until real endpoints/credentials are
supplied. CI runs the suite on 3.10–3.12 and verifies the Router builds standalone.
See docs/ARCHITECTURE.md for the section-by-section map.
Apache-2.0. See LICENSE and NOTICE; every package under
packages/ ships the same license text in its wheel.