From ef94cc6da4a05eaabdb1b171aac244e778e880a7 Mon Sep 17 00:00:00 2001 From: aitbc Date: Tue, 19 May 2026 18:08:36 +0200 Subject: [PATCH] refactor: remove browser wallet extensions from repository - Deleted extensions/ directory containing Chrome and Firefox wallet implementations - Removed aitbc-wallet and aitbc-wallet-firefox subdirectories with all source files - Removed Firefox .xpi package (v1.0.5) - Deleted extension documentation (README.md files, API reference, architecture diagrams) - Removed manifest files, background scripts, content scripts, injected scripts, and popup UI code - Cleaned up wallet API implementation (connect --- docs/blockchain/ENVIRONMENT_CONFIGURATION.md | 448 ++++++++++++++++++ extensions/README.md | 77 --- extensions/aitbc-wallet-firefox-v1.0.5.xpi | Bin 22784 -> 0 bytes extensions/aitbc-wallet-firefox/README.md | 133 ------ extensions/aitbc-wallet-firefox/background.js | 153 ------ extensions/aitbc-wallet-firefox/content.js | 35 -- extensions/aitbc-wallet-firefox/injected.js | 106 ----- extensions/aitbc-wallet-firefox/manifest.json | 46 -- extensions/aitbc-wallet-firefox/popup.html | 109 ----- extensions/aitbc-wallet-firefox/popup.js | 162 ------- website/agent/.htaccess | 44 ++ website/agent/README.md | 105 ++++ website/agent/chains.json | 72 +++ website/agent/discovery.json | 70 +++ website/agent/health | 23 + website/agent/index.html | 356 ++++++++++++++ website/agent/islands.json | 103 ++++ website/agent/join/ait-mainnet.json | 121 +++++ website/agent/join/ait-testnet.json | 122 +++++ website/agent/openapi.json | 418 ++++++++++++++++ website/aitbc-proxy.conf | 40 ++ website/index.html | 34 ++ 22 files changed, 1956 insertions(+), 821 deletions(-) create mode 100644 docs/blockchain/ENVIRONMENT_CONFIGURATION.md delete mode 100644 extensions/README.md delete mode 100644 extensions/aitbc-wallet-firefox-v1.0.5.xpi delete mode 100644 extensions/aitbc-wallet-firefox/README.md delete mode 100644 extensions/aitbc-wallet-firefox/background.js delete mode 100644 extensions/aitbc-wallet-firefox/content.js delete mode 100644 extensions/aitbc-wallet-firefox/injected.js delete mode 100644 extensions/aitbc-wallet-firefox/manifest.json delete mode 100644 extensions/aitbc-wallet-firefox/popup.html delete mode 100644 extensions/aitbc-wallet-firefox/popup.js create mode 100644 website/agent/.htaccess create mode 100644 website/agent/README.md create mode 100644 website/agent/chains.json create mode 100644 website/agent/discovery.json create mode 100644 website/agent/health create mode 100644 website/agent/index.html create mode 100644 website/agent/islands.json create mode 100644 website/agent/join/ait-mainnet.json create mode 100644 website/agent/join/ait-testnet.json create mode 100644 website/agent/openapi.json diff --git a/docs/blockchain/ENVIRONMENT_CONFIGURATION.md b/docs/blockchain/ENVIRONMENT_CONFIGURATION.md new file mode 100644 index 00000000..74d94ef3 --- /dev/null +++ b/docs/blockchain/ENVIRONMENT_CONFIGURATION.md @@ -0,0 +1,448 @@ +# AITBC Blockchain Node Environment Configuration Guide + +Complete reference for configuring `node.env` and `blockchain.env` files for AITBC blockchain nodes. + +## Overview + +AITBC uses two environment configuration files: + +- **`/etc/aitbc/node.env`** - Node-specific settings (unique per node) +- **`/etc/aitbc/blockchain.env`** - Shared blockchain settings (can be identical across nodes) + +## node.env Reference + +**Location:** `/etc/aitbc/node.env` +**Purpose:** Contains variables unique to each node. Must be customized for every node in the network. + +### Node Identity + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `NODE_ID` | Yes | - | Unique identifier for this node (e.g., `aitbc`, `aitbc1`, `aitbc2`) | +| `p2p_node_id` | Yes | - | Unique P2P network identity. Format: `node-` | +| `proposer_id` | Yes* | - | PoA proposer address. Format: `ait1` | +| `enable_block_production` | No | `true` | Set `false` on follower nodes to prevent forks | +| `block_production_chains` | No | - | Comma-separated list of chains to produce blocks for | + +*Required if `enable_block_production=true` + +### P2P Configuration + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `p2p_bind_host` | Yes | `0.0.0.0` | Interface to bind P2P service | +| `p2p_bind_port` | Yes | `7070` | P2P service port | +| `p2p_peers` | No | - | Comma-separated list of peer nodes (e.g., `aitbc1:7070,aitbc2:7070`) | +| `trusted_proposers` | No | - | For follower nodes - trusted proposer addresses | + +### Node-Specific Overrides + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `NODE_HOST` | No | `0.0.0.0` | Override default host binding | +| `NODE_PORT` | No | `7070` | Override default port | + +### Example node.env (Hub Node) + +```bash +# AITBC Node-Specific Environment Configuration +# This file contains variables unique to this node + +# Node Identity +NODE_ID=aitbc + +# P2P Configuration +p2p_node_id=node-ad4e9170aea04a349469d17758de7b27 +p2p_bind_host=0.0.0.0 +p2p_bind_port=7070 +proposer_id=ait1ytkh0cn8v2a4zjwzyav6854832myf9j7unsse8yntmuwzst4qhtqe9hqdw + +# P2P Peers (empty for hub node) +p2p_peers= + +# Trusted Propers (for follower nodes) +trusted_proposers= + +# Block Production Configuration +block_production_chains=ait-mainnet +enable_block_production=true +``` + +### Example node.env (Follower Node) + +```bash +# AITBC Node-Specific Environment Configuration +# This file contains variables unique to this node + +# Node Identity +NODE_ID=aitbc2 + +# P2P Configuration +p2p_node_id=node-7af14c549bab473d9deb4ca8ab4bdcde +p2p_bind_host=0.0.0.0 +p2p_bind_port=7070 +proposer_id=ait1ytkh0cn8v2a4zjwzyav6854832myf9j7unsse8yntmuwzst4qhtqe9hqdw + +# P2P Peers (connect to hub and other nodes) +p2p_peers=aitbc:7070,aitbc1:7070 + +# Trusted Propers (for follower nodes) +trusted_proposers= + +# Block Production Configuration +block_production_chains= +enable_block_production=false +``` + +--- + +## blockchain.env Reference + +**Location:** `/etc/aitbc/blockchain.env` +**Purpose:** Contains shared blockchain settings. Can be identical across all nodes or customized per node. + +### Environment & Logging + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `NODE_ENV` | No | `production` | Environment mode (`development`, `production`) | +| `DEBUG` | No | `false` | Enable debug logging | +| `LOG_LEVEL` | No | `INFO` | Logging level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) | + +### Security + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `API_KEY_HASH_SECRET` | Yes | - | Secret for API key hashing | +| `SECRET_KEY` | Yes | - | Application secret key | +| `BLOCKCHAIN_API_KEY` | Yes | - | API key for blockchain access | +| `COORDINATOR_API_KEY` | Yes | - | API key for coordinator access | +| `JWT_SECRET` | Yes | - | JWT signing secret | + +### Database Configuration + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `DATABASE_URL` | No | - | PostgreSQL URL (if using PostgreSQL) | +| `db_encryption_enabled` | No | `false` | Enable SQLCipher encryption (ait-mainnet only) | +| `MEMPOOL_DB_URL` | No | - | PostgreSQL URL for mempool backend | + +### Redis & Gossip + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `REDIS_URL` | Yes | - | Redis connection URL | +| `gossip_backend` | Yes | `broadcast` | Gossip backend type | +| `gossip_broadcast_url` | Yes | - | Redis URL for gossip broadcast | +| `SYNC_REDIS_URL` | Yes | - | Redis URL for chain sync | + +### Sync Configuration + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `SYNC_SOURCE_HOST` | No | - | Host to sync from | +| `SYNC_SOURCE_PORT` | No | `8006` | Port to sync from | +| `SYNC_LEADER_HOST` | No | - | Leader node host | +| `SYNC_IMPORT_HOST` | No | `localhost` | Import service host | +| `SYNC_IMPORT_PORT` | No | `8006` | Import service port | +| `SYNC_CHAIN_ID` | No | - | Chain ID to sync | +| `auto_sync_enabled` | No | `false` | Enable automatic sync on gap detection | + +### Blockchain Configuration + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `CHAIN_ID` | Yes | `ait-mainnet` | Primary chain ID | +| `supported_chains` | Yes | `ait-mainnet` | Comma-separated list of supported chains | +| `island_id` | Yes | - | Island identifier for this node | +| `BLOCK_TIME` | No | `5` | Target block time in seconds | +| `NETWORK_ID` | No | `1337` | Network identifier | +| `CONSENSUS` | No | `proof_of_authority` | Consensus mechanism | + +### RPC Configuration + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `rpc_bind_host` | No | `0.0.0.0` | RPC service bind host | +| `rpc_bind_port` | No | `8006` | RPC service port | +| `default_peer_rpc_url` | No | - | Default peer RPC URL for sync | + +### Service Ports + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `api_host` | No | `0.0.0.0` | API service host | +| `api_port` | No | `8000` | API service port | +| `wallet_host` | No | `0.0.0.0` | Wallet service host | +| `wallet_port` | No | `8003` | Wallet service port | +| `exchange_host` | No | `0.0.0.0` | Exchange service host | +| `exchange_port` | No | `8001` | Exchange service port | + +### Feature Flags + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `ENFORCE_STATE_ROOT_VALIDATION` | No | `false` | Enable state root validation | +| `WORKERS` | No | `1` | Number of worker processes | + +### Monitoring + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `MONITORING_PORT` | No | `9000` | Monitoring service port | +| `PROMETHEUS_PORT` | No | `9090` | Prometheus port | +| `GRAFANA_PORT` | No | `3000` | Grafana port | + +### Example blockchain.env (Hub Node - ait-mainnet) + +```bash +# AITBC Environment Configuration +# This file contains shared environment variables + +# Environment +NODE_ENV=production +DEBUG=false +LOG_LEVEL=INFO + +# API Key Security +API_KEY_HASH_SECRET=f55e8a64e2cc3b44c50a43f25c7f985a6251ad4d0ceb469852d297721def28d2 +SECRET_KEY=production-secret-key-change-me-in-production +BLOCKCHAIN_API_KEY=production-api-key-change-me +COORDINATOR_API_KEY=admin_prod_key_use_real_value +JWT_SECRET=production-jwt-secret-32-chars-long-change-me + +# Redis Configuration +REDIS_URL=redis://localhost:6379/0 +gossip_backend=broadcast +gossip_broadcast_url=redis://10.1.223.93:6379 +SYNC_REDIS_URL=redis://10.1.223.93:6379 + +# Sync Configuration (for ait-testnet following) +SYNC_SOURCE_HOST=aitbc1 +SYNC_SOURCE_PORT=8006 +SYNC_LEADER_HOST=aitbc1 +SYNC_IMPORT_HOST=localhost +SYNC_IMPORT_PORT=8006 +SYNC_CHAIN_ID=ait-testnet + +# Blockchain Configuration +CHAIN_ID=ait-mainnet +BLOCK_TIME=5 +NETWORK_ID=1337 +CONSENSUS=proof_of_authority + +# RPC Configuration +rpc_bind_host=0.0.0.0 +rpc_bind_port=8006 + +# API Configuration +api_host=0.0.0.0 +api_port=8000 + +# Wallet Configuration +wallet_host=0.0.0.0 +wallet_port=8003 + +# Exchange Configuration +exchange_host=0.0.0.0 +exchange_port=8001 + +# Services Configuration +auto_sync_enabled=true +island_id=ait-mainnet-island +supported_chains=ait-mainnet,ait-testnet +db_encryption_enabled=false +default_peer_rpc_url=http://aitbc1:8006 +MEMPOOL_DB_URL=postgresql+psycopg://aitbc_mempool:password@localhost:5432/aitbc_mempool +ENFORCE_STATE_ROOT_VALIDATION=true +WORKERS=1 + +# Monitoring Configuration +MONITORING_PORT=9000 +PROMETHEUS_PORT=9090 +GRAFANA_PORT=3000 +``` + +### Example blockchain.env (Hub Node - ait-testnet) + +```bash +# AITBC Environment Configuration + +# Environment +NODE_ENV=production +DEBUG=false +LOG_LEVEL=INFO + +# API Key Security +API_KEY_HASH_SECRET=f55e8a64e2cc3b44c50a43f25c7f985a6251ad4d0ceb469852d297721def28d2 +SECRET_KEY=production-secret-key-change-me-in-production +BLOCKCHAIN_API_KEY=production-api-key-change-me +COORDINATOR_API_KEY=admin_prod_key_use_real_value +JWT_SECRET=production-jwt-secret-32-chars-long-change-me + +# Redis Configuration +REDIS_URL=redis://localhost:6379/0 +gossip_backend=broadcast +gossip_broadcast_url=redis://10.1.223.93:6379 +SYNC_REDIS_URL=redis://10.1.223.93:6379 + +# Sync Configuration (following ait-mainnet) +SYNC_SOURCE_HOST=aitbc +SYNC_SOURCE_PORT=8006 +SYNC_LEADER_HOST=aitbc +SYNC_IMPORT_HOST=localhost +SYNC_IMPORT_PORT=8006 +SYNC_CHAIN_ID=ait-testnet + +# Blockchain Configuration +CHAIN_ID=ait-testnet +BLOCK_TIME=5 +NETWORK_ID=1337 +CONSENSUS=proof_of_authority + +# RPC Configuration +rpc_bind_host=0.0.0.0 +rpc_bind_port=8006 + +# API Configuration +api_host=0.0.0.0 +api_port=8000 + +# Wallet Configuration +wallet_host=0.0.0.0 +wallet_port=8003 + +# Exchange Configuration +exchange_host=0.0.0.0 +exchange_port=8001 + +# Services Configuration +auto_sync_enabled=true +island_id=ait-testnet-island +supported_chains=ait-testnet +db_encryption_enabled=false +default_peer_rpc_url=http://aitbc:8006 +MEMPOOL_DB_URL=postgresql+psycopg://aitbc_mempool:password@localhost:5432/aitbc_mempool +ENFORCE_STATE_ROOT_VALIDATION=true +WORKERS=1 + +# Monitoring Configuration +MONITORING_PORT=9000 +PROMETHEUS_PORT=9090 +GRAFANA_PORT=3000 +``` + +--- + +## Node Role Patterns + +### Hub Node (Block Producer) + +**Characteristics:** +- `enable_block_production=true` +- `block_production_chains=` +- `p2p_peers=` (empty or minimal) +- `auto_sync_enabled=false` (for its own chain) +- Creates genesis block locally + +**Example:** aitbc for ait-mainnet, aitbc1 for ait-testnet + +### Follower Node + +**Characteristics:** +- `enable_block_production=false` +- `block_production_chains=` (empty) +- `p2p_peers=hub:7070,other:7070` +- `auto_sync_enabled=true` +- `default_peer_rpc_url=http://hub:8006` +- Syncs genesis from hub + +**Example:** gitea-runner following both chains + +--- + +## Common Issues and Solutions + +### Issue: "Gap detected" errors + +**Cause:** Node is receiving blocks but missing intermediate blocks + +**Solution:** +```bash +# Enable auto-sync +auto_sync_enabled=true +default_peer_rpc_url=http://hub-node:8006 +``` + +### Issue: Fork detection errors + +**Cause:** Multiple nodes with same `proposer_id` producing blocks simultaneously + +**Solution:** +```bash +# On follower nodes +enable_block_production=false +``` + +### Issue: P2P service fails to start + +**Cause:** Missing `p2p_bind_host` or `p2p_bind_port` + +**Solution:** +```bash +# Add to node.env +p2p_bind_host=0.0.0.0 +p2p_bind_port=7070 +``` + +### Issue: Chain ID mismatch + +**Cause:** `supported_chains` not set explicitly + +**Solution:** +```bash +# Always set explicitly in blockchain.env +supported_chains=ait-mainnet,ait-testnet +``` + +### Issue: Database location mismatch + +**Cause:** `DATABASE_URL` doesn't match actual database path + +**Solution:** +```bash +# Use correct path (default is /var/lib/aitbc/data//chain.db) +# Don't override DATABASE_URL unless using PostgreSQL +``` + +--- + +## Validation Checklist + +Before starting services, verify: + +- [ ] `NODE_ID` is unique across all nodes +- [ ] `p2p_node_id` is unique across all nodes +- [ ] `proposer_id` is unique for each proposer node +- [ ] `p2p_bind_host` and `p2p_bind_port` are set +- [ ] `supported_chains` matches the network chain ID +- [ ] `island_id` is set correctly +- [ ] `auto_sync_enabled` is `true` for follower nodes +- [ ] `enable_block_production` is `false` for follower nodes +- [ ] `default_peer_rpc_url` points to hub node for followers +- [ ] Redis URLs are correct and accessible + +--- + +## Related Documentation + +- [Adding Third Node Guide](./adding_gitea_runner_as_third_node.md) - Real-world setup example +- [Node Deployment Guide](../infrastructure/NODE_AITBC.md) - Infrastructure setup +- [Blockchain Node Schema](./node/SCHEMA.md) - Database schema reference +- [Multi-Node Setup Core](../../.windsurf/workflows/multi-node-blockchain-setup-core.md) - Workflow guide + +--- + +**Version:** 1.0 +**Last Updated:** 2026-05-19 +**Status:** Complete diff --git a/extensions/README.md b/extensions/README.md deleted file mode 100644 index fb9f6d12..00000000 --- a/extensions/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# AITBC Browser Wallet Extensions - -This directory contains browser wallet extensions for AITBC, supporting both Chrome and Firefox browsers. - -## Quick Start - -### For Chrome/Brave/Edge Users - -1. Navigate to `aitbc-wallet/` folder -2. Follow the installation instructions in `aitbc-wallet/README.md` - -### For Firefox Users - -1. Navigate to `aitbc-wallet-firefox/` folder -2. Follow the installation instructions in `aitbc-wallet-firefox/README.md` - -## Using the Extensions - -1. Install the appropriate extension for your browser -2. Navigate to the AITBC Trade Exchange: https://aitbc.bubuit.net/Exchange -3. Toggle from "Demo Mode" to "Real Mode" -4. Click "Connect AITBC Wallet" -5. Create a new account or import an existing one -6. Approve the connection request - -## Features - -- ✅ Cross-browser support (Chrome, Firefox, Edge, Brave) -- ✅ Secure local key storage -- ✅ dApp connection management -- ✅ Transaction signing -- ✅ Message signing -- ✅ Balance tracking -- ✅ Account management (create/import) - -## Security Best Practices - -1. **Never share your private key** - It's the key to your funds -2. **Keep backups** - Save your private key in a secure location -3. **Verify URLs** - Always check you're on aitbc.bubuit.net -4. **Use strong passwords** - Protect your browser with a strong password -5. **Keep updated** - Keep your browser and extension updated - -## Development - -Both extensions share most of their code: - -- `injected.js` - Provides the wallet API to dApps -- `popup.html/js` - Wallet user interface -- `content.js` - Communicates between the extension and dApps - -The main differences are: -- Chrome uses Manifest V3 -- Firefox uses Manifest V2 (required for full functionality) -- Different background script architectures - -## Architecture - -``` -┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ -│ dApp Page │────▶│ Content Script │────▶│ Background Script│ -│ (Exchange UI) │ │ (bridge) │ │ (wallet logic) │ -└─────────────────┘ └──────────────────┘ └─────────────────┘ - ▲ │ - │ ▼ - ┌──────────────────┐ ┌─────────────────┐ - │ Injected Script │ │ Extension UI │ - │ (window.aitbcWallet)│ │ (popup.html) │ - └──────────────────┘ └─────────────────┘ -``` - -## Support - -For issues or questions: -1. Check the individual README files for your browser -2. Create an issue in the repository -3. Join our community discussions diff --git a/extensions/aitbc-wallet-firefox-v1.0.5.xpi b/extensions/aitbc-wallet-firefox-v1.0.5.xpi deleted file mode 100644 index d08590ff071f1dd22d0857ced8e964628d87a489..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22784 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 acc.address); -} - -// Get balance for an address -async function getBalance(address) { - // In a real implementation, this would query the blockchain - // For demo, return stored balance - const result = await chrome.storage.local.get(['accounts']); - const accounts = result.accounts || []; - const account = accounts.find(acc => acc.address === address); - - return { - address: address, - balance: account ? account.balance || 0 : 0, - symbol: 'AITBC' - }; -} - -// Send transaction -async function sendTransaction(params) { - // In a real implementation, this would create, sign, and broadcast a transaction - const { to, amount, data } = params; - - // Get current account - const result = await chrome.storage.local.get(['currentAccount']); - const account = result.currentAccount; - - if (!account) { - throw new Error('No account connected'); - } - - // Confirm transaction - const confirmed = confirm(`Send ${amount} AITBC to ${to}?\n\nFrom: ${account.address}`); - if (!confirmed) { - throw new Error('Transaction rejected'); - } - - // Return mock transaction hash - return { - hash: '0x' + Array.from(crypto.getRandomValues(new Uint8Array(32)), b => b.toString(16).padStart(2, '0')).join(''), - status: 'pending' - }; -} - -// Sign message -async function signMessage(message) { - // Get current account - const result = await chrome.storage.local.get(['currentAccount']); - const account = result.currentAccount; - - if (!account) { - throw new Error('No account connected'); - } - - // Confirm signing - const confirmed = confirm(`Sign the following message?\n\n"${message}"\n\nAccount: ${account.address}`); - if (!confirmed) { - throw new Error('Message signing rejected'); - } - - // In a real implementation, this would sign with the private key - // For demo, return a mock signature - const encoder = new TextEncoder(); - const data = encoder.encode(message + account.privateKey); - const hash = await crypto.subtle.digest('SHA-256', data); - - return Array.from(new Uint8Array(hash), b => b.toString(16).padStart(2, '0')).join(''); -} diff --git a/extensions/aitbc-wallet-firefox/content.js b/extensions/aitbc-wallet-firefox/content.js deleted file mode 100644 index 1a208d68..00000000 --- a/extensions/aitbc-wallet-firefox/content.js +++ /dev/null @@ -1,35 +0,0 @@ -// Content script for AITBC Wallet Firefox extension -(function() { - // Inject the wallet API into the page - const script = document.createElement('script'); - script.src = chrome.runtime.getURL('injected.js'); - script.onload = function() { - this.remove(); - }; - (document.head || document.documentElement).appendChild(script); - - // Create a port to background script - const port = chrome.runtime.connect({ name: "aitbc-wallet" }); - - // Listen for messages from the injected script - window.addEventListener('message', function(event) { - // Only accept messages from our own window - if (event.source !== window) return; - - if (event.data.type && event.data.type === 'AITBC_WALLET_REQUEST') { - // Forward the request to the background script - port.postMessage(event.data); - } - }); - - // Listen for responses from background script - port.onMessage.addListener(function(response) { - // Send the response back to the page - window.postMessage({ - type: 'AITBC_WALLET_RESPONSE', - id: response.id, - result: response.result, - error: response.error - }, '*'); - }); -})(); diff --git a/extensions/aitbc-wallet-firefox/injected.js b/extensions/aitbc-wallet-firefox/injected.js deleted file mode 100644 index e4cd6a0e..00000000 --- a/extensions/aitbc-wallet-firefox/injected.js +++ /dev/null @@ -1,106 +0,0 @@ -// Injected script that provides the AITBC wallet API to the dApp -(function() { - // Create the wallet API object - const aitbcWallet = { - // Check if wallet is available - isAvailable: function() { - return true; - }, - - // Connect to wallet - connect: async function() { - return new Promise((resolve, reject) => { - const requestId = Date.now().toString(); - - // Send request to content script - window.postMessage({ - type: 'AITBC_WALLET_REQUEST', - id: requestId, - method: 'connect' - }, '*'); - - // Listen for response - const messageHandler = function(event) { - if (event.data.type === 'AITBC_WALLET_RESPONSE' && event.data.id === requestId) { - window.removeEventListener('message', messageHandler); - if (event.data.response.error) { - reject(new Error(event.data.response.error)); - } else { - resolve(event.data.response); - } - } - }; - - window.addEventListener('message', messageHandler); - - // Timeout after 30 seconds - setTimeout(() => { - window.removeEventListener('message', messageHandler); - reject(new Error('Connection timeout')); - }, 30000); - }); - }, - - // Get account address - getAccount: async function() { - const accounts = await this.request({ method: 'accounts' }); - return accounts[0]; - }, - - // Get balance - getBalance: async function(address) { - return this.request({ method: 'getBalance', params: { address } }); - }, - - // Send transaction - sendTransaction: async function(to, amount, data = null) { - return this.request({ - method: 'sendTransaction', - params: { to, amount, data } - }); - }, - - // Sign message - signMessage: async function(message) { - return this.request({ method: 'signMessage', params: { message } }); - }, - - // Generic request method - request: async function(payload) { - return new Promise((resolve, reject) => { - const requestId = Date.now().toString(); - - window.postMessage({ - type: 'AITBC_WALLET_REQUEST', - id: requestId, - method: payload.method, - params: payload.params || {} - }, '*'); - - const messageHandler = function(event) { - if (event.data.type === 'AITBC_WALLET_RESPONSE' && event.data.id === requestId) { - window.removeEventListener('message', messageHandler); - if (event.data.response.error) { - reject(new Error(event.data.response.error)); - } else { - resolve(event.data.response); - } - } - }; - - window.addEventListener('message', messageHandler); - - setTimeout(() => { - window.removeEventListener('message', messageHandler); - reject(new Error('Request timeout')); - }, 30000); - }); - } - }; - - // Inject the wallet API into the window object - window.aitbcWallet = aitbcWallet; - - // Fire an event to notify the dApp that the wallet is ready - window.dispatchEvent(new Event('aitbcWalletReady')); -})(); diff --git a/extensions/aitbc-wallet-firefox/manifest.json b/extensions/aitbc-wallet-firefox/manifest.json deleted file mode 100644 index 7b97484f..00000000 --- a/extensions/aitbc-wallet-firefox/manifest.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "manifest_version": 2, - "name": "AITBC Wallet", - "version": "1.0.0", - "description": "AITBC Browser Wallet for trading and managing AITBC tokens", - - "permissions": [ - "storage", - "activeTab" - ], - - "content_scripts": [ - { - "matches": ["https://aitbc.bubuit.net/*", "http://localhost:3002/*"], - "js": ["content.js"], - "run_at": "document_start" - } - ], - - "browser_action": { - "default_popup": "popup.html", - "default_title": "AITBC Wallet", - "default_icon": { - "16": "icons/icon-16.png", - "32": "icons/icon-32.png", - "48": "icons/icon-48.png", - "128": "icons/icon-128.png" - } - }, - - "web_accessible_resources": [ - ["injected.js"] - ], - - "icons": { - "16": "icons/icon-16.png", - "32": "icons/icon-32.png", - "48": "icons/icon-48.png", - "128": "icons/icon-128.png" - }, - - "background": { - "scripts": ["background.js"], - "persistent": false - } -} diff --git a/extensions/aitbc-wallet-firefox/popup.html b/extensions/aitbc-wallet-firefox/popup.html deleted file mode 100644 index 16612b8c..00000000 --- a/extensions/aitbc-wallet-firefox/popup.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - -
- -

Wallet

-
- -
-
Account Address:
-
Not connected
-
0 AITBC
-
- -
- - - - - -
- -
-

Recent Transactions

-
-
No transactions yet
-
-
- - - - diff --git a/extensions/aitbc-wallet-firefox/popup.js b/extensions/aitbc-wallet-firefox/popup.js deleted file mode 100644 index dee47403..00000000 --- a/extensions/aitbc-wallet-firefox/popup.js +++ /dev/null @@ -1,162 +0,0 @@ -// Popup script for AITBC Wallet extension -let currentAccount = null; -let accounts = []; - -// Load wallet data on popup open -document.addEventListener('DOMContentLoaded', async function() { - await loadWalletData(); - updateUI(); -}); - -// Load wallet data from storage -async function loadWalletData() { - const result = await chrome.storage.local.get(['accounts', 'currentAccount']); - accounts = result.accounts || []; - currentAccount = result.currentAccount || null; -} - -// Save wallet data to storage -async function saveWalletData() { - await chrome.storage.local.set({ - accounts: accounts, - currentAccount: currentAccount - }); -} - -// Update UI with current wallet state -function updateUI() { - const addressEl = document.getElementById('accountAddress'); - const balanceEl = document.getElementById('balance'); - - if (currentAccount) { - addressEl.textContent = currentAccount.address; - balanceEl.textContent = `${currentAccount.balance || 0} AITBC`; - } else { - addressEl.textContent = 'Not connected'; - balanceEl.textContent = '0 AITBC'; - } -} - -// Create a new account -async function createAccount() { - // Generate a new private key and address - const privateKey = generatePrivateKey(); - const address = await generateAddress(privateKey); - - const newAccount = { - address: address, - privateKey: privateKey, - balance: 0, - created: new Date().toISOString() - }; - - accounts.push(newAccount); - currentAccount = newAccount; - await saveWalletData(); - updateUI(); - - alert('New account created! Please save your private key securely.'); -} - -// Import account from private key -async function importAccount() { - const privateKey = prompt('Enter your private key:'); - if (!privateKey) return; - - try { - const address = await generateAddress(privateKey); - - // Check if account already exists - const existing = accounts.find(a => a.address === address); - if (existing) { - currentAccount = existing; - } else { - currentAccount = { - address: address, - privateKey: privateKey, - balance: 0, - created: new Date().toISOString() - }; - accounts.push(currentAccount); - } - - await saveWalletData(); - updateUI(); - alert('Account imported successfully!'); - } catch (error) { - alert('Invalid private key!'); - } -} - -// Send tokens -async function sendTokens() { - if (!currentAccount) { - alert('Please create or import an account first!'); - return; - } - - const to = prompt('Send to address:'); - const amount = prompt('Amount:'); - - if (!to || !amount) return; - - // In a real implementation, this would create and sign a transaction - alert(`Would send ${amount} AITBC to ${to}`); -} - -// Receive tokens -function receiveTokens() { - if (!currentAccount) { - alert('Please create or import an account first!'); - return; - } - - alert(`Your receiving address:\n${currentAccount.address}`); -} - -// View on explorer -function viewOnExplorer() { - if (!currentAccount) { - alert('Please create or import an account first!'); - return; - } - - chrome.tabs.create({ url: `https://aitbc.bubuit.net/explorer/address/${currentAccount.address}` }); -} - -// Generate a random private key (demo only) -function generatePrivateKey() { - const array = new Uint8Array(32); - crypto.getRandomValues(array); - return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join(''); -} - -// Generate address from private key (demo only) -async function generateAddress(privateKey) { - // In a real implementation, this would derive the address from the private key - // using the appropriate cryptographic algorithm - const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(privateKey)); - return 'aitbc1' + Array.from(new Uint8Array(hash), b => b.toString(16).padStart(2, '0')).join('').substring(0, 40); -} - -// Listen for connection requests from dApps -chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - if (request.method === 'connect') { - // Show connection dialog - const connected = confirm(`Allow this site to connect to your AITBC Wallet?`); - - if (connected && currentAccount) { - sendResponse({ - success: true, - address: currentAccount.address - }); - } else { - sendResponse({ - success: false, - error: 'User rejected connection' - }); - } - } - - return true; // Keep the message channel open for async response -}); diff --git a/website/agent/.htaccess b/website/agent/.htaccess new file mode 100644 index 00000000..66e387b6 --- /dev/null +++ b/website/agent/.htaccess @@ -0,0 +1,44 @@ +# Apache configuration for agent endpoints +# Enable CORS for agent access +Header always set Access-Control-Allow-Origin "*" +Header always set Access-Control-Allow-Methods "GET, OPTIONS" +Header always set Access-Control-Allow-Headers "Content-Type, Accept" + +# Set JSON content type for .json files + + ForceType application/json + Header always set Content-Type "application/json" + + +# Cache control - short cache for dynamic content + + Header always set Cache-Control "max-age=60, must-revalidate" + + + + Header always set Cache-Control "max-age=30, must-revalidate" + + + + Header always set Cache-Control "max-age=60, must-revalidate" + + +# Longer cache for static content + + Header always set Cache-Control "max-age=3600" + + + + Header always set Cache-Control "max-age=3600" + + +# Disable caching for health endpoint + + Header always set Cache-Control "no-cache, no-store, must-revalidate" + + +# Enable rewrite engine for clean URLs +RewriteEngine On + +# Redirect /agent to /agent/ (add trailing slash) +RewriteRule ^/agent$ /agent/ [R=301,L] diff --git a/website/agent/README.md b/website/agent/README.md new file mode 100644 index 00000000..6ea636aa --- /dev/null +++ b/website/agent/README.md @@ -0,0 +1,105 @@ +# AITBC Agent API + +Machine-readable API endpoints for autonomous agents to discover and interact with the AITBC network. + +## Overview + +This directory provides agent-first access to the AITBC blockchain network. All endpoints return structured JSON for programmatic consumption. + +## Quick Start + +```bash +# 1. Discover the network +curl -s http://aitbc:8006/agent/discovery.json | jq . + +# 2. Get island information +curl -s http://aitbc:8006/agent/islands.json | jq '.islands[]' + +# 3. Get chain information +curl -s http://aitbc:8006/agent/chains.json | jq '.chains[]' + +# 4. Get join instructions +curl -s http://aitbc:8006/agent/join/ait-mainnet.json | jq '.how_to_join' +``` + +## Endpoints + +### Discovery + +| Endpoint | Description | Cache | +|----------|-------------|-------| +| `/agent/discovery.json` | Complete network information | 60s | +| `/agent/islands.json` | All islands with status | 30s | +| `/agent/chains.json` | Chain configurations | 60s | + +### Join Network + +| Endpoint | Description | Cache | +|----------|-------------|-------| +| `/agent/join/ait-mainnet.json` | Join instructions for mainnet | 1h | +| `/agent/join/ait-testnet.json` | Join instructions for testnet | 1h | + +### API Documentation + +| Endpoint | Description | Cache | +|----------|-------------|-------| +| `/agent/openapi.json` | OpenAPI 3.0 specification | 1h | +| `/agent/health` | Health check endpoint | No cache | + +### RPC Endpoints + +| Endpoint | Description | +|----------|-------------| +| `/rpc/head` | Current head block | +| `/rpc/info` | Chain information | +| `/rpc/islands` | Island memberships | +| `/rpc/islands/{id}` | Island details | + +## CORS Support + +All `/agent/` and `/rpc/` endpoints include CORS headers for cross-origin access: + +``` +Access-Control-Allow-Origin: * +Access-Control-Allow-Methods: GET, POST, OPTIONS +Access-Control-Allow-Headers: Content-Type, Accept +``` + +## Agent Landing Page + +Human-readable documentation is available at: +- http://aitbc:8006/agent/ + +## Network Architecture + +### Islands + +- **ait-mainnet-island**: Production chain (hub: aitbc) +- **ait-testnet-island**: Test chain (hub: aitbc1) + +### Nodes + +| Node | Role | Chains | Island | +|------|------|--------|--------| +| aitbc | Hub | ait-mainnet | ait-mainnet-island | +| aitbc | Follower | ait-testnet | ait-testnet-island | +| aitbc1 | Hub | ait-testnet | ait-testnet-island | +| aitbc1 | Follower | ait-mainnet | ait-mainnet-island | + +## Configuration Templates + +See `/agent/join/*.json` files for complete configuration templates including: +- Environment variables +- P2P peer lists +- RPC endpoints +- Step-by-step join instructions + +## Version + +- API Version: 1.0.0 +- Format Version: 1.0 +- Last Updated: 2026-05-19 + +## For Humans + +The main website is available at the root: http://aitbc:8006/ diff --git a/website/agent/chains.json b/website/agent/chains.json new file mode 100644 index 00000000..02f01aa7 --- /dev/null +++ b/website/agent/chains.json @@ -0,0 +1,72 @@ +{ + "chains": [ + { + "chain_id": "ait-mainnet", + "name": "AIT Mainnet", + "island_id": "ait-mainnet-island", + "type": "production", + "status": "active", + "config": { + "block_time": 5, + "consensus": "proof_of_authority", + "network_id": 1337, + "enable_empty_blocks": true, + "max_empty_block_interval": 60 + }, + "endpoints": { + "rpc": ["http://aitbc:8006/rpc", "http://aitbc1:8006/rpc"], + "head": "/rpc/head", + "info": "/rpc/info", + "supply": "/rpc/supply" + }, + "join": { + "p2p": { + "port": 7070, + "peers": ["aitbc:7070", "aitbc1:7070"] + }, + "sync": { + "source": "http://aitbc1:8006", + "chain_id": "ait-mainnet" + }, + "guide": "/agent/join/ait-mainnet.json" + } + }, + { + "chain_id": "ait-testnet", + "name": "AIT Testnet", + "island_id": "ait-testnet-island", + "type": "test", + "status": "active", + "config": { + "block_time": 5, + "consensus": "proof_of_authority", + "network_id": 1337, + "enable_empty_blocks": true, + "max_empty_block_interval": 60 + }, + "endpoints": { + "rpc": ["http://aitbc:8006/rpc", "http://aitbc1:8006/rpc"], + "head": "/rpc/head", + "info": "/rpc/info", + "supply": "/rpc/supply" + }, + "join": { + "p2p": { + "port": 7070, + "peers": ["aitbc:7070", "aitbc1:7070"] + }, + "sync": { + "source": "http://aitbc1:8006", + "chain_id": "ait-testnet" + }, + "guide": "/agent/join/ait-testnet.json" + } + } + ], + "_meta": { + "generated_at": "2026-05-19T15:50:00Z", + "format_version": "1.0", + "total_chains": 2, + "active_chains": 2 + } +} diff --git a/website/agent/discovery.json b/website/agent/discovery.json new file mode 100644 index 00000000..6155e06b --- /dev/null +++ b/website/agent/discovery.json @@ -0,0 +1,70 @@ +{ + "network": { + "name": "AITBC", + "version": "1.0.0", + "description": "AI-powered blockchain platform with multi-island architecture", + "apis": { + "rpc": { + "url": "http://localhost:8006/rpc", + "documentation": "/agent/openapi.json", + "version": "1.0" + }, + "agent": { + "discovery": "/agent/discovery.json", + "islands": "/agent/islands.json", + "chains": "/agent/chains.json", + "health": "/agent/health", + "join": "/agent/join/" + } + }, + "islands": [ + { + "island_id": "ait-mainnet-island", + "name": "AIT Mainnet", + "chain_id": "ait-mainnet", + "status": "active", + "description": "Primary production chain for AITBC", + "endpoints": { + "rpc": ["http://aitbc:8006", "http://aitbc1:8006"], + "p2p": ["aitbc:7070", "aitbc1:7070"] + }, + "chain_info": { + "block_time": 5, + "consensus": "proof_of_authority", + "network_id": 1337 + }, + "join_guide": "/agent/join/ait-mainnet.json" + }, + { + "island_id": "ait-testnet-island", + "name": "AIT Testnet", + "chain_id": "ait-testnet", + "status": "active", + "description": "Test network for development and experimentation", + "endpoints": { + "rpc": ["http://aitbc:8006", "http://aitbc1:8006"], + "p2p": ["aitbc:7070", "aitbc1:7070"] + }, + "chain_info": { + "block_time": 5, + "consensus": "proof_of_authority", + "network_id": 1337 + }, + "join_guide": "/agent/join/ait-testnet.json" + } + ] + }, + "this_node": { + "node_id": "aitbc", + "role": "hub", + "is_hub": true, + "block_production": true, + "chains": ["ait-mainnet"], + "island_memberships": ["ait-mainnet-island"], + "discovery_url": "http://aitbc:8006/agent/discovery.json" + }, + "_meta": { + "generated_at": "2026-05-19T15:50:00Z", + "format_version": "1.0" + } +} diff --git a/website/agent/health b/website/agent/health new file mode 100644 index 00000000..cb3c844d --- /dev/null +++ b/website/agent/health @@ -0,0 +1,23 @@ +{ + "status": "healthy", + "timestamp": "2026-05-19T15:50:00Z", + "node_id": "aitbc", + "services": { + "blockchain_node": "running", + "blockchain_rpc": "running", + "p2p": "running" + }, + "chains": [ + { + "chain_id": "ait-mainnet", + "status": "active", + "sync_status": "hub" + }, + { + "chain_id": "ait-testnet", + "status": "active", + "sync_status": "syncing" + } + ], + "discovery": "http://aitbc:8006/agent/discovery.json" +} diff --git a/website/agent/index.html b/website/agent/index.html new file mode 100644 index 00000000..d643d838 --- /dev/null +++ b/website/agent/index.html @@ -0,0 +1,356 @@ + + + + + + AITBC Agent API - Machine-Readable Network Interface + + + + + + +
+
+

AITBC Agent API v1.0

+

Machine-readable network interface for autonomous agents

+
+ +
+ Agent-First Design: This page provides human-readable documentation, but all endpoints return structured JSON for programmatic consumption. Start with /agent/discovery.json for complete network information. +
+ +
+

Network Status

+
+
+

AIT Mainnet

+

active

+

Island: ait-mainnet-island

+

Role: Hub (Block Production)

+
+
+

AIT Testnet

+

active

+

Island: ait-testnet-island

+

Role: Follower (Syncing)

+
+
+
+ +
+

Agent Discovery

+
+
+
+ /agent/discovery.json + GET +
+

Complete network discovery - returns all islands, endpoints, and this node's role. Start here.

+ View JSON → +
+
+
+ +
+

Island Management

+
+
+
+ /agent/islands.json + GET +
+

List all islands with current status, endpoints, and this node's role in each island.

+ View JSON → +
+
+
+ /rpc/islands + GET +
+

Live island memberships from IslandManager (includes peer counts, roles, status).

+
+
+
+ /rpc/islands/{island_id} + GET +
+

Detailed information about a specific island.

+
+
+
+ +
+

Chain Information

+
+
+
+ /agent/chains.json + GET +
+

All chains with configuration, endpoints, and join information.

+ View JSON → +
+
+
+ /rpc/head + GET +
+

Current head block (height, hash, timestamp).

+
+
+
+ /rpc/info + GET +
+

Chain information (chain_id, network_id, consensus).

+
+
+
+ +
+

Join Network

+
+
+
+ /agent/join/ait-mainnet.json + GET +
+

Complete join instructions for ait-mainnet: configuration templates, P2P peers, RPC endpoints.

+ View JSON → +
+
+
+ /agent/join/ait-testnet.json + GET +
+

Complete join instructions for ait-testnet.

+ View JSON → +
+
+
+ +
+

API Documentation

+
+
+
+ /agent/openapi.json + GET +
+

OpenAPI 3.0 specification for all RPC and agent endpoints.

+ View JSON → +
+
+
+ /agent/health + GET +
+

Health check endpoint for monitoring.

+ View JSON → +
+
+
+ +
+

Quick Start for Agents

+
+ + # 1. Discover network +curl -s http://aitbc:8006/agent/discovery.json | jq . + +# 2. Get island information +curl -s http://aitbc:8006/agent/islands.json | jq '.islands[]' + +# 3. Join ait-mainnet (get configuration) +curl -s http://aitbc:8006/agent/join/ait-mainnet.json | jq '.how_to_join' + +# 4. Check chain head +curl -s http://aitbc:8006/rpc/head | jq . +
+
+ +
+

Key Configuration Values

+
+ + { + "p2p": { + "port": 7070, + "peers": ["aitbc:7070", "aitbc1:7070"] + }, + "rpc": { + "aitbc": "http://aitbc:8006", + "aitbc1": "http://aitbc1:8006" + }, + "islands": [ + { + "id": "ait-mainnet-island", + "chain": "ait-mainnet", + "hub": "aitbc" + }, + { + "id": "ait-testnet-island", + "chain": "ait-testnet", + "hub": "aitbc1" + } + ] +} +
+
+ +
+

AITBC Network • Agent-First Architecture

+

Node ID: aitbc | Role: Hub | Human Interface →

+
+
+ + diff --git a/website/agent/islands.json b/website/agent/islands.json new file mode 100644 index 00000000..1cca299b --- /dev/null +++ b/website/agent/islands.json @@ -0,0 +1,103 @@ +{ + "islands": [ + { + "island_id": "ait-mainnet-island", + "island_name": "AIT Mainnet", + "chain_id": "ait-mainnet", + "status": "active", + "role": "hub", + "chain_info": { + "block_time": 5, + "consensus": "proof_of_authority", + "network_id": 1337, + "genesis_timestamp": "2026-05-19T14:44:44.174290" + }, + "endpoints": { + "rpc": [ + { + "url": "http://aitbc:8006", + "node_id": "aitbc", + "role": "hub" + }, + { + "url": "http://aitbc1:8006", + "node_id": "aitbc1", + "role": "follower" + } + ], + "p2p": [ + { + "address": "aitbc:7070", + "node_id": "aitbc" + }, + { + "address": "aitbc1:7070", + "node_id": "aitbc1" + } + ] + }, + "this_node": { + "node_id": "aitbc", + "role": "hub", + "is_hub": true, + "block_production_chains": ["ait-mainnet"], + "enable_block_production": true + } + }, + { + "island_id": "ait-testnet-island", + "island_name": "AIT Testnet", + "chain_id": "ait-testnet", + "status": "active", + "role": "follower", + "chain_info": { + "block_time": 5, + "consensus": "proof_of_authority", + "network_id": 1337, + "genesis_timestamp": "2025-01-01T00:00:00" + }, + "endpoints": { + "rpc": [ + { + "url": "http://aitbc:8006", + "node_id": "aitbc", + "role": "follower" + }, + { + "url": "http://aitbc1:8006", + "node_id": "aitbc1", + "role": "hub" + } + ], + "p2p": [ + { + "address": "aitbc:7070", + "node_id": "aitbc" + }, + { + "address": "aitbc1:7070", + "node_id": "aitbc1" + } + ] + }, + "sync_source": { + "host": "aitbc1", + "port": 8006, + "rpc_url": "http://aitbc1:8006" + }, + "this_node": { + "node_id": "aitbc", + "role": "follower", + "is_hub": false, + "block_production_chains": [], + "enable_block_production": false + } + } + ], + "_meta": { + "generated_at": "2026-05-19T15:50:00Z", + "format_version": "1.0", + "total_islands": 2, + "active_islands": 2 + } +} diff --git a/website/agent/join/ait-mainnet.json b/website/agent/join/ait-mainnet.json new file mode 100644 index 00000000..65c3b311 --- /dev/null +++ b/website/agent/join/ait-mainnet.json @@ -0,0 +1,121 @@ +{ + "chain_id": "ait-mainnet", + "island_id": "ait-mainnet-island", + "name": "AIT Mainnet", + "description": "Primary production chain for AITBC platform", + "type": "production", + "status": "active", + "requirements": { + "minimum_version": "1.0.0", + "required_services": ["aitbc-blockchain-node", "aitbc-blockchain-rpc"], + "ports": { + "p2p": 7070, + "rpc": 8006 + } + }, + "how_to_join": { + "step_1_environment": { + "description": "Configure node.env for this node", + "file": "/etc/aitbc/node.env", + "variables": { + "NODE_ID": "your-node-id", + "p2p_node_id": "node-$(uuidgen | tr -d '-')", + "p2p_bind_host": "0.0.0.0", + "p2p_bind_port": "7070", + "proposer_id": "your-proposer-id", + "p2p_peers": "aitbc:7070,aitbc1:7070", + "enable_block_production": "false", + "block_production_chains": "" + }, + "note": "Set enable_block_production=false for follower nodes" + }, + "step_2_blockchain_env": { + "description": "Configure blockchain.env", + "file": "/etc/aitbc/blockchain.env", + "variables": { + "island_id": "ait-mainnet-island", + "supported_chains": "ait-mainnet", + "CHAIN_ID": "ait-mainnet", + "auto_sync_enabled": "true", + "default_peer_rpc_url": "http://aitbc1:8006", + "SYNC_SOURCE_HOST": "aitbc1", + "SYNC_SOURCE_PORT": "8006", + "SYNC_CHAIN_ID": "ait-mainnet" + } + }, + "step_3_networking": { + "description": "Ensure network connectivity", + "requirements": [ + "Port 7070 open for P2P communication", + "Port 8006 open for RPC access", + "Connectivity to aitbc (10.1.223.93) and aitbc1 (10.1.223.40)" + ] + }, + "step_4_start_services": { + "description": "Start blockchain services", + "commands": [ + "systemctl start aitbc-blockchain-node.service", + "systemctl start aitbc-blockchain-rpc.service" + ] + }, + "step_5_verify": { + "description": "Verify successful join", + "commands": [ + "curl -s http://localhost:8006/rpc/head", + "sqlite3 /var/lib/aitbc/data/ait-mainnet/chain.db 'SELECT MAX(height) FROM block'" + ], + "expected_result": "Block height should be increasing as blocks are synced" + } + }, + "endpoints": { + "rpc": { + "hub": "http://aitbc:8006", + "backup": "http://aitbc1:8006" + }, + "p2p": { + "peers": ["aitbc:7070", "aitbc1:7070"] + } + }, + "configuration_templates": { + "follower_node": { + "node.env": { + "NODE_ID": "your-node-id", + "p2p_node_id": "node-unique-uuid", + "p2p_bind_host": "0.0.0.0", + "p2p_bind_port": "7070", + "proposer_id": "ait1your-proposer-id", + "p2p_peers": "aitbc:7070,aitbc1:7070", + "trusted_proposers": "", + "block_production_chains": "", + "enable_block_production": "false" + }, + "blockchain.env": { + "auto_sync_enabled": "true", + "island_id": "ait-mainnet-island", + "supported_chains": "ait-mainnet", + "default_peer_rpc_url": "http://aitbc1:8006", + "SYNC_SOURCE_HOST": "aitbc1", + "SYNC_SOURCE_PORT": "8006", + "SYNC_CHAIN_ID": "ait-mainnet" + } + } + }, + "troubleshooting": { + "gap_detected_errors": { + "cause": "Node is receiving blocks but missing intermediate blocks", + "solution": "Ensure auto_sync_enabled=true and default_peer_rpc_url points to active hub" + }, + "fork_detection": { + "cause": "Multiple nodes with same proposer_id producing blocks", + "solution": "Ensure enable_block_production=false on follower nodes" + }, + "p2p_connection_failed": { + "cause": "Cannot connect to P2P peers", + "solution": "Verify p2p_bind_host and p2p_bind_port are set correctly, check firewall rules" + } + }, + "_meta": { + "format_version": "1.0", + "updated_at": "2026-05-19T15:50:00Z" + } +} diff --git a/website/agent/join/ait-testnet.json b/website/agent/join/ait-testnet.json new file mode 100644 index 00000000..62bd1fa6 --- /dev/null +++ b/website/agent/join/ait-testnet.json @@ -0,0 +1,122 @@ +{ + "chain_id": "ait-testnet", + "island_id": "ait-testnet-island", + "name": "AIT Testnet", + "description": "Test network for AITBC platform development and experimentation", + "type": "test", + "status": "active", + "requirements": { + "minimum_version": "1.0.0", + "required_services": ["aitbc-blockchain-node", "aitbc-blockchain-rpc"], + "ports": { + "p2p": 7070, + "rpc": 8006 + } + }, + "how_to_join": { + "step_1_environment": { + "description": "Configure node.env for this node", + "file": "/etc/aitbc/node.env", + "variables": { + "NODE_ID": "your-node-id", + "p2p_node_id": "node-$(uuidgen | tr -d '-')", + "p2p_bind_host": "0.0.0.0", + "p2p_bind_port": "7070", + "proposer_id": "your-proposer-id", + "p2p_peers": "aitbc:7070,aitbc1:7070", + "enable_block_production": "false", + "block_production_chains": "" + }, + "note": "Set enable_block_production=false for follower nodes (hub is aitbc1)" + }, + "step_2_blockchain_env": { + "description": "Configure blockchain.env", + "file": "/etc/aitbc/blockchain.env", + "variables": { + "island_id": "ait-testnet-island", + "supported_chains": "ait-testnet", + "CHAIN_ID": "ait-testnet", + "auto_sync_enabled": "true", + "default_peer_rpc_url": "http://aitbc:8006", + "SYNC_SOURCE_HOST": "aitbc", + "SYNC_SOURCE_PORT": "8006", + "SYNC_CHAIN_ID": "ait-testnet" + } + }, + "step_3_networking": { + "description": "Ensure network connectivity", + "requirements": [ + "Port 7070 open for P2P communication", + "Port 8006 open for RPC access", + "Connectivity to aitbc (10.1.223.93) and aitbc1 (10.1.223.40)" + ] + }, + "step_4_start_services": { + "description": "Start blockchain services", + "commands": [ + "systemctl start aitbc-blockchain-node.service", + "systemctl start aitbc-blockchain-rpc.service" + ] + }, + "step_5_verify": { + "description": "Verify successful join", + "commands": [ + "curl -s http://localhost:8006/rpc/head", + "sqlite3 /var/lib/aitbc/data/ait-testnet/chain.db 'SELECT MAX(height) FROM block'" + ], + "expected_result": "Block height should be increasing as blocks are synced from hub (aitbc1)" + } + }, + "endpoints": { + "rpc": { + "hub": "http://aitbc1:8006", + "backup": "http://aitbc:8006" + }, + "p2p": { + "peers": ["aitbc:7070", "aitbc1:7070"] + } + }, + "configuration_templates": { + "follower_node": { + "node.env": { + "NODE_ID": "your-node-id", + "p2p_node_id": "node-unique-uuid", + "p2p_bind_host": "0.0.0.0", + "p2p_bind_port": "7070", + "proposer_id": "ait1your-proposer-id", + "p2p_peers": "aitbc:7070,aitbc1:7070", + "trusted_proposers": "", + "block_production_chains": "", + "enable_block_production": "false" + }, + "blockchain.env": { + "auto_sync_enabled": "true", + "island_id": "ait-testnet-island", + "supported_chains": "ait-testnet", + "default_peer_rpc_url": "http://aitbc:8006", + "SYNC_SOURCE_HOST": "aitbc", + "SYNC_SOURCE_PORT": "8006", + "SYNC_CHAIN_ID": "ait-testnet" + } + } + }, + "troubleshooting": { + "gap_detected_errors": { + "cause": "Node is receiving blocks but missing intermediate blocks", + "solution": "Ensure auto_sync_enabled=true and default_peer_rpc_url points to active hub (aitbc1 for testnet)" + }, + "fork_detection": { + "cause": "Multiple nodes with same proposer_id producing blocks", + "solution": "Ensure enable_block_production=false on follower nodes (only aitbc1 should produce on testnet)" + }, + "p2p_connection_failed": { + "cause": "Cannot connect to P2P peers", + "solution": "Verify p2p_bind_host and p2p_bind_port are set correctly, check firewall rules" + } + }, + "_meta": { + "format_version": "1.0", + "updated_at": "2026-05-19T15:50:00Z", + "note": "Testnet hub is aitbc1, not aitbc. Ensure sync source points to correct hub." + } +} diff --git a/website/agent/openapi.json b/website/agent/openapi.json new file mode 100644 index 00000000..4ee1fb87 --- /dev/null +++ b/website/agent/openapi.json @@ -0,0 +1,418 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "AITBC Agent API", + "description": "Machine-readable API for AITBC blockchain agents. Provides endpoints for discovery, island management, and chain operations.", + "version": "1.0.0", + "contact": { + "name": "AITBC Network", + "url": "https://aitbc.bubuit.net" + } + }, + "servers": [ + { + "url": "http://localhost:8006", + "description": "Local node RPC" + }, + { + "url": "http://aitbc:8006", + "description": "AITBC hub node" + }, + { + "url": "http://aitbc1:8006", + "description": "AITBC1 node" + } + ], + "paths": { + "/agent/discovery.json": { + "get": { + "summary": "Network Discovery", + "description": "Returns complete network information for agent auto-discovery", + "operationId": "getDiscovery", + "responses": { + "200": { + "description": "Network discovery information", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DiscoveryResponse" + } + } + } + } + } + } + }, + "/agent/islands.json": { + "get": { + "summary": "List Islands", + "description": "Returns all islands in the network with current status", + "operationId": "getIslands", + "responses": { + "200": { + "description": "List of islands", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IslandsResponse" + } + } + } + } + } + } + }, + "/agent/chains.json": { + "get": { + "summary": "List Chains", + "description": "Returns all chains in the network with configuration", + "operationId": "getChains", + "responses": { + "200": { + "description": "List of chains", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChainsResponse" + } + } + } + } + } + } + }, + "/agent/join/{chain_id}.json": { + "get": { + "summary": "Join Information", + "description": "Returns detailed instructions for joining a specific chain", + "operationId": "getJoinInfo", + "parameters": [ + { + "name": "chain_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": ["ait-mainnet", "ait-testnet"] + }, + "description": "Chain ID to get join information for" + } + ], + "responses": { + "200": { + "description": "Join instructions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JoinInfoResponse" + } + } + } + } + } + } + }, + "/rpc/head": { + "get": { + "summary": "Get Chain Head", + "description": "Returns the current head block of the chain", + "operationId": "getHead", + "responses": { + "200": { + "description": "Current head block", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeadResponse" + } + } + } + } + } + } + }, + "/rpc/info": { + "get": { + "summary": "Get Chain Info", + "description": "Returns general information about the blockchain", + "operationId": "getInfo", + "responses": { + "200": { + "description": "Chain information", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InfoResponse" + } + } + } + } + } + } + }, + "/rpc/islands": { + "get": { + "summary": "List Island Memberships", + "description": "Returns island memberships for this node", + "operationId": "listIslands", + "responses": { + "200": { + "description": "Island memberships", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IslandMembershipsResponse" + } + } + } + } + } + } + }, + "/rpc/islands/{island_id}": { + "get": { + "summary": "Get Island Details", + "description": "Returns detailed information about a specific island", + "operationId": "getIsland", + "parameters": [ + { + "name": "island_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Island ID" + } + ], + "responses": { + "200": { + "description": "Island details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IslandDetailResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "DiscoveryResponse": { + "type": "object", + "properties": { + "network": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "version": { "type": "string" }, + "apis": { + "type": "object", + "properties": { + "rpc": { + "type": "object", + "properties": { + "url": { "type": "string" }, + "documentation": { "type": "string" }, + "version": { "type": "string" } + } + }, + "agent": { + "type": "object", + "properties": { + "discovery": { "type": "string" }, + "islands": { "type": "string" }, + "chains": { "type": "string" }, + "health": { "type": "string" }, + "join": { "type": "string" } + } + } + } + }, + "islands": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IslandSummary" + } + } + } + }, + "this_node": { + "type": "object", + "properties": { + "node_id": { "type": "string" }, + "role": { "type": "string" }, + "is_hub": { "type": "boolean" }, + "block_production": { "type": "boolean" }, + "chains": { + "type": "array", + "items": { "type": "string" } + } + } + } + } + }, + "IslandSummary": { + "type": "object", + "properties": { + "island_id": { "type": "string" }, + "name": { "type": "string" }, + "chain_id": { "type": "string" }, + "status": { "type": "string", "enum": ["active", "inactive"] }, + "endpoints": { + "type": "object", + "properties": { + "rpc": { + "type": "array", + "items": { "type": "string" } + }, + "p2p": { + "type": "array", + "items": { "type": "string" } + } + } + } + } + }, + "IslandsResponse": { + "type": "object", + "properties": { + "islands": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IslandDetail" + } + }, + "_meta": { + "type": "object" + } + } + }, + "IslandDetail": { + "type": "object", + "properties": { + "island_id": { "type": "string" }, + "island_name": { "type": "string" }, + "chain_id": { "type": "string" }, + "status": { "type": "string" }, + "role": { "type": "string" }, + "chain_info": { + "type": "object", + "properties": { + "block_time": { "type": "integer" }, + "consensus": { "type": "string" }, + "network_id": { "type": "integer" } + } + }, + "endpoints": { + "type": "object" + }, + "this_node": { + "type": "object" + } + } + }, + "ChainsResponse": { + "type": "object", + "properties": { + "chains": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChainInfo" + } + } + } + }, + "ChainInfo": { + "type": "object", + "properties": { + "chain_id": { "type": "string" }, + "name": { "type": "string" }, + "island_id": { "type": "string" }, + "type": { "type": "string" }, + "config": { + "type": "object", + "properties": { + "block_time": { "type": "integer" }, + "consensus": { "type": "string" } + } + }, + "join": { + "type": "object" + } + } + }, + "JoinInfoResponse": { + "type": "object", + "properties": { + "chain_id": { "type": "string" }, + "island_id": { "type": "string" }, + "how_to_join": { + "type": "object" + }, + "endpoints": { + "type": "object" + }, + "configuration_templates": { + "type": "object" + } + } + }, + "HeadResponse": { + "type": "object", + "properties": { + "height": { "type": "integer" }, + "hash": { "type": "string" }, + "timestamp": { "type": "string" }, + "tx_count": { "type": "integer" } + } + }, + "InfoResponse": { + "type": "object", + "properties": { + "chain_id": { "type": "string" }, + "network_id": { "type": "integer" }, + "block_time": { "type": "integer" }, + "version": { "type": "string" } + } + }, + "IslandMembershipsResponse": { + "type": "object", + "properties": { + "islands": { + "type": "array", + "items": { + "type": "object", + "properties": { + "island_id": { "type": "string" }, + "island_name": { "type": "string" }, + "chain_id": { "type": "string" }, + "status": { "type": "string" }, + "role": { "type": "string" }, + "peer_count": { "type": "integer" }, + "is_hub": { "type": "boolean" } + } + } + } + } + }, + "IslandDetailResponse": { + "type": "object", + "properties": { + "island_id": { "type": "string" }, + "island_name": { "type": "string" }, + "chain_id": { "type": "string" }, + "status": { "type": "string" }, + "role": { "type": "string" }, + "peer_count": { "type": "integer" }, + "is_hub": { "type": "boolean" }, + "joined_at": { "type": "number" } + } + } + } + } +} diff --git a/website/aitbc-proxy.conf b/website/aitbc-proxy.conf index e3a31757..0b5b6b41 100644 --- a/website/aitbc-proxy.conf +++ b/website/aitbc-proxy.conf @@ -27,6 +27,46 @@ server { proxy_buffers 8 4k; } + # Agent API - CORS enabled for cross-origin agent access + location /agent/ { + proxy_pass http://10.1.223.93/agent/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # CORS headers for agent access + add_header Access-Control-Allow-Origin * always; + add_header Access-Control-Allow-Methods "GET, OPTIONS" always; + add_header Access-Control-Allow-Headers "Content-Type, Accept" always; + add_header Access-Control-Max-Age 86400 always; + + # Handle preflight requests + if ($request_method = 'OPTIONS') { + return 204; + } + } + + # RPC endpoints - CORS enabled + location /rpc/ { + proxy_pass http://10.1.223.93:8006/rpc/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # CORS headers for agent access + add_header Access-Control-Allow-Origin * always; + add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always; + add_header Access-Control-Allow-Headers "Content-Type, Accept, Authorization" always; + add_header Access-Control-Max-Age 86400 always; + + # Handle preflight requests + if ($request_method = 'OPTIONS') { + return 204; + } + } + # Health check endpoint location /health { proxy_pass http://10.1.223.93/health; diff --git a/website/index.html b/website/index.html index d958d4d8..3a2469ef 100644 --- a/website/index.html +++ b/website/index.html @@ -16,6 +16,39 @@ + + + + @@ -50,6 +83,7 @@ Marketplace Exchange Docs + Agent API