Documentation

Operators & self-hosting

Run your own AgentMesh: configuration, deployment, catalog, pricing, payments and recovery.

Hosted vs. your own instance

Hosted AgentMeshYour own instance
Dashboardwww.agentmeshx.techWherever you deploy client/ (for example Vercel)
APIhttps://agentmesh-riuz.onrender.com/api/v1Your host, e.g. https://api.example.com/api/v1
OperatorsRun by the AgentMesh maintainersYou
Accounts, nodes, moneyShared hosted networkCompletely separate; nothing is shared with the hosted network

The operator dashboard (/operator) works the same on both. The rest of this page is for people running their own instance from the source repository.

Configuration

The API reads environment variables (or a .env file in the repository root). Anything other than ENVIRONMENT=dev|development|test|testing, including leaving it unset, counts as production, and then the API refuses to start unless:

  • SECRET_KEY is random and at least 32 characters (openssl rand -hex 32);
  • CORS_ORIGINS lists explicit origins (JSON array or comma-separated; no *);
  • PUBLIC_BASE_URL uses https://;
  • PAYSTACK_SECRET_KEY, if set, is a test key, unless PAYSTACK_ALLOW_LIVE=true.
VariablePurpose
MONGODB_URL, MONGODB_DBMongoDB connection string (for example from MongoDB Atlas) and database name (default agentmesh). It must be a replica set, which Atlas always is: ledger postings are saved in multi-document transactions. Required outside development.
SECRET_KEYRoot of the purpose-specific keys (session tokens, MFA challenges, sponsored-view tokens, command signing unless set separately, MFA-secret encryption unless set separately). Changing it signs everyone out and rotates the command-signing key; see docs/security/INCIDENT_RESPONSE.md before rotating.
FORWARDED_ALLOW_IPSProxy addresses (IPs or CIDRs) whose X-Forwarded-For is trusted for client addresses. Set it to your platform’s proxy range; never * when the API port is reachable directly.
STAFF_MFA_REQUIRED, DATA_ENCRYPTION_KEYOperators and admins must use TOTP (default on). Optional dedicated base64 32-byte key for MFA secrets at rest.
ALLOW_LEGACY_NODE_CREDENTIALSAccept workers older than node sessions (default on). Turn off once every worker is upgraded.
COMMAND_SIGNING_KEY, COMMAND_SIGNING_PREVIOUS_KEYOptional explicit Ed25519 seeds for signed commands; rotate by moving the current one to PREVIOUS.
AI_RUN_RETENTION_DAYS, USAGE_RECORD_RETENTION_DAYS, AUDIT_LOG_RETENTION_DAYS and othersRetention per data class (see Security & limitations).
PUBLIC_BASE_URL, FRONTEND_URLPublic https URLs of the API and the dashboard. Paystack returns customers to $FRONTEND_URL/customer/wallet.
CORS_ORIGINSDashboard origins allowed to call the API.
COORDINATION_BACKEND=redis, REDIS_URLNeeded only for more than one API replica. Use a password-protected Redis that is not reachable from the internet: replicas trust its messages.
BILLING_CURRENCYOne platform currency (default NGN). Choose it before taking payments.
PAYSTACK_SECRET_KEY, PAYSTACK_WEBHOOK_IPSPayments; empty key disables them.
METRICS_TOKENBearer token for GET /metrics; empty disables it.
NEXT_PUBLIC_API_URLDashboard build setting: your API’s /api/v1 URL. Baked in at build time.

.env.example lists the minimum set. Billing intervals, payment limits and rate limits have sensible defaults; see server/app/core/config.py for every setting.

Deploy

Either run the whole stack with Docker Compose:

git clone https://github.com/meshach5667/AgentMesh.git && cd AgentMesh
export SECRET_KEY=$(openssl rand -hex 32)
export PUBLIC_BASE_URL=https://api.example.com FRONTEND_URL=https://app.example.com
docker compose up -d

or deploy the parts separately: the API from Dockerfile.server (or uvicorn app.main:app in server/) on any host with MongoDB access, and the dashboard from client/ on Vercel with NEXT_PUBLIC_API_URL set. Put the API behind TLS, run uvicorn with --proxy-headers, and restrict --forwarded-allow-ips to your proxy. The Compose stack's MongoDB is reachable only from the API container.

Database

AgentMesh stores everything in MongoDB. When the API starts, it creates any missing collections and indexes and adds the built-in model catalog entries that are missing; it never changes or removes existing data, so several replicas can start at once. There are no migrations to run. For local development, run mongod --replSet rs0 once with rs.initiate(), or use docker compose up -d mongo.

First operator

Operators cannot self-register. On a machine with the same MONGODB_URL as the API (for example your host's shell):

cd server
python scripts/create_operator.py ops@example.com              # prompts for a password (12+ characters)
python scripts/create_operator.py lead@example.com --role admin

Running it for an existing account promotes that account. Each use is recorded in the audit log as user.operator_provisioned. Operator accounts have no multi-factor authentication yet; protect them carefully.

Model catalog

Under Model Catalog, approve models and enable or disable their artifacts (one per runtime). New catalog entries start unapproved. Adding an artifact, which needs a pinned source (an Ollama tag, or a Hugging Face commit and, for llama.cpp, a file SHA-256), is done with POST /models/{id}/artifacts; there is no dashboard form for it yet.

Pricing

Under Pricing, publish an immutable price version, effective now or later: CPU core-hour, memory GB-hour, GPU-memory GB-hour, input and output tokens per million, and the platform commission. Until a price list exists, usage is measured but not charged. You can set token prices to zero to charge for resources only.

The commission is the share of each charge the platform keeps; the provider whose node served the deployment is credited the rest. A version published without a rate gets DEFAULT_PLATFORM_FEE_RATE (10%), and no version may exceed MAX_PLATFORM_FEE_RATE (50%). To change only the commission, use Change commission (or POST /admin/commission): it publishes a version with the prices in force. Every charge keeps the rate it was split at, so past earnings never change. The fee is rounded to the ledger's precision and the provider gets exactly the rest, so commission plus provider share always equals what the customer paid. Only READY time and successful requests are charged.

Payments (Paystack)

  1. In the Paystack dashboard (test mode), copy the secret key into PAYSTACK_SECRET_KEY and set the webhook URL to https://<your-api>/api/v1/payments/paystack/webhook.
  2. Check the integration against Paystack's real test API: PAYSTACK_SECRET_KEY=sk_test_... python scripts/paystack_smoke.py (add --pay to pay with a test card).
  3. Make a top-up from the dashboard and confirm that the webhook arrives: Reconciliation should show no open top-ups, and Ledger should show the deposit.
  4. Payouts use Paystack Transfers: they need a funded balance and transfer OTP turned off, or they stop at OTP_REQUIRED.

Top-ups are credited once, only after Paystack reports success with a matching amount and currency. Payouts are held at request time and need operator approval (Payouts). Reconciliation runs every 10 minutes when payments are enabled.

Revenue analytics

Revenue charts, per day for the last 7, 30 or 90 days, what customers paid, split into the provider share and the commission, together with refunds, top-ups, payouts and card refunds. Platform revenue is the commission net of reversed fees, plus sponsorship revenue, which is shown as a separate figure. Every number is computed by the server from rated charges, refunds and payments; nothing is estimated. The same data is available at GET /analytics/revenue?scope=network&days=30.

Refunds

  • A compute charge refund (full or partial) credits the customer's wallet. The provider share and the commission are reversed in the same proportions as the original split (REFUND_CLAWBACK and PLATFORM_FEE_REVERSAL ledger entries). A provider who was already paid out can go negative; later earnings cover it.
  • A top-up refund returns unspent wallet funds to the customer's card through Paystack. The amount is held at once; the refund settles when Paystack reports it processed (webhook or reconciliation) and the hold is released if it fails. Only one card refund per top-up can be open at a time.
  • Each refund needs a reason, which the customer sees. Refunds are audited and checked by reconciliation; a charge can never be refunded beyond what was paid.

Sponsorships

Sponsored placements are optional and off by default. Under Sponsorships, turn advertising on, choose which placements show items (landing page, docs, customer and provider dashboards, 1 to 3 items each) and manage campaigns for AI tools, GPU hardware and infrastructure providers. Campaigns start as drafts; drafts can be activated, active campaigns paused and resumed, and any campaign ended (final). A campaign is served only while active, inside its schedule and under its budget.

Items are labelled “Sponsored”, link only to the campaign's stored https URL with rel="sponsored", and can be dismissed by viewers. Impressions count once per signed view token when at least half the item is visible; clicks count once per view. No personal data is used to select items, and no third-party ad scripts are loaded. Pricing is CPM, CPC or a flat fee; earnings are reported on the Revenue page separately from compute commission.

Monitoring

EndpointUse
GET /healthLiveness.
GET /health/readyDatabase (and Redis) readiness; 503 if either fails. Use for load balancers.
GET /metricsPrometheus, with Authorization: Bearer $METRICS_TOKEN.
GET /api/v1/admin/overviewOperator dashboard figures, including nodes with overdue heartbeats.
GET /api/v1/admin/reconciliationLedger invariants and open payment items.
GET /api/v1/admin/anomaliesProviders whose token claims exceed the request and response sizes the gateway observed, or whose requests mostly fail.
POST /api/v1/admin/nodes/{id}/quarantineCut a node off at once (no authentication, tunnel closed, deployments moved); release with /release. See docs/security/INCIDENT_RESPONSE.md.

Logs are JSON lines with a request id; API keys, node credentials, bearer tokens and Paystack keys are redacted, and security events (session refusals, closed tunnels, rejected commands) are logged with an allowlist of fields under agentmesh.security. Alert on readiness failing, rising 5xx, payouts stuck PENDING or OTP_REQUIRED, reconciliation reporting ok: false, and Background job … failed in the logs.

Backups and recovery

cd server
MONGODB_URL=mongodb+srv://… KEEP=14 ops/backup_db.sh /var/backups/agentmesh
MONGODB_URL=mongodb+srv://… MONGODB_DB=agentmesh_restore ops/restore_db.sh /var/backups/agentmesh/agentmesh-<ts>.archive.gz
  • Backups are compressed mongodump archives (mode 0600). On Atlas, also enable its scheduled backups. Store copies off the host, encrypted, and test a restore monthly. The restore script refuses a non-empty database.
  • After a restore: point MONGODB_DB at it, start the API, then run reconciliation to re-check open top-ups and payouts with Paystack.
  • API restarts lose nothing: workers reconnect and billing resumes from each deployment’s watermark without double charging.
  • Never refund a pending payout by hand; the transfer may still complete. Refund customers from Refunds, which reverses the provider share and commission through the ledger.
  • Incident runbooks (compromised nodes, leaked credentials, key rotation, rollback) are in docs/security/INCIDENT_RESPONSE.md.

Publishing worker releases

  1. Set WORKER_VERSION in server/node_agent/config.py, and WORKER_VERSION in client/src/lib/config.ts to match (a test checks this).
  2. Commit, then tag and push: git tag worker-v0.3.0 && git push origin worker-v0.3.0.
  3. The Worker release workflow checks the tag matches, builds the wheel, checks it installs, writes SHA256SUMS, and creates the GitHub release with the wheel, install.sh, uninstall.sh and SHA256SUMS.
  4. Redeploy the dashboard so Add a machine points at the new version.

Signed self-updates are a separate, optional channel: sign a release manifest with scripts/sign_worker_release.py using an Ed25519 key kept offline, and point WORKER_RELEASE_DIR at the output. Never add that private key to GitHub Actions or the control plane.