{"id":50532797,"url":"https://github.com/faremeter/marketplace","last_synced_at":"2026-06-03T15:01:33.548Z","repository":{"id":352264734,"uuid":"1198811272","full_name":"faremeter/marketplace","owner":"faremeter","description":"The Marketplace is a global registry where API providers list their services, set per-request pricing, and get discovered by AI agents.","archived":false,"fork":false,"pushed_at":"2026-05-22T18:25:50.000Z","size":2004,"stargazers_count":3,"open_issues_count":2,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T21:54:25.022Z","etag":null,"topics":["machine-payments","openapi","x402"],"latest_commit_sha":null,"homepage":"https://api.corbits.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/faremeter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-01T19:29:56.000Z","updated_at":"2026-05-22T18:25:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/faremeter/marketplace","commit_stats":null,"previous_names":["faremeter/marketplace"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/faremeter/marketplace","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faremeter%2Fmarketplace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faremeter%2Fmarketplace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faremeter%2Fmarketplace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faremeter%2Fmarketplace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/faremeter","download_url":"https://codeload.github.com/faremeter/marketplace/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faremeter%2Fmarketplace/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33870026,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["machine-payments","openapi","x402"],"created_at":"2026-06-03T15:01:32.469Z","updated_at":"2026-06-03T15:01:33.538Z","avatar_url":"https://github.com/faremeter.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Faremeter Marketplace\n\nA multi-tenant payment proxy for the [x402 protocol](https://www.x402.org/). Publishers register their APIs, set pricing, and the proxy handles payment validation, settlement, and request routing. Clients pay per-request using on-chain escrow accounts.\n\n## Architecture\n\n```\n                          Control Plane\n                        (API + UI + Discovery)\n                               |\n                          PostgreSQL (RDS)\n                               |\n                     WireGuard mesh (wg1)\n                               |\n                           API Node\n                    (nginx + gateway sidecar)\n                      /        |         \\\n              Facilitator   Sidecar    Backend API\n           (settles payment) (captures) (proxied request)\n                      \\        |         /\n                          Client\n```\n\n- **API Nodes** handle client requests: TLS termination, subdomain routing, payment validation (via the facilitator), and proxying to the publisher's backend. Gateway-mode tenants use the `@faremeter/gateway-nginx` SDK to generate nginx routing config and run a sidecar process that captures settlement metadata. Legacy tenants continue to use the hand-written Lua access module.\n- **Control Plane** manages tenants, endpoints, pricing, and wallets. Pushes config to API nodes over the WireGuard mesh. Does not handle client traffic.\n- **Discovery Service** runs on the control plane and provides a searchable registry of published APIs\n\n**Stacks:**\n\n- **VPC Stack** -- AWS networking (subnets, security groups, NAT)\n- **Database Stack** -- PostgreSQL RDS (Multi-AZ, encrypted)\n- **Control-Plane Stack** -- EC2 instance running the control-plane API, UI, and discovery service\n- **API Node Stack** -- EC2 instance(s) running nginx for request proxying, with a gateway sidecar for payment capture and settlement on gateway-mode tenants\n\nNodes communicate over a WireGuard mesh network. wg1 (10.12.0.0/24) handles internal control-plane to API node traffic. wg0 (10.11.0.0/24) ships logs to an external Grafana/Loki endpoint.\n\n## Prerequisites\n\n- **AWS account** with a Route53 hosted zone for your domain\n- **AWS CLI** configured with credentials (`aws configure`)\n- **Pulumi CLI** ([install](https://www.pulumi.com/docs/install/))\n- **Node.js 22.x** ([install](https://nodejs.org/))\n- **pnpm** (`npm install -g pnpm`)\n- **WireGuard tools** (`apt install wireguard-tools` or `brew install wireguard-tools`)\n- **Grafana/Loki endpoint** for centralized logging via wg0 ([Grafana Cloud](https://grafana.com/products/cloud/))\n\n### Clone and initialize\n\n```bash\ngit clone \u003crepo-url\u003e\ncd faremeter-marketplace\ngit submodule update --init\npnpm install\n```\n\nThe `infra-toolbox` submodule (`github.com/faremeter/infra-toolbox`) provides shared build scripts.\n\nIf `make` fails with `./bin/check-env: No such file or directory`, the submodule was not initialized. Run `git submodule update --init` again and verify `ls infra-toolbox/` shows files.\n\n## Local Docker Compose\n\nFor full local development, this repo now ships a real Docker Compose stack for:\n\n- PostgreSQL\n- control-plane API\n- discovery API\n- control-plane UI\n- two local OpenResty API nodes\n- marketplace sidecar wrapper\n- real Faremeter sidecar package\n- real facilitator app from the selected Faremeter checkout\n- local publisher mock\n\n### Requirements\n\n- Docker Desktop or Docker Engine with Compose\n- The Faremeter checkout available at `../faremeter`, or an explicit\n  `FAREMETER_REPO_PATH`\n- A Solana keypair or explicit Solana address for the facilitator/service wallet\n\nThe Compose file mounts this checkout as `/workspace/marketplace`, so the local\nmarketplace directory can have any name. It also mounts the selected Faremeter\ncheckout as `/workspace/faremeter`.\n\nBy default, Compose looks for Faremeter one directory above this checkout:\n\n```text\n${PWD}/../faremeter\n```\n\nIf Faremeter lives somewhere else, pass an explicit host path:\n\n```bash\nFAREMETER_REPO_PATH=/absolute/path/to/faremeter make local-up\n```\n\nBefore starting the stack, create or provide the local payment keypair:\n\n```bash\nmkdir -p keypairs\nsolana-keygen new --outfile keypairs/facilitator.json\n```\n\n`LOCAL_SERVICE_SOLANA_ADDRESS` is optional. If unset, the seed script derives\nthe receiver wallet address from `keypairs/facilitator.json`.\n\nThe local stack supports both Solana devnet and mainnet-beta. Devnet is the\ndefault when `SOLANA_NETWORK` is unset:\n\n```text\nSOLANA_NETWORK=devnet\nSOLANA_RPC_URL=https://api.devnet.solana.com\n```\n\nRun against mainnet-beta by setting both the network and RPC URL:\n\n```bash\nSOLANA_NETWORK=mainnet-beta \\\nSOLANA_RPC_URL=https://api.mainnet-beta.solana.com \\\nmake local-up\n```\n\nUse `mainnet-beta`, not `mainnet`, because that is the cluster name accepted by\nthe Faremeter Solana packages. The seed script and local check use the selected\nnetwork to look up the correct USDC mint and x402 network IDs.\n\n### Start the stack\n\n```bash\ndocker compose up --build -d\n```\n\nOr via make:\n\n```bash\nmake local-up\n```\n\nCompose will:\n\n1. install workspace dependencies for both `faremeter` and `marketplace`\n2. start Postgres and the app services\n3. run the real facilitator against the selected Solana network\n4. seed a local admin user, two local nodes, a demo tenant, and a demo endpoint\n5. sync the generated tenant config into both local API nodes\n\n### Local URLs\n\n- Control plane API: `http://localhost:11337`\n- Control plane UI: `http://localhost:11338`\n- Discovery: `http://localhost:11339`\n- API node proxy A: `http://localhost:18080`\n- API node proxy B: `http://localhost:18081`\n- Demo proxy host A: `http://demo-api.local.proxy.localhost:18080/v1/chat/completions`\n- Demo proxy host B: `http://demo-api.local.proxy.localhost:18081/v1/chat/completions`\n\nThese host ports are configurable if they collide with an existing local stack:\n\n```bash\nMARKETPLACE_CONTROL_PLANE_PORT=1337 \\\nMARKETPLACE_UI_PORT=1338 \\\nMARKETPLACE_DISCOVERY_PORT=1339 \\\nMARKETPLACE_PROXY_PORT=8080 \\\nMARKETPLACE_PROXY_PORT_B=8081 \\\nMARKETPLACE_POSTGRES_PORT=5433 \\\ndocker compose up --build -d\n```\n\n### Local credentials\n\n- Email: `admin@local.faremeter.test`\n- Password: `localdev123`\n\n### Useful commands\n\n```bash\nmake local-down\nmake local-logs\nmake local-seed\nmake local-check\n```\n\n`make local-check` verifies the seeded paid proxy route returns `402` with USDC\npayment requirements for the selected Solana network through both local proxy\nnodes, creates a free endpoint through the control plane, routes that endpoint\nthrough both nodes, and verifies control-plane transaction and analytics\nrecords.\n\n## Generate Secrets\n\nBefore deploying, generate all the secrets you will need.\n\n### WireGuard keys\n\nGenerate one keypair per node. For a single-node setup (1 control-plane + 1 API node):\n\n```bash\n# Control-plane node\nwg genkey | tee cp-privatekey | wg pubkey \u003e cp-publickey\n\n# API node\nwg genkey | tee api-privatekey | wg pubkey \u003e api-publickey\n```\n\n### Database password\n\n```bash\nopenssl rand -base64 32\n```\n\n### Wallet encryption key\n\n```bash\nopenssl rand -hex 32\n```\n\nThis produces a 64-character hex string used for encrypting wallet data at rest.\n\n### Find your AMI\n\nFind the latest Debian 13 (Trixie) x86_64 AMI for your region:\n\n```bash\naws ec2 describe-images \\\n  --owners 136693071363 \\\n  --filters \"Name=name,Values=debian-13-amd64-*\" \"Name=state,Values=available\" \\\n  --query 'sort_by(Images, \u0026CreationDate)[-1].ImageId' \\\n  --output text \\\n  --region us-west-2\n```\n\nReplace `us-west-2` with your region. Save the AMI ID for the stack configs.\n\n## Deploy\n\nDeploy the stacks in order. Each stack depends on the outputs of the previous one.\n\n### Step 1: VPC\n\n```bash\ncd apps/vpc-stack\npulumi stack init test-1\npulumi config set aws:region us-west-2\npulumi up\n```\n\n### Step 2: Database\n\n```bash\ncd apps/database-stack\npulumi stack init test-1\npulumi config set aws:region us-west-2\npulumi config set rds:dbName control_plane\npulumi config set rds:masterUsername rds_admin\npulumi config set --secret rds:masterPassword \"\u003cyour-db-password\u003e\"\npulumi config set rds:instanceClass db.t3.medium\npulumi config set rds:allocatedStorage \"20\"\npulumi config set rds:vpcStackRef \"\u003cyour-pulumi-org\u003e/vpc/test-1\"\npulumi up\n```\n\nAfter the database is up, create the application user. SSH into the control-plane node (deployed in the next step) and run:\n\n```sql\nCREATE USER control_plane_1 WITH PASSWORD '\u003cyour-app-password\u003e';\nGRANT CONNECT ON DATABASE control_plane TO control_plane_1;\nGRANT ALL ON SCHEMA public TO control_plane_1;\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO control_plane_1;\nALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO control_plane_1;\nGRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO control_plane_1;\nALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO control_plane_1;\n```\n\nOr use the provided script after the control-plane stack is deployed:\n\n```bash\ncd apps/database-stack\n./scripts/create-app-user.sh\n```\n\n### Step 3: Control-Plane\n\n```bash\ncd apps/control-plane-stack\npulumi stack init test-1\n```\n\nConfigure all required values:\n\n```bash\n# Region and AMI\npulumi config set aws:region us-west-2\npulumi config set node:ami \u003cyour-ami-id\u003e\n\n# DNS\npulumi config set dns:rootZoneName \u003cyour-domain.com\u003e\npulumi config set dns:alternateZoneNames '[]'  # or '[\"alt-domain.io\"]'\n\n# WireGuard (wg0 -- logging network)\npulumi config set --secret wireguard:privateKey \"$(cat cp-privatekey)\"\npulumi config set wireguard:publicKey \"$(cat cp-publickey)\"\npulumi config set wireguard:address 10.11.0.1\n\n# WireGuard (wg1 -- internal mesh)\npulumi config set wireguard:multiAddress 10.12.0.1\n\n# Database (use RDS endpoint from Step 2 output)\npulumi config set database:host \u003crds-endpoint\u003e\npulumi config set database:port \"5432\"\npulumi config set database:name control_plane\npulumi config set database:user control_plane_1\npulumi config set --secret database:password \"\u003cyour-app-password\u003e\"\n\n# Wallet encryption\npulumi config set --secret wallet:encryptionKey \"\u003cyour-64-char-hex-key\u003e\"\n\n# Service ports\npulumi config set controlPlane:port \"1337\"\npulumi config set ui:port \"1338\"\npulumi config set controlPlane:nodeId \"1\"\n\n# VPC reference\npulumi config set vpc:stackRef \"\u003cyour-pulumi-org\u003e/vpc/test-1\"\n\n# Optional integrations\n# pulumi config set --secret faremeterDashboard:apiKey \"\u003cyour-key\u003e\"\n# pulumi config set --secret attio:apiKey \"\u003cyour-key\u003e\"\n# pulumi config set attio:listId \"\u003cyour-list-id\u003e\"\n```\n\nDeploy:\n\n```bash\npulumi up\n```\n\n### Step 4: Deploy Applications\n\nAfter the control-plane EC2 instance is running, deploy the applications:\n\n```bash\n# Control-plane API (runs migrations on first deploy)\ncd apps/control-plane \u0026\u0026 PULUMI_STACKS=\"test-1\" ./deploy.sh \u0026\u0026 cd ../..\n\n# Control-plane UI\ncd apps/control-plane-ui \u0026\u0026 PULUMI_STACKS=\"test-1\" ./deploy.sh \u0026\u0026 cd ../..\n\n# Discovery service\ncd apps/discovery \u0026\u0026 PULUMI_STACKS=\"test-1\" ./deploy.sh \u0026\u0026 cd ../..\n```\n\n### Step 5: API Node\n\n```bash\ncd apps/api-node-stack\npulumi stack init test-1\n```\n\nConfigure:\n\n```bash\n# Region and AMI\npulumi config set aws:region us-west-2\npulumi config set node:ami \u003cyour-ami-id\u003e\n\n# Control-plane connection (wg1 IP and port)\npulumi config set controlPlane:addresses \"10.12.0.1:1337\"\n\n# WireGuard (wg0 -- logging)\npulumi config set --secret wireguard:privateKey \"$(cat api-privatekey)\"\npulumi config set wireguard:publicKey \"$(cat api-publickey)\"\npulumi config set wireguard:address 10.11.0.2\n\n# WireGuard (wg1 -- internal mesh)\npulumi config set wireguard:multiAddress 10.12.0.2\n\n# Control-plane WireGuard peers (pubkey:wg1-ip:public-ip)\n# Get the control-plane public IP:\n#   cd apps/control-plane-stack \u0026\u0026 pulumi stack select test-1\n#   pulumi stack output nodes --show-secrets | jq -r '.[0].connection.host'\npulumi config set controlPlane:wgPeers \"\u003ccp-pubkey\u003e:10.12.0.1:\u003ccp-public-ip\u003e\"\n\n# DNS and identity\npulumi config set dns:rootZoneName \u003cyour-domain.com\u003e\npulumi config set api-node:nodeName api-node-1\npulumi config set api-node:nodeId \"1\"\n```\n\nDeploy:\n\n```bash\npulumi up\n```\n\n### Step 6: Verify\n\nSSH into the control-plane node:\n\n```bash\ncd apps/control-plane-stack\npulumi stack select test-1\nssh -i \u003ckey\u003e admin@\u003ccontrol-plane-ip\u003e\n```\n\nCheck services:\n\n```bash\nsudo systemctl status control-plane\nsudo systemctl status discovery\nsudo systemctl status nginx\nsudo wg show\n```\n\nSSH into the API node:\n\n```bash\ncd apps/api-node-stack\npulumi stack select test-1\nssh -i \u003ckey\u003e admin@\u003capi-node-ip\u003e\n```\n\nCheck services:\n\n```bash\nsudo systemctl status nginx\nsudo systemctl status faremeter-sidecar\nsudo wg show\n```\n\nTest the control-plane health endpoint:\n\n```bash\ncurl https://api.\u003cyour-domain.com\u003e/health\n```\n\nTest the API node directly (use the API node's public IP):\n\n```bash\ncurl -k https://\u003capi-node-ip\u003e/\n```\n\nThe API node should return a 400 or 404 (no tenant configured yet). A connection refused or timeout means nginx isn't running or the security group is blocking port 443.\n\n## High Availability (Optional)\n\nTo add a second control-plane node:\n\n```bash\ncd apps/control-plane-stack\npulumi stack init test-2\n# Same config as test-1, but change:\n#   wireguard:address -\u003e 10.11.0.3\n#   wireguard:multiAddress -\u003e 10.12.0.20\n#   controlPlane:nodeId -\u003e \"2\"\n#   database:user -\u003e control_plane_2 (create this user first)\npulumi up\n```\n\nTo add a second API node:\n\n```bash\ncd apps/api-node-stack\npulumi stack init test-2\n# Same config as test-1, but change:\n#   wireguard:address -\u003e 10.11.0.4\n#   wireguard:multiAddress -\u003e 10.12.0.3\n#   api-node:nodeName -\u003e api-node-2\n#   api-node:nodeId -\u003e \"2\"\n#   controlPlane:wgPeers -\u003e include both control-plane nodes\npulumi up\n```\n\nRoute53 weighted routing distributes traffic across control-plane nodes automatically.\n\n## Troubleshooting\n\n**`./bin/check-env: No such file or directory`**\nRun `git submodule update --init`.\n\n**WireGuard peers not connecting**\nCheck that the public IPs in `controlPlane:wgPeers` are correct. Verify UDP port 51821 is open in the security group. Run `sudo wg show` on both nodes to see handshake status.\n\n**RDS connection refused**\nThe database security group only allows connections from the control-plane security group. Verify both are in the same VPC and the security group rules are correct.\n\n**Certificate provisioning fails**\nCertbot uses Route53 DNS challenge. Verify the Route53 zone ID is correct and the EC2 instance role has `route53:ChangeResourceRecordSets` permission.\n\n**Deploy script fails with \"stack not found\"**\nSet `PULUMI_STACKS` to match your stack name: `PULUMI_STACKS=\"test-1\" ./deploy.sh`\n\n## Configuration Reference\n\nSee `Pulumi.example.yaml` in each stack directory for all available configuration keys with descriptions.\n\n## License\n\n[LGPLv3](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaremeter%2Fmarketplace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffaremeter%2Fmarketplace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaremeter%2Fmarketplace/lists"}