Odel
apievangelist aws

apievangelist aws

@api-evangelistJavaScriptUpdated 1mo ago

Sixteen years of API research as MCP tools — stories, areas, governance blocks & services

Server endpointStreamable HTTPNo authProbed

This is the third-party server itself — Odel doesn't run it. Hitting this URL directly talks straight to the upstream server with no auth or proxying. Connect through Odel to front it with managed auth.

apievangelist-aws

AWS infrastructure for the API Evangelist network. Most websites stay static on GitHub Pages; this repo holds two independent stacks:

ApiEvangelistStack — the first-class API + MCP server:

  • api.apievangelist.com — REST API (/v1) over the whole research network: 5,100+ stories, 77 topic areas, 2,400+ governance building blocks (guidance, rules, policies, standards, strategies, schema, properties, experiences, lifecycle), conversations, papers, services, vocabulary, newsletters, and feeds.
  • mcp.apievangelist.com — MCP server (Streamable HTTP), 17 tools including the concierge layer: get_overview, guide_topic, find_services.

BlogsSiteStack — static hosting for blogs.apievangelist.com. The blogs site (api-evangelist/blogs, ~111k aggregated posts) outgrew GitHub Pages, whose hosted runner OOMs building it. It is built locally and aws s3 sync'd to an S3 bucket fronted by CloudFront (OAC, *-clean-URL Function, blogs.apievangelist.com ACM cert). See Blogs site below.

Docs: https://developer.apievangelist.com · OpenAPI: https://developer.apievangelist.com/apis/apievangelist-v1-openapi.yml · Registry: com.apievangelist/api-evangelist.

Architecture

One CDK stack (lib/apievangelist-stack.js), mirroring apis-io-aws:

  • CloudFront distribution with both domains → one API Gateway (/v1* → search Lambda, /mcp* → MCP Lambda); a CloudFront Function routes by Host header. Caching disabled (every request is metered); x-origin-secret locks API GW to the distribution; WAF rate-limits abusive IPs.
  • Search Lambda (Node 20, MiniSearch): loads catalog.json (~13 MB, 15k docs) from S3 at cold start; full bodies live as content/<type>/<slug>.md objects fetched on include=content. Ranking boosts tunable via RANK_BOOST_* env vars.
  • Authorizer: x-api-key → DynamoDB tier; keyless callers get internal if the Origin is apievangelist.com or any *.apievangelist.com site, else free. Usage plans: free (5 r/s, 1k/day), internal (100 r/s, 5M/day), owner (200 r/s, 5M/day).
  • Analytics Lambda: daily Logs Insights aggregation of evt:apisearch lines → demand reports in S3, served owner-only at /v1/searches.

DNS is Cloudflare, grey-cloud (DNS-only) CNAMEs to CloudFront — proxied records would route through the network's worker and break the API. scripts/cloudflare-dns.sh hardcodes proxied:false.

Operating

npm test                      # 31 handler tests (search, authorizer, MCP)
npm run build:catalog         # rebuild dist/catalog.json + dist/content/ from local repos
npm run deploy:catalog        # build + upload to S3 (Lambda picks up on next cold start)
bash scripts/deploy.sh diff   # CDK diff (CERT_ARN + .env AE_* keys required)
bash scripts/deploy.sh deploy # CDK deploy

Secrets in /Users/kinlane/GitHub/.env: AE_FREE_KEY, AE_INTERNAL_KEY, AE_OWNER_KEY, AE_MCP_ORIGIN_SECRET. Registry runbook: ../admin/mcp-registry-apievangelist.md.

Refresh cadence: re-run npm run deploy:catalog whenever network content changes (posts, store items, areas) so the API reflects the sites.

Blogs site

BlogsSiteStack serves blogs.apievangelist.com as a static site on S3 + CloudFront. The Jekyll build (~111k posts, ~5 GB, ~16 min) is done locally — the github-pages runner OOMs on it, and github-pages' Jekyll 3.10 also ignores render_with_liquid. Build under Homebrew Ruby (/opt/homebrew/opt/ruby/bin); the chruby ruby on this machine links a stale libruby and can't load native gems.

# One-time infra (S3 bucket + CloudFront + OAC + clean-URL Function). The cert is a
# DNS-validated ACM cert for blogs.apievangelist.com (us-east-1); *.apievangelist.com is a
# Cloudflare origin cert (IMPORTED) that CloudFront rejects, so a dedicated one is used.
npx cdk deploy BlogsSiteStack --require-approval never \
  -c blogsDomain=blogs.apievangelist.com \
  -c blogsCertArn=arn:aws:acm:us-east-1:007596076917:certificate/9d6344ac-7c6a-48f3-8d35-33dc01e7a411

# Build + sync + invalidate (BUCKET/DIST from the stack outputs).
BUCKET=blogs.apievangelist.com DIST=EDFO4PRAM0OC3 ./scripts/deploy-blogs.sh
SKIP_BUILD=1 BUCKET=... DIST=... ./scripts/deploy-blogs.sh   # sync an existing _site

# DNS: point blogs.apievangelist.com at the distribution (Cloudflare, DNS-only / grey cloud).
DRY_RUN=1 TARGET=d5e4ib1wlxlas.cloudfront.net ./scripts/cloudflare-dns-blogs.sh   # preview
TARGET=d5e4ib1wlxlas.cloudfront.net ./scripts/cloudflare-dns-blogs.sh             # apply

Stack outputs: bucket blogs.apievangelist.com, distribution EDFO4PRAM0OC3 (d5e4ib1wlxlas.cloudfront.net).