Documentation
Operators & self-hosting
Run your own AgentMesh: configuration, deployment, catalog, pricing, payments and recovery.
Hosted vs. your own instance
| Hosted AgentMesh | Your own instance | |
|---|---|---|
| Dashboard | www.agentmeshx.tech | Wherever you deploy client/ (for example Vercel) |
| API | https://agentmesh-riuz.onrender.com/api/v1 | Your host, e.g. https://api.example.com/api/v1 |
| Operators | Run by the AgentMesh maintainers | You |
| Accounts, nodes, money | Shared hosted network | Completely 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_KEYis random and at least 32 characters (openssl rand -hex 32);CORS_ORIGINSlists explicit origins (JSON array or comma-separated; no*);PUBLIC_BASE_URLuseshttps://;PAYSTACK_SECRET_KEY, if set, is a test key, unlessPAYSTACK_ALLOW_LIVE=true.
| Variable | Purpose |
|---|---|
MONGODB_URL, MONGODB_DB | MongoDB 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_KEY | Root 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_IPS | Proxy 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_KEY | Operators and admins must use TOTP (default on). Optional dedicated base64 32-byte key for MFA secrets at rest. |
ALLOW_LEGACY_NODE_CREDENTIALS | Accept workers older than node sessions (default on). Turn off once every worker is upgraded. |
COMMAND_SIGNING_KEY, COMMAND_SIGNING_PREVIOUS_KEY | Optional 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 others | Retention per data class (see Security & limitations). |
PUBLIC_BASE_URL, FRONTEND_URL | Public https URLs of the API and the dashboard. Paystack returns customers to $FRONTEND_URL/customer/wallet. |
CORS_ORIGINS | Dashboard origins allowed to call the API. |
COORDINATION_BACKEND=redis, REDIS_URL | Needed only for more than one API replica. Use a password-protected Redis that is not reachable from the internet: replicas trust its messages. |
BILLING_CURRENCY | One platform currency (default NGN). Choose it before taking payments. |
PAYSTACK_SECRET_KEY, PAYSTACK_WEBHOOK_IPS | Payments; empty key disables them. |
METRICS_TOKEN | Bearer token for GET /metrics; empty disables it. |
NEXT_PUBLIC_API_URL | Dashboard 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 -dor 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 adminRunning 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)
Test mode only
- In the Paystack dashboard (test mode), copy the secret key into
PAYSTACK_SECRET_KEYand set the webhook URL tohttps://<your-api>/api/v1/payments/paystack/webhook. - Check the integration against Paystack's real test API:
PAYSTACK_SECRET_KEY=sk_test_... python scripts/paystack_smoke.py(add--payto pay with a test card). - 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.
- 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_CLAWBACKandPLATFORM_FEE_REVERSALledger 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
| Endpoint | Use |
|---|---|
GET /health | Liveness. |
GET /health/ready | Database (and Redis) readiness; 503 if either fails. Use for load balancers. |
GET /metrics | Prometheus, with Authorization: Bearer $METRICS_TOKEN. |
GET /api/v1/admin/overview | Operator dashboard figures, including nodes with overdue heartbeats. |
GET /api/v1/admin/reconciliation | Ledger invariants and open payment items. |
GET /api/v1/admin/anomalies | Providers whose token claims exceed the request and response sizes the gateway observed, or whose requests mostly fail. |
POST /api/v1/admin/nodes/{id}/quarantine | Cut 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
- Set
WORKER_VERSIONinserver/node_agent/config.py, andWORKER_VERSIONinclient/src/lib/config.tsto match (a test checks this). - Commit, then tag and push:
git tag worker-v0.3.0 && git push origin worker-v0.3.0. - The
Worker releaseworkflow checks the tag matches, builds the wheel, checks it installs, writesSHA256SUMS, and creates the GitHub release with the wheel,install.sh,uninstall.shandSHA256SUMS. - Redeploy the dashboard so Add a machine points at the new version.
The repository must be public
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.