A2A Agent Acquisition Architecture — Autonomous Mesh of OSINT Scouts, Deterministic Gatekeepers, and Decentralized Revenue Settlement
⚠️ DISCLAIMER: This repository is a white-label multi-agent infrastructure framework designed for academic research, data collection engineering, and sovereign machine-to-machine telemetry testing. It contains no pre-packaged alpha generation strategies, deployment parameters, or quantitative trade indicators. Users must explicitly code their own algorithmic modules. Use at your own risk.
- Philosophy: Separation of Powers
- Architecture Overview
- System Components
- Quick Start
- Environment Variables
- JSON Schemas
- API Reference
- Revenue Model
- Security & Authentication
- Telegram Integration
- Monitoring & Logging
- Troubleshooting Runbook
- License
The Triad Core Framework is built on a strict Separation of Powers architecture — the single most important design decision in the system.
┌─────────────────────────────────────────────────────────────┐
│ SEPARATION OF POWERS │
├────────────────────────┬────────────────────────────────────┤
│ SCOUT AGENT │ GATEKEEPER AGENT │
│ (Secondary Node) │ (Primary Node) │
├────────────────────────┼────────────────────────────────────┤
│ Unstructured heuristics│ Deterministic mathematics │
│ LLM text parsing │ No LLM inference │
│ Probabilistic signals │ Hard numerical validation │
│ Web scraping / OSINT │ Revenue settlement │
│ Creative data sources │ Exclusive vault control │
│ │ Mesh authentication │
├────────────────────────┴────────────────────────────────────┤
│ LAN Mesh (HTTP/JSON) │
│ scout:8877 → POST telemetry → gatekeeper:8877 │
└─────────────────────────────────────────────────────────────┘
Why this matters:
The Scout Agent can hallucinate, speculate, and scrape creatively — that's its job. But the Gatekeeper Agent never interprets natural language, never runs an LLM, and never makes probabilistic judgments. It receives only mathematically validated numerical vectors and enforces rigid statistical hurdles before any data enters the revenue pipeline.
This means a broken or compromised scout cannot corrupt the gatekeeper's decision engine or divert revenue. The vault address is loaded dynamically from the USER_METAMASK_ADDRESS environment variable — no hardcoded strings, no runtime overrides. Every deployer defines their own.
┌──────────────────────────────────┐
│ EXTERNAL WORLD │
│ (Buying Agents / Web3 Wallets) │
└──────────────┬───────────────────┘
│
Paying agents │ POL/USDC → vault
GET /get-alpha │ (from .env config)
│
┌──────────────▼───────────────────┐
│ GATEKEEPER AGENT │
│ Flask IPC — 0.0.0.0:8877 │
│ │
│ ┌───────────────────────────┐ │
│ │ Mathematical Hurdles │ │
│ │ • Z-score bounds [-4, 4] │ │
│ │ • Finite real check │ │
│ │ • Confidence [0, 1] clamp │ │
│ │ • Composite alpha score │ │
│ └───────────────────────────┘ │
│ │
│ ┌───────────────────────────┐ │
│ │ Revenue Engine │ │
│ │ • Micro-fee: 0.001 POL │ │
│ │ • Vault from env → locked │ │
│ └───────────────────────────┘ │
└──────────────┬───────────────────┘
│
LAN Mesh (Wi-Fi) │ HTTP POST
│
┌──────────────▼───────────────────┐
│ SCOUT AGENT │
│ Python stdlib — no Flask │
│ │
│ Sources: │
│ • SEC EDGAR 8-K filings │
│ • IR transcripts │
│ • Threat feeds (OSINT) │
│ • Geopolitical signals │
│ │
│ Output: minified JSON telemetry │
└──────────────────────────────────┘
│
┌──────────────▼───────────────────┐
│ REDIS MESSAGE BROKER │
│ Optional — stream buffering │
└──────────────────────────────────┘
Location: scout_agent/scout.py
Runtime: Python 3.11 (stdlib only — no Flask, no web3)
Node: Secondary Ubuntu laptop
The Scout Agent performs unstructured web scraping and OSINT gathering from configurable sources. It:
- Scrapes SEC EDGAR for 8-K filings, IR transcripts, and regulatory disclosures
- Computes Z-score deltas from raw numerical data
- Packages findings into standardised JSON telemetry packets
- POSTs packets to the Gatekeeper over the local LAN mesh
- Sends periodic heartbeats to verify connectivity
Modes:
python scout.py --once— single pass (cron-friendly)python scout.py --daemon— continuous loop with configurable intervalpython scout.py --once --verbose— debug single pass with full output
Design principle: The Scout contains no business logic for revenue, settlement, or authentication. It is a pure data collection and forwarding layer.
Location: gatekeeper_agent/gatekeeper.py
Runtime: Python 3.11 + Flask + Gunicorn
Node: Primary Ubuntu workstation (this machine)
Port: 8877 (bound to 0.0.0.0)
The Gatekeeper Agent is the core of the system. It:
- Listens for scout telemetry on
/alphafeed/scout-stream - Runs every incoming data vector through
MathHurdles— a strict mathematical validation engine - Tracks revenue accrual with the vault address from
USER_METAMASK_ADDRESSas the exclusive withdrawal target - Serves validated alpha data to paying external agents via
/alphafeed/get-alpha - Maintains mesh health state via heartbeat monitoring
- Exposes an A2A discovery manifest at
/.well-known/ai-agents.json
Critical invariant: The vault address is populated from the USER_METAMASK_ADDRESS environment variable at runtime. It is never hardcoded in source. Every user must define their own vault address in .env before starting the Gatekeeper. This prevents supply-chain attacks from redirecting revenue — the operating system's env var scope is your security boundary.
Location: docker-compose.yml (Redis 7 Alpine container)
Port: 6379
Redis provides optional stream buffering between the Scout and Gatekeeper. If the Gatekeeper is temporarily unavailable, scout telemetry is queued in Redis and replayed on reconnection. In dual-node deployments, Redis runs on the primary node only.
Location:
mesh/watchdog.py— monitors secondary node freshnessmesh/heartbeat_client.py— sends periodic heartbeats from secondary node
The watchdog checks that the secondary node has sent a heartbeat within the last 300 seconds (configurable via STALE_THRESHOLD). If a node goes dark, the watchdog logs an alert and exits non-zero for external monitoring systems to pick up.
Run the entire stack on one machine for development and testing:
# 1. Clone the repository
git clone https://github.com/BARRYPMARSHALL/triad-core-framework.git
cd triad-core-framework
# 2. Configure environment
cp .env.example .env
# Edit .env — set USER_METAMASK_ADDRESS to your Polygon wallet address.
# The Gatekeeper will refuse to start without it.
# 3. Launch all services
docker compose up -d
# 4. Verify everything is running
docker compose ps
curl http://localhost:8877/alphafeed/health
# 5. Watch the revenue log
tail -f /tmp/gatekeeper_revenue.log
# 6. View service logs
docker compose logs -f scout
docker compose logs -f gatekeeperDeploy across two physical Ubuntu laptops connected via the same local LAN (Wi-Fi or ethernet). This section walks you through every command necessary.
On both nodes, run one of these to find the LAN IP (typically starts with 192.168.x.x, 10.x.x.x, or 172.16.x.x):
# Option A — modern Linux
ip route show | grep -oP 'src \K[0-9.]+'
# Option B — fallback
hostname -I | awk '{print $1}'
# Option C — legacy systems
ifconfig | grep -E 'inet ' | grep -v '127.0.0.1' | awk '{print $2}'Make a note of both IPs. For this guide:
- Primary (Gatekeeper) node:
192.168.1.75 - Secondary (Scout) node:
192.168.1.100
Replace these with your actual IPs throughout.
# On the primary workstation (192.168.1.75):
# 1. Install system dependencies
sudo apt update && sudo apt install -y python3-pip git redis-server ufw
# 2. Clone the repository
git clone https://github.com/BARRYPMARSHALL/triad-core-framework.git
cd triad-core-framework
# 3. Install Python dependencies
pip install -r gatekeeper_agent/requirements.txt
# 4. Configure UFW firewall — allow only the scout node on port 8877
# Replace <scout_ip> with the actual IP of the secondary laptop
sudo ufw allow from 192.168.1.100 to any port 8877 proto tcp
# Also allow SSH if you manage remotely
# sudo ufw allow ssh
sudo ufw enable
sudo ufw status # verify: 8877 ALLOW FROM 192.168.1.100
# 5. Set your vault address and start the Gatekeeper
cd gatekeeper_agent
USER_METAMASK_ADDRESS="0xYourMetaMaskWalletAddress" \
MESH_KEY="your-strong-mesh-key" \
python3 gatekeeper.py
# 6. (Optional) Start Redis for stream buffering
sudo systemctl start redis-server# On the secondary laptop (192.168.1.100):
# 1. Install Python 3.11
sudo apt update && sudo apt install -y python3 ufw
# 2. Clone the repository
git clone https://github.com/BARRYPMARSHALL/triad-core-framework.git
cd triad-core-framework
# 3. Verify the network socket to the Gatekeeper BEFORE launching
# Replace <gatekeeper_ip> with the primary node's LAN IP
nc -zv 192.168.1.75 8877
# Expected output:
# Connection to 192.168.1.75 port 8877 [tcp/*] succeeded!
# If you see "Connection refused", troubleshoot before proceeding:
# - Is the Gatekeeper running on the primary node?
# - Is UFW allowing traffic from this IP?
# - Are both machines on the same subnet?
# 4. Run the Scout Agent pointing at the Gatekeeper's LAN IP
cd scout_agent
GATEKEEPER_URL=http://192.168.1.75:8877 \
MESH_KEY="your-strong-mesh-key" \
SCOUT_NODE_ID="tigerwolf-laptop" \
python3 scout.py --daemon
# 5. In a separate terminal, start the heartbeat client
cd ../mesh
GATEKEEPER_URL=http://192.168.1.75:8877 \
MESH_KEY="your-strong-mesh-key" \
python3 heartbeat_client.py# From any machine on the LAN:
curl -s http://192.168.1.75:8877/alphafeed/mesh/status \
-H "X-Mesh-Key: your-strong-mesh-key" | python3 -m json.toolExpected output:
{
"mesh_id": "triad-core-alpha-feed",
"nodes": {
"tigerwolf-laptop": {
"last_seen": "2026-06-04T08:55:31",
"ip": "192.168.1.100",
"status": "online"
}
},
"metrics": {
"heartbeats_received": 12,
"scout_packets_ingested": 5,
"scout_errors": 0
},
"revenue": {
"vault": "0xYourMetaMaskWalletAddress",
"total_pol": 0.005
}
}All configuration is done through environment variables. The vault address is loaded dynamically from .env — no hardcoded wallet strings exist in the source code.
| Variable | Default | Description |
|---|---|---|
PORT |
8877 |
Gatekeeper listening port |
MESH_KEY |
triad-mesh-key-2026 |
Shared secret for inter-agent auth |
REDIS_URL |
redis://redis:6379/0 |
Redis connection string |
USER_METAMASK_ADDRESS |
(required — no default) | Required. Your MetaMask vault address on Polygon. All A2A revenue is settled exclusively to this address. Set this in your .env file before starting the Gatekeeper. The Gatekeeper exits immediately at startup if this is missing. |
GATEKEEPER_URL |
http://192.168.1.75:8877 |
Scout → Gatekeeper endpoint |
SCOUT_NODE_ID |
tigerwolf-node |
Scout node identifier |
SCOUT_INTERVAL |
60 |
Scout pass interval in seconds |
HEARTBEAT_INTERVAL |
60 |
Heartbeat interval in seconds |
STALE_THRESHOLD |
300 |
Seconds before a node is considered stale |
TELEGRAM_BOT_TOKEN |
— | Telegram bot token for outbound notifications only |
TELEGRAM_ALLOWED_USERS |
— | Comma-separated Telegram user IDs |
TELEGRAM_GROUP_ALLOWED_CHATS |
— | Comma-separated chat IDs (group mode) |
WEB3_RPC_URL |
https://polygon-rpc.com |
Polygon RPC for on-chain verification |
Important:
USER_METAMASK_ADDRESSis required. The Gatekeeper will exit with a startup error if this variable is not set. There is no fallback address in the source code — every deployer must define their own Polygon wallet before starting the Gatekeeper. Copy.env.example→.env, setUSER_METAMASK_ADDRESS=0xYourMetaMaskWalletAddress, then launch.
Every scout data transmission follows this exact schema:
{
"$schema": "https://schemas.triad-core.io/telemetry-v1.json",
"type": "object",
"required": [
"node_id", "scout_id", "payload_type", "data", "timestamp"
],
"properties": {
"node_id": {
"type": "string",
"description": "Identifies the sending scout node"
},
"scout_id": {
"type": "string",
"description": "Unique per-packet identifier (UUID4 hex)"
},
"payload_type": {
"type": "string",
"enum": [
"zscore_delta",
"market_calc",
"osint_alert",
"threat_signal"
],
"description": "Type of telemetry payload"
},
"data": {
"type": "object",
"required": ["threat", "oracle", "military"],
"properties": {
"threat": {
"type": "number",
"description": "Threat Z-score delta",
"minimum": -4.0,
"maximum": 4.0
},
"oracle": {
"type": "number",
"description": "Oracle prediction delta",
"minimum": -4.0,
"maximum": 4.0
},
"military": {
"type": "number",
"description": "Military intelligence delta",
"minimum": -4.0,
"maximum": 4.0
},
"markets_tracked": {
"type": "integer",
"minimum": 0
},
"edge_pp": {
"type": "number",
"description": "Computed edge in percentage points"
},
"confidence": {
"type": "number",
"minimum": 0.0,
"maximum": 1.0,
"description": "Signal confidence score"
}
}
},
"sequence": {
"type": "integer",
"description": "Monotonic counter for ordering"
},
"timestamp": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 UTC timestamp"
},
"mesh_version": {
"type": "string",
"description": "Protocol version"
}
}
}Example packet:
{
"node_id": "tigerwolf-node",
"scout_id": "sct_0ea0c852d3",
"payload_type": "zscore_delta",
"data": {
"threat": -0.76,
"oracle": 0.13,
"military": -1.55,
"markets_tracked": 7,
"edge_pp": 13.3,
"confidence": 0.72
},
"sequence": 42,
"timestamp": "2026-06-04T08:55:00Z",
"mesh_version": "2.0.0"
}The full manifest is auto-served at http://<gatekeeper>:8877/.well-known/ai-agents.json:
{
"$schema": "https://agents.ai/schemas/agent-manifest-v1.json",
"agent_id": "alpha-feed-gatekeeper",
"name": "AlphaFeed Gatekeeper — Geopolitical OSINT Alpha Feed",
"description": "Deterministic mathematical gatekeeper for OSINT Z-score deltas. No LLM text parsing.",
"endpoints": [
{
"path": "/alphafeed/get-alpha",
"method": "GET",
"description": "Latest validated alpha signal",
"auth": "X-Mesh-Key"
},
{
"path": "/alphafeed/revenue",
"method": "GET",
"description": "Revenue balance and vault status"
}
],
"pricing": {
"model": "per_request",
"amount_pol": 0.001,
"vault": "0xYourMetaMaskWalletAddress",
"vault_locked": true
},
"settlement": {
"network": "polygon",
"vault_address": "0xYourMetaMaskWalletAddress",
"method": "direct_wallet_transfer (no middleman)",
"withdrawal_policy": "exclusive — no alternate addresses accepted"
}
}Note: The vault address shown above is a placeholder. The live manifest served by a running Gatekeeper returns the address set in your
USER_METAMASK_ADDRESSenvironment variable.
{
"node_id": "tigerwolf-node",
"node_name": "tigerwolf-ubuntu",
"ip_address": "192.168.1.100",
"uptime_seconds": 86400.0,
"load_avg": [0.5, 0.3, 0.2],
"memory_pct": 42.1,
"version": "2.0.0"
}| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/alphafeed/health |
None | Liveness check |
GET |
/.well-known/ai-agents.json |
None | A2A discovery manifest |
POST |
/alphafeed/scout-stream |
X-Mesh-Key | Ingest scout telemetry |
POST |
/alphafeed/heartbeat |
X-Mesh-Key | Receive node heartbeat |
GET |
/alphafeed/mesh/status |
X-Mesh-Key | Mesh health dashboard |
GET |
/alphafeed/get-alpha |
X-Mesh-Key | Latest validated alpha signal |
GET |
/alphafeed/revenue |
X-Mesh-Key | Accrued revenue balance |
The Scout Agent exposes no external endpoints. All communication is outbound-only via HTTP POST to the Gatekeeper.
- Cost: 0.001 POL (Polygon native token) per data pull
- Accepted tokens: POL (native), USDC (Polygon)
- Payment: Direct wallet-to-wallet transfer
- Verification: Submit transaction hash to the Gatekeeper's
/payendpoint
The withdrawal address is read exclusively from the USER_METAMASK_ADDRESS environment variable. There is no fallback — the Gatekeeper exits immediately at startup if this variable is unset:
VAULT = os.getenv("USER_METAMASK_ADDRESS")
if not VAULT:
sys.exit(1) # user must set USER_METAMASK_ADDRESS in .envThis is by design. Every deployer must define their own MetaMask address in their .env file before starting the Gatekeeper. There is no built-in default — if you see a startup error, you haven't configured the environment variable yet.
Security invariant: Once set at startup, the vault is locked for the lifetime of the process. No API call, runtime argument, or configuration change can alter the withdrawal target.
External Agent → sends POL/USDC to VAULT (from USER_METAMASK_ADDRESS)
→ submits tx_hash to Gatekeeper
→ Gatekeeper credits agent's API key
→ Agent pulls data via GET /get-alpha
→ Fee deducted per pull
All inter-agent traffic requires the X-Mesh-Key header:
X-Mesh-Key: <shared-secret>
The mesh key is set via the MESH_KEY environment variable. In production, use a strong random string:
python3 -c "import secrets; print(secrets.token_hex(32))"-
The Gatekeeper binds to
0.0.0.0:8877— accessible to any machine on the LAN -
Critical: Restrict access to only known scout nodes via UFW:
# Allow only the specific scout node sudo ufw allow from <scout_ip_address> to any port 8877 proto tcp # Explicitly block external WAN access to port 8877 # (UFW default deny incoming covers this, but being explicit helps auditing) sudo ufw deny 8877 # Enable the firewall sudo ufw enable # Verify the rules sudo ufw status verbose
Expected
ufw statusoutput:To Action From -- ------ ---- 8877/tcp ALLOW 192.168.1.100 8877/tcp DENY Anywhere 8877/tcp (v6) DENY Anywhere (v6) -
The Scout Agent makes outbound connections only — no open ports required
-
Always verify the socket before deploying:
nc -zv <gatekeeper_ip> 8877
Telegram is configured as a read-only outbound notification tier:
- No inbound command processing from Telegram
- Only settlement confirmations and execution artifacts are posted
- Bot token has minimal permissions (send messages only in allowed chats)
The Telegram interface is intentionally restricted to broadcast-only mode. Configuration is handled via environment variables:
# .env
TELEGRAM_BOT_TOKEN=your_bot_token
TELEGRAM_ALLOWED_USERS=user_id_1,user_id_2
TELEGRAM_GROUP_ALLOWED_CHATS=group_chat_idWhat Telegram is used for:
- ✅ Settlement confirmations (POL/USDC received)
- ✅ Execution artifacts requiring a signature
- ❌ Internal mesh chatter
- ❌ Scout telemetry logs
- ❌ Debug output
All operational logging routes to local files:
/tmp/gatekeeper_revenue.log— revenue events/tmp/scout_agent.log— scout telemetry/tmp/local_mesh_migration.log— mesh connection events/tmp/alphafeed_acquisition.log— agent discovery and acquisition/tmp/github_deployment.log— git deployment log
| Log | Path | Content |
|---|---|---|
| Revenue | /tmp/gatekeeper_revenue.log |
Every micro-fee collection and settlement |
| Scout | /tmp/scout_agent.log |
Telemetry ingestion and scrape results |
| Mesh | /tmp/local_mesh_migration.log |
Connection handshakes and heartbeat events |
| Acquisition | /tmp/alphafeed_acquisition.log |
Agent discovery and barter proposals |
| Deployment | /tmp/github_deployment.log |
Git operations and staging output |
The watchdog (mesh/watchdog.py) runs every 2 minutes and:
- Checks if the secondary node has sent a heartbeat in the last 300 seconds
- Silently exits with code 0 on success
- Exits with code 1 if a node is stale (for cron/systemd alerting)
# Run manually
python3 mesh/watchdog.py
echo $? # 0 = healthy, 1 = stale nodeThe Docker Compose deployment includes automatic health checks:
- Gatekeeper: HTTP GET /alphafeed/health every 15 seconds
- Scout: Internal process health via Docker HEALTHCHECK
- Redis: redis-cli ping every 10 seconds
Symptom: nc -zv 192.168.1.75 8877 returns Connection refused from the secondary node.
Resolution steps:
-
Is the Gatekeeper process running?
# On the primary node: ss -tlnp | grep 8877
If nothing appears, start the Gatekeeper:
cd gatekeeper_agent USER_METAMASK_ADDRESS="0xYourMetaMaskWalletAddress" \ MESH_KEY="your-mesh-key" \ python3 gatekeeper.py
-
Is it bound to the right interface?
ss -tlnp | grep 8877 # Expected: LISTEN 0 0.0.0.0:8877 # If it shows 127.0.0.1:8877, the Gatekeeper is localhost-only — # check that it's started with 0.0.0.0 binding.
-
Is UFW blocking the scout?
# On the primary node: sudo ufw status verboseIf you see
8877/tcp DENYwithout an accompanying ALLOW for the scout IP:sudo ufw allow from 192.168.1.100 to any port 8877 proto tcp sudo ufw reload
-
Are both nodes on the same subnet?
# On both nodes: ip route show | grep default # The gateway IP should match. If they're on different subnets # (e.g., 192.168.1.x vs 10.0.0.x), the mesh won't route.
-
Can the primary node ping the secondary?
# From primary: ping -c 3 192.168.1.100If ping fails, check physical connection (Wi-Fi network, ethernet cable).
Symptom: Scout reports POST scout-stream OK but Gatekeeper logs validation errors.
Resolution steps:
-
Check the rejection reason in Gatekeeper logs:
docker compose logs gatekeeper | grep "SCOUT REJECTED" # Or if running bare-metal: tail -20 /tmp/gatekeeper.log | grep "SCOUT REJECTED"
-
Common causes and fixes:
-
Z-score out of bounds: All three vector values (threat, oracle, military) must be in [-4.0, 4.0]. Clamp your values before sending.
# In your scout, add clamping: def clamp(v): return max(-4.0, min(4.0, v))
-
Non-finite values: Python
float('inf'),float('nan'), orNonewill be rejected. Usemath.isfinite()check before building the packet:import math assert all(math.isfinite(v) for v in [threat, oracle, military])
-
Missing required fields: Every packet MUST contain
node_id,scout_id,payload_type,data, andtimestamp. Thedataobject MUST containthreat,oracle, andmilitarykeys. -
Confidence out of range: Must be in [0.0, 1.0]. Clamp before sending.
-
-
Test with a known-valid packet:
curl -s -X POST http://192.168.1.75:8877/alphafeed/scout-stream \ -H "Content-Type: application/json" \ -H "X-Mesh-Key: your-mesh-key" \ -d '{ "node_id": "test", "scout_id": "test_001", "payload_type": "zscore_delta", "data": {"threat": -0.5, "oracle": 0.3, "military": -1.2, "confidence": 0.8, "markets_tracked": 5}, "sequence": 1, "timestamp": "2026-06-04T12:00:00Z", "mesh_version": "2.0.0" }'
If this succeeds, the issue is in your scout's data generation — not the mesh.
Symptom: Gatekeeper returns 400 Invalid JSON body sporadically, or telemetry arrives with mangled fields.
Resolution steps:
-
Is the Wi-Fi link stable? Run a continuous ping test from the scout node:
# On secondary node, run for 60 seconds: ping -c 60 192.168.1.75 | tail -3
If packet loss > 0.5%, the Wi-Fi link is unreliable. Switch to ethernet or move the nodes closer.
-
Enable TCP keepalive on the scout's HTTP client: The urllib default timeout is generous but doesn't guarantee packet integrity over flaky links. Add retry logic to the scout:
# In scout.py post_to_gateway(), wrap with retry: import time MAX_RETRIES = 3 for attempt in range(MAX_RETRIES): try: # ... existing POST code ... return True except urllib.error.HTTPError as e: if attempt < MAX_RETRIES - 1: time.sleep(2 ** attempt) # exponential backoff continue raise
-
Validate JSON before sending: In your scout, catch serialization errors:
try: body = json.dumps(payload).encode("utf-8") except (TypeError, ValueError) as e: logger.error("JSON serialization failed: %s", e) # Inspect payload for non-serializable types return False
-
Reduce packet size for noisy networks: If your payload exceeds ~10 KB, truncate or paginate. Noisy Wi-Fi links drop large frames disproportionately.
Symptom: Scout logs one set of z-scores, but the revenue log or /alphafeed/get-alpha shows different values.
Resolution steps:
-
Check the persisted scout data on the Gatekeeper:
# On the primary node, examine what the Gatekeeper actually stored: cat /app/scout_data/latest_zscore_delta.json | python3 -m json.tool
Compare the
datafields here against what the scout claims to have sent. -
Verify the sequence counter is monotonic:
# Check the scout's state file: cat /tmp/scout_state.json # The "sequence" field should increment by exactly 1 each pass. # If it resets or skips, the scout's state persistence may be corrupted.
-
Watch for float precision drift: Python's
floatis IEEE 754 double-precision. Over thousands of packets, tiny rounding differences can accumulate. If your alpha score is computed from scout data and re-computed by the Gatekeeper, ensure both sides use the same rounding:- Scout:
round(value, 4)before building the packet - Gatekeeper: already applies
round(composite, 4)
- Scout:
-
Enable verbose scout logging for a single pass:
python3 scout.py --once --verbose
This prints the exact JSON being POSTed. Compare with what arrives at the Gatekeeper.
Symptom: Running python3 gatekeeper.py prints an error and exits before the server starts.
Resolution:
FATAL: USER_METAMASK_ADDRESS environment variable is not set.
Open .env.example, copy it to .env, and set your Polygon
MetaMask wallet address before starting the Gatekeeper.
Example: USER_METAMASK_ADDRESS=0xYourMetaMaskWalletAddress
Fix:
# Set the env var inline:
USER_METAMASK_ADDRESS="0xYourMetaMaskWalletAddress" python3 gatekeeper.py
# Or use a .env file (create from example):
cp .env.example .env
nano .env # set USER_METAMASK_ADDRESS=0xYourMetaMaskWalletAddressSymptom: The mesh status endpoint shows "status": "online" but the watchdog exits with code 1, or the "last_seen" timestamp is more than 5 minutes old.
Resolution:
# Check recent heartbeats
curl -s http://localhost:8877/alphafeed/mesh/status \
-H "X-Mesh-Key: your-mesh-key" | python3 -c "
import sys, json
d = json.load(sys.stdin)
for node, info in d['nodes'].items():
print(f'{node}: {info[\"status\"]} — last seen {info[\"last_seen\"]}')
"
# If stale, check the secondary node:
# - Is the laptop awake? (screen unlocked, not sleeping)
# - Is the heartbeat client running? (ps aux | grep heartbeat)
# - Can it reach the Gatekeeper's IP? (nc -zv 192.168.1.75 8877)
# - Is the heartbeat interval too long? (check HEARTBEAT_INTERVAL vs STALE_THRESHOLD)Symptom: Calls to /alphafeed/get-alpha succeed but the revenue balance stays at 0.
Resolution:
# Check the revenue log
cat /tmp/gatekeeper_revenue.log
# Check the Gatekeeper balance endpoint
curl -s http://localhost:8877/alphafeed/revenue \
-H "X-Mesh-Key: your-mesh-key" | python3 -m json.tool
# Verify USER_METAMASK_ADDRESS is set correctly
# (if it's empty, the Gatekeeper won't even start)
echo $USER_METAMASK_ADDRESSMIT — see LICENSE for details.
Built by FREE33 & tigerwolf33 under the Triad Core directive. Telegram: @freedom33free_bot Endpoint: http://192.168.1.75:8877