Merge branch 'main' into claude/celebrate-cece-01U7rFKSt1xaRDcyWAqd1ijj

This commit is contained in:
Alexa Amundson
2025-11-17 21:27:11 -06:00
committed by GitHub
19 changed files with 6391 additions and 101 deletions

255
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,255 @@
# Dependabot Configuration
# ========================
#
# Dependabot automatically creates pull requests to update dependencies.
# This keeps your project secure and up-to-date.
#
# Documentation: https://docs.github.com/en/code-security/dependabot
#
# What Dependabot does:
# --------------------
# - Checks for outdated dependencies daily/weekly/monthly
# - Creates PRs to update them
# - Groups related updates together
# - Respects semantic versioning
# - Works with GitHub Actions to run tests
#
# How to use:
# ----------
# This file is already in the correct location (.github/dependabot.yml)
# Just commit it and Dependabot will start working automatically!
version: 2
updates:
# ========================================
# Backend Python Dependencies
# ========================================
- package-ecosystem: "pip"
directory: "/backend"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
open-pull-requests-limit: 5
reviewers:
- "alexa-amundson"
labels:
- "dependencies"
- "backend"
- "python"
commit-message:
prefix: "chore(deps)"
include: "scope"
# Group minor and patch updates together
groups:
fastapi-stack:
patterns:
- "fastapi*"
- "uvicorn*"
- "pydantic*"
- "starlette*"
database-stack:
patterns:
- "sqlalchemy*"
- "alembic*"
- "psycopg*"
- "asyncpg*"
testing-stack:
patterns:
- "pytest*"
- "coverage*"
- "httpx*"
# ========================================
# Frontend NPM Dependencies (if exists)
# ========================================
# Uncomment if you have a package.json for frontend
# - package-ecosystem: "npm"
# directory: "/frontend"
# schedule:
# interval: "weekly"
# day: "monday"
# time: "06:00"
# open-pull-requests-limit: 5
# reviewers:
# - "alexa-amundson"
# labels:
# - "dependencies"
# - "frontend"
# - "javascript"
# commit-message:
# prefix: "chore(deps)"
# groups:
# dev-dependencies:
# dependency-type: "development"
# ========================================
# Python SDK Dependencies
# ========================================
- package-ecosystem: "pip"
directory: "/sdk/python"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
open-pull-requests-limit: 3
labels:
- "dependencies"
- "sdk"
- "python"
commit-message:
prefix: "chore(sdk)"
# ========================================
# TypeScript SDK Dependencies
# ========================================
- package-ecosystem: "npm"
directory: "/sdk/typescript"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
open-pull-requests-limit: 3
labels:
- "dependencies"
- "sdk"
- "typescript"
commit-message:
prefix: "chore(sdk)"
# ========================================
# GitHub Actions Workflows
# ========================================
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
open-pull-requests-limit: 3
labels:
- "dependencies"
- "github-actions"
- "ci-cd"
commit-message:
prefix: "chore(ci)"
# ========================================
# Docker (if using Dockerfile)
# ========================================
- package-ecosystem: "docker"
directory: "/backend"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
open-pull-requests-limit: 2
labels:
- "dependencies"
- "docker"
commit-message:
prefix: "chore(docker)"
# ========================================
# Configuration Options Explained
# ========================================
#
# package-ecosystem:
# - pip: Python (requirements.txt, setup.py)
# - npm: JavaScript/TypeScript (package.json)
# - github-actions: GitHub Actions workflows
# - docker: Dockerfiles
# - bundler: Ruby (Gemfile)
# - composer: PHP (composer.json)
# - cargo: Rust (Cargo.toml)
#
# directory:
# - Path where the manifest file is located
# - Use "/" for root, "/backend" for subdirectory
#
# schedule.interval:
# - daily: Check every day
# - weekly: Check once a week
# - monthly: Check once a month
#
# open-pull-requests-limit:
# - Max number of open dependency PRs at once
# - Prevents PR spam
# - Recommended: 3-10
#
# reviewers:
# - GitHub usernames to request review from
# - Helps ensure updates are reviewed
#
# labels:
# - Labels to add to PRs
# - Helps organize and filter
#
# groups:
# - Group related dependencies into single PR
# - Reduces PR noise
# - Example: Update all pytest-related packages together
#
# commit-message.prefix:
# - Prefix for commit messages
# - Common: "chore(deps)", "build", "deps"
#
# ========================================
# Best Practices
# ========================================
#
# 1. Review PRs before merging:
# - Check changelogs
# - Run tests
# - Look for breaking changes
#
# 2. Use groups to reduce PR noise:
# - Group related packages
# - Group by type (dev vs prod)
#
# 3. Set appropriate schedule:
# - Weekly for active projects
# - Monthly for stable projects
# - Daily for security-critical projects
#
# 4. Limit open PRs:
# - Prevents backlog of unreviewed PRs
# - 5-10 is usually good
#
# 5. Auto-merge patch updates (optional):
# - Use GitHub auto-merge for patch versions
# - Requires passing CI tests
#
# ========================================
# Monitoring
# ========================================
#
# View Dependabot activity:
# - Repository → Insights → Dependency graph → Dependabot
# - Repository → Pull requests (filter by label: "dependencies")
#
# Configure alerts:
# - Repository → Settings → Security & analysis
# - Enable "Dependabot alerts"
# - Enable "Dependabot security updates"
#
# ========================================
# Troubleshooting
# ========================================
#
# Dependabot not creating PRs?
# - Check .github/dependabot.yml is valid YAML
# - Verify manifest files exist (requirements.txt, package.json)
# - Check repository settings → Security & analysis
# - Look for errors in Settings → Dependabot
#
# Too many PRs?
# - Reduce open-pull-requests-limit
# - Change schedule to monthly
# - Use groups to combine updates
#
# PRs failing tests?
# - Fix breaking changes before merging
# - Pin problematic dependencies
# - Use version ranges in manifest files

View File

@@ -0,0 +1,63 @@
name: Sync Cloudflare DNS
on:
push:
paths:
- 'ops/domains.yaml'
branches:
- main
workflow_dispatch: # Allow manual triggers
jobs:
sync-dns:
name: Sync DNS Records to Cloudflare
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip'
- name: Install dependencies
run: |
pip install -r scripts/cloudflare/requirements.txt
- name: Validate domains.yaml
run: |
python -c "import yaml; yaml.safe_load(open('ops/domains.yaml'))"
- name: Sync DNS records (dry run)
env:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
run: |
python scripts/cloudflare/sync_dns.py --dry-run
- name: Sync DNS records
env:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
run: |
python scripts/cloudflare/sync_dns.py
- name: Validate DNS configuration
run: |
python scripts/cloudflare/validate_dns.py --domain blackroad.systems --dns-only
continue-on-error: true # Don't fail if DNS hasn't propagated yet
- name: Comment on commit (if manual trigger)
if: github.event_name == 'workflow_dispatch'
uses: actions/github-script@v7
with:
script: |
github.rest.repos.createCommitComment({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha,
body: '✅ Cloudflare DNS sync completed successfully!'
})

View File

@@ -0,0 +1,134 @@
# CodeQL Security Analysis Workflow Template
# ==========================================
#
# This template sets up CodeQL code scanning for security vulnerabilities.
#
# How to use:
# -----------
# 1. Copy this file to .github/workflows/codeql-analysis.yml in your repo
# 2. Update the languages array based on your repo (python, javascript, typescript, etc.)
# 3. Customize paths to analyze if needed
# 4. Commit and push - CodeQL will run automatically
#
# What is CodeQL?
# --------------
# CodeQL is GitHub's semantic code analysis engine that finds security vulnerabilities
# and coding errors. It's free for public repos and GitHub Enterprise.
#
# Supported languages:
# -------------------
# - python
# - javascript (includes TypeScript)
# - go
# - java
# - csharp
# - cpp
# - ruby
# - swift
name: CodeQL Security Analysis
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
schedule:
# Run CodeQL analysis every Monday at 00:00 UTC
- cron: '0 0 * * 1'
workflow_dispatch:
# Limit concurrent runs
concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: true
jobs:
analyze:
name: CodeQL Analysis
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
# Required for CodeQL to upload results
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
# Update this array based on your repository's languages
# For BlackRoad OS backend: ['python', 'javascript']
# For frontend only: ['javascript']
language: ['python', 'javascript']
steps:
# ========================================
# 1. Checkout code
# ========================================
- name: Checkout repository
uses: actions/checkout@v4
# ========================================
# 2. Initialize CodeQL
# ========================================
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you want to analyze specific paths only:
# paths:
# - backend/
# - src/
# paths-ignore:
# - tests/
# - '**/*.test.js'
# ========================================
# 3. Build code (if needed)
# ========================================
# For compiled languages (Java, C#, C++), add build steps here
# For interpreted languages (Python, JavaScript), auto-build works
# Autobuild attempts to build any compiled languages
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Alternative: Manual build steps for Python if needed
# - name: Build Python (manual)
# if: matrix.language == 'python'
# run: |
# python -m pip install --upgrade pip
# pip install -r backend/requirements.txt
# ========================================
# 4. Perform CodeQL Analysis
# ========================================
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
# ========================================
# Workflow Summary
# ========================================
#
# This workflow:
# 1. Runs on push, PR, schedule (weekly), and manual dispatch
# 2. Analyzes code for security vulnerabilities using CodeQL
# 3. Uploads results to GitHub Security tab
# 4. Creates alerts for any issues found
#
# View results:
# - Go to your repository → Security tab → Code scanning alerts
#
# Customization:
# - Add more languages to matrix.language array
# - Filter paths to analyze specific directories
# - Adjust schedule frequency
# - Add custom queries for domain-specific security checks

View File

@@ -0,0 +1,418 @@
# Comprehensive CI Workflow Template
# ===================================
#
# This template provides a complete CI pipeline for BlackRoad repositories.
# It includes linting, type checking, testing, and build verification.
#
# How to use:
# -----------
# 1. Copy this file to .github/workflows/ci.yml in your repo
# 2. Customize the jobs based on your stack (Python/JS/both)
# 3. Update paths and commands as needed
# 4. Commit and push - CI will run on PRs and pushes
#
# What this workflow does:
# -----------------------
# - Lints code (Python: flake8/black, JS: eslint/prettier)
# - Runs type checking (Python: mypy, TS: tsc)
# - Runs test suite with coverage
# - Builds the application
# - Validates environment config
# - Reports results to PR
name: Continuous Integration
on:
push:
branches:
- main
- develop
- 'feature/**'
pull_request:
branches:
- main
- develop
# Cancel previous runs if new push
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
env:
PYTHON_VERSION: '3.11'
NODE_VERSION: '20'
jobs:
# ========================================
# Job 1: Python Linting
# ========================================
lint-python:
name: Lint Python Code
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: 'backend/requirements.txt'
- name: Install linting tools
run: |
pip install flake8 black isort mypy
- name: Run Black (code formatter check)
run: |
black --check backend/app
echo "✅ Black formatting check passed"
- name: Run isort (import sorting check)
run: |
isort --check-only backend/app
echo "✅ isort check passed"
- name: Run flake8 (linter)
run: |
flake8 backend/app --max-line-length=100 --ignore=E203,W503
echo "✅ flake8 linting passed"
# ========================================
# Job 2: Python Type Checking
# ========================================
type-check-python:
name: Python Type Check
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install dependencies
run: |
pip install -r backend/requirements.txt
pip install mypy types-redis types-requests
- name: Run mypy
run: |
mypy backend/app --ignore-missing-imports
echo "✅ Type checking passed"
# ========================================
# Job 3: Python Tests
# ========================================
test-python:
name: Python Tests
runs-on: ubuntu-latest
timeout-minutes: 10
# Test matrix for multiple Python versions (optional)
strategy:
matrix:
python-version: ['3.11'] # Add ['3.10', '3.11', '3.12'] for multi-version testing
services:
# PostgreSQL for tests
postgres:
image: postgres:15
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: blackroad_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
# Redis for tests
redis:
image: redis:7
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
pip install -r backend/requirements.txt
pip install pytest pytest-cov pytest-asyncio
- name: Set up test environment
env:
DATABASE_URL: postgresql://test:test@localhost:5432/blackroad_test
REDIS_URL: redis://localhost:6379/0
SECRET_KEY: test-secret-key-for-ci
ENVIRONMENT: test
run: |
echo "Test environment configured"
- name: Run tests with coverage
env:
DATABASE_URL: postgresql://test:test@localhost:5432/blackroad_test
REDIS_URL: redis://localhost:6379/0
SECRET_KEY: test-secret-key-for-ci
ENVIRONMENT: test
run: |
cd backend
pytest -v \
--cov=app \
--cov-report=term \
--cov-report=xml \
--cov-report=html \
--maxfail=3
- name: Upload coverage to Codecov (optional)
if: success()
uses: codecov/codecov-action@v3
with:
file: ./backend/coverage.xml
flags: backend
name: backend-coverage
# ========================================
# Job 4: JavaScript/TypeScript Linting (if applicable)
# ========================================
lint-javascript:
name: Lint JavaScript/TypeScript
runs-on: ubuntu-latest
timeout-minutes: 5
# Only run if package.json exists
if: hashFiles('package.json') != ''
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run Prettier check
run: npm run format:check
# ========================================
# Job 5: JavaScript/TypeScript Tests (if applicable)
# ========================================
test-javascript:
name: JavaScript/TypeScript Tests
runs-on: ubuntu-latest
timeout-minutes: 10
if: hashFiles('package.json') != ''
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
if: success()
uses: codecov/codecov-action@v3
with:
file: ./coverage/coverage-final.json
flags: frontend
name: frontend-coverage
# ========================================
# Job 6: Build Verification
# ========================================
build:
name: Build Verification
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
# Python build (if applicable)
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Verify Python build
run: |
pip install build
cd backend
python -m build
echo "✅ Python package builds successfully"
# Node build (if applicable)
- name: Set up Node.js
if: hashFiles('package.json') != ''
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Build frontend (if applicable)
if: hashFiles('package.json') != ''
run: |
npm ci
npm run build
echo "✅ Frontend builds successfully"
# ========================================
# Job 7: Environment Validation
# ========================================
validate-env:
name: Validate Environment Config
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Validate .env.example exists
run: |
if [ ! -f backend/.env.example ]; then
echo "❌ backend/.env.example not found"
exit 1
fi
echo "✅ .env.example exists"
- name: Validate .env.example is not committed
run: |
if [ -f backend/.env ]; then
echo "❌ backend/.env should not be committed!"
exit 1
fi
echo "✅ .env is gitignored"
- name: Validate Railway config
run: |
if [ -f railway.toml ]; then
echo "✅ railway.toml exists"
else
echo "⚠️ railway.toml not found (not required but recommended)"
fi
# ========================================
# Job 8: Security Scan (optional)
# ========================================
security-scan:
name: Security Scan
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Bandit (Python security linter)
run: |
pip install bandit
bandit -r backend/app -ll
echo "✅ Security scan passed"
- name: Check for secrets in code
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
# ========================================
# Job 9: Summary
# ========================================
ci-summary:
name: CI Summary
runs-on: ubuntu-latest
needs:
- lint-python
- type-check-python
- test-python
- build
- validate-env
if: always()
steps:
- name: Check all jobs passed
if: |
needs.lint-python.result == 'failure' ||
needs.type-check-python.result == 'failure' ||
needs.test-python.result == 'failure' ||
needs.build.result == 'failure' ||
needs.validate-env.result == 'failure'
run: |
echo "❌ CI failed - check job logs above"
exit 1
- name: All checks passed
run: |
echo "✅ All CI checks passed!"
echo "Code is ready to merge"
# ========================================
# Workflow Summary
# ========================================
#
# This comprehensive CI workflow:
# 1. Lints Python code (black, isort, flake8)
# 2. Type checks Python (mypy)
# 3. Runs Python tests with coverage (pytest)
# 4. Lints JavaScript/TypeScript (eslint, prettier)
# 5. Runs JS/TS tests (jest)
# 6. Verifies build succeeds
# 7. Validates environment configuration
# 8. Scans for security issues
# 9. Provides overall summary
#
# Customize based on your stack:
# - Remove JS jobs if Python-only
# - Remove Python jobs if JS-only
# - Add database migrations check
# - Add API contract tests
# - Add performance benchmarks
#
# Status checks:
# - Configure branch protection to require these jobs
# - Prevents merging failing code
# - Ensures code quality standards

View File

@@ -0,0 +1,286 @@
# Frontend Deployment Workflow Template
# ======================================
#
# This template deploys static frontend sites to various platforms.
#
# Supported targets:
# - GitHub Pages
# - Cloudflare Pages
# - Railway static hosting
# - Vercel
# - Netlify
#
# How to use:
# -----------
# 1. Copy this file to .github/workflows/frontend-deploy.yml
# 2. Choose your deployment target (uncomment the relevant job)
# 3. Configure custom domain settings
# 4. Add required secrets
# 5. Push to trigger deployment
name: Deploy Frontend
on:
push:
branches:
- main
paths:
- 'landing/**'
- 'frontend/**'
- 'backend/static/**'
workflow_dispatch:
# Only allow one deployment at a time
concurrency:
group: frontend-deploy
cancel-in-progress: false
jobs:
# ========================================
# Option 1: Deploy to GitHub Pages
# ========================================
deploy-github-pages:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
timeout-minutes: 10
# Grant GITHUB_TOKEN permissions
permissions:
contents: read
pages: write
id-token: write
# Protect production environment
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Checkout code
uses: actions/checkout@v4
# If you have a build step:
# - name: Set up Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '20'
# cache: 'npm'
#
# - name: Install dependencies
# run: npm ci
#
# - name: Build site
# run: npm run build
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Update path based on your static files location
path: 'backend/static' # or 'landing/', 'dist/', etc.
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
- name: Deployment summary
run: |
echo "✅ Deployed to GitHub Pages"
echo "URL: ${{ steps.deployment.outputs.page_url }}"
# ========================================
# Option 2: Deploy to Railway (Static)
# ========================================
# deploy-railway:
# name: Deploy to Railway
# runs-on: ubuntu-latest
# timeout-minutes: 10
#
# environment:
# name: production-frontend
# url: https://blackroad.systems
#
# steps:
# - name: Checkout code
# uses: actions/checkout@v4
#
# - name: Install Railway CLI
# run: |
# curl -fsSL https://railway.app/install.sh | sh
# echo "$HOME/.railway/bin" >> $GITHUB_PATH
#
# - name: Deploy to Railway
# env:
# RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
# run: |
# railway up --service frontend --detach
# echo "✅ Deployed to Railway"
# ========================================
# Option 3: Deploy to Cloudflare Pages
# ========================================
# deploy-cloudflare-pages:
# name: Deploy to Cloudflare Pages
# runs-on: ubuntu-latest
# timeout-minutes: 10
#
# environment:
# name: cloudflare-pages
# url: https://blackroad.systems
#
# steps:
# - name: Checkout code
# uses: actions/checkout@v4
#
# # Build step if needed
# # - name: Build site
# # run: npm run build
#
# - name: Publish to Cloudflare Pages
# uses: cloudflare/pages-action@v1
# with:
# apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
# accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# projectName: blackroad-os
# directory: backend/static # or dist/, build/, etc.
# gitHubToken: ${{ secrets.GITHUB_TOKEN }}
#
# - name: Deployment summary
# run: echo "✅ Deployed to Cloudflare Pages"
# ========================================
# Option 4: Deploy to Vercel
# ========================================
# deploy-vercel:
# name: Deploy to Vercel
# runs-on: ubuntu-latest
# timeout-minutes: 10
#
# environment:
# name: vercel-production
#
# steps:
# - name: Checkout code
# uses: actions/checkout@v4
#
# - name: Deploy to Vercel
# uses: amondnet/vercel-action@v25
# with:
# vercel-token: ${{ secrets.VERCEL_TOKEN }}
# vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
# vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
# vercel-args: '--prod'
# working-directory: ./
#
# - name: Deployment summary
# run: echo "✅ Deployed to Vercel"
# ========================================
# Post-Deployment: Cache Purge
# ========================================
purge-cache:
name: Purge Cloudflare Cache
runs-on: ubuntu-latest
needs: deploy-github-pages # Update based on your deployment job
if: success()
steps:
- name: Purge Cloudflare cache
env:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
run: |
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
echo "✅ Cloudflare cache purged"
# ========================================
# Post-Deployment: Smoke Tests
# ========================================
smoke-tests:
name: Frontend Smoke Tests
runs-on: ubuntu-latest
needs: deploy-github-pages # Update based on your deployment job
if: success()
steps:
- name: Wait for deployment propagation
run: sleep 30
- name: Test homepage loads
run: |
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://blackroad.systems/)
if [ "$HTTP_CODE" != "200" ]; then
echo "❌ Homepage returned HTTP $HTTP_CODE"
exit 1
fi
echo "✅ Homepage loads successfully"
- name: Test key pages
run: |
PAGES=(
"/"
"/architecture"
"/pricing"
"/contact"
)
for PAGE in "${PAGES[@]}"; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://blackroad.systems$PAGE")
if [ "$HTTP_CODE" = "200" ]; then
echo "✅ $PAGE loads successfully"
else
echo "⚠️ $PAGE returned HTTP $HTTP_CODE"
fi
done
- name: Test SSL certificate
run: |
echo | openssl s_client -servername blackroad.systems -connect blackroad.systems:443 2>/dev/null | \
openssl x509 -noout -dates
echo "✅ SSL certificate is valid"
# ========================================
# Configuration Guide
# ========================================
#
# GitHub Pages Setup:
# ------------------
# 1. Repository → Settings → Pages
# 2. Source: GitHub Actions
# 3. Custom domain: blackroad.systems
# 4. CNAME file: Create in static files root
#
# Required GitHub Secrets:
# -----------------------
# - CF_API_TOKEN (for cache purge)
# - CF_ZONE_ID (for cache purge)
#
# Optional Secrets (based on target):
# ----------------------------------
# - RAILWAY_TOKEN (for Railway)
# - CLOUDFLARE_API_TOKEN (for CF Pages)
# - CLOUDFLARE_ACCOUNT_ID (for CF Pages)
# - VERCEL_TOKEN (for Vercel)
# - VERCEL_ORG_ID (for Vercel)
# - VERCEL_PROJECT_ID (for Vercel)
#
# Custom Domain:
# -------------
# 1. Add CNAME file to your static files:
# echo "blackroad.systems" > CNAME
# 2. Configure DNS (see infra/cloudflare/records.yaml)
# 3. Wait for DNS propagation (5-60 minutes)
#
# Deployment Targets Comparison:
# -----------------------------
# GitHub Pages: Free, simple, good for docs/marketing
# Cloudflare Pages: Free, global CDN, fast
# Railway: Paid, unified with backend
# Vercel: Free tier, excellent DX, edge functions
# Netlify: Free tier, form handling, split testing

View File

@@ -0,0 +1,292 @@
# Railway Deployment Workflow Template
# ======================================
#
# This template can be copied to any BlackRoad repository that deploys to Railway.
#
# How to use:
# -----------
# 1. Copy this file to .github/workflows/railway-deploy.yml in your repo
# 2. Update the service name and environment variables as needed
# 3. Add required GitHub secrets:
# - RAILWAY_TOKEN (get from: railway tokens create)
# - RAILWAY_SERVICE_ID (optional, for specific service targeting)
# 4. Push to main branch to trigger deployment
#
# Required GitHub Secrets:
# -----------------------
# RAILWAY_TOKEN - Railway API token for CLI authentication
#
# Optional GitHub Secrets/Variables:
# ---------------------------------
# RAILWAY_SERVICE_ID - Specific Railway service ID to deploy
# SENTRY_DSN - Sentry error monitoring DSN
#
# Customization:
# -------------
# - Change trigger branches (currently: main)
# - Add/remove build steps
# - Configure environment-specific variables
# - Add post-deploy notifications (Slack, Discord, etc.)
name: Deploy to Railway
on:
push:
branches:
- main
paths-ignore:
- '**.md'
- 'docs/**'
- '.github/**'
- '!.github/workflows/railway-deploy.yml'
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
type: choice
options:
- production
- staging
default: 'production'
# Only allow one deployment at a time
concurrency:
group: railway-deploy-${{ github.ref }}
cancel-in-progress: false
jobs:
deploy:
name: Deploy to Railway
runs-on: ubuntu-latest
timeout-minutes: 15
# Set deployment environment
environment:
name: ${{ github.event.inputs.environment || 'production' }}
url: https://os.blackroad.systems # Update with your actual URL
steps:
# ========================================
# 1. Checkout code
# ========================================
- name: Checkout code
uses: actions/checkout@v4
# ========================================
# 2. Install Railway CLI
# ========================================
- name: Install Railway CLI
run: |
curl -fsSL https://railway.app/install.sh | sh
echo "$HOME/.railway/bin" >> $GITHUB_PATH
- name: Verify Railway installation
run: railway --version
# ========================================
# 3. Set up environment
# ========================================
- name: Set up environment variables
run: |
echo "RAILWAY_TOKEN=${{ secrets.RAILWAY_TOKEN }}" >> $GITHUB_ENV
echo "GIT_SHA=${GITHUB_SHA::8}" >> $GITHUB_ENV
echo "DEPLOY_TIME=$(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_ENV
# ========================================
# 4. Pre-deploy validation (optional)
# ========================================
- name: Validate environment variables
run: |
if [ -z "${{ secrets.RAILWAY_TOKEN }}" ]; then
echo "❌ Error: RAILWAY_TOKEN secret is not set"
exit 1
fi
echo "✅ Environment variables validated"
# ========================================
# 5. Deploy to Railway
# ========================================
- name: Deploy to Railway
id: deploy
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
run: |
echo "🚀 Deploying to Railway..."
echo "Environment: ${{ github.event.inputs.environment || 'production' }}"
echo "Commit: ${GITHUB_SHA::8}"
echo "Branch: ${GITHUB_REF_NAME}"
# Deploy using Railway CLI
# If RAILWAY_SERVICE_ID is set, deploy to specific service
if [ -n "${{ secrets.RAILWAY_SERVICE_ID }}" ]; then
railway up \
--service "${{ secrets.RAILWAY_SERVICE_ID }}" \
--detach
else
railway up --detach
fi
echo "✅ Deployment initiated"
# ========================================
# 6. Wait for deployment and health check
# ========================================
- name: Wait for deployment
id: wait
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
run: |
echo "⏳ Waiting for deployment to complete..."
# Wait up to 5 minutes for deployment
MAX_WAIT=300
ELAPSED=0
INTERVAL=10
while [ $ELAPSED -lt $MAX_WAIT ]; do
# Check deployment status (simplified - adjust based on Railway CLI output)
STATUS=$(railway status --json 2>/dev/null || echo '{"status":"unknown"}')
echo "Status check at ${ELAPSED}s: Deployment in progress..."
# Sleep and increment
sleep $INTERVAL
ELAPSED=$((ELAPSED + INTERVAL))
done
echo "⏰ Deployment wait period completed"
# ========================================
# 7. Health check (optional but recommended)
# ========================================
- name: Health check
id: health
run: |
echo "🏥 Running health check..."
# Update with your actual health endpoint
HEALTH_URL="https://os.blackroad.systems/health"
# Try health check up to 5 times
MAX_ATTEMPTS=5
ATTEMPT=1
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
echo "Health check attempt $ATTEMPT/$MAX_ATTEMPTS..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" || echo "000")
if [ "$HTTP_CODE" = "200" ]; then
echo "✅ Health check passed (HTTP $HTTP_CODE)"
exit 0
fi
echo "⚠️ Health check returned HTTP $HTTP_CODE, retrying..."
sleep 10
ATTEMPT=$((ATTEMPT + 1))
done
echo "❌ Health check failed after $MAX_ATTEMPTS attempts"
exit 1
# ========================================
# 8. Post-deploy notifications (optional)
# ========================================
- name: Notify deployment success
if: success()
run: |
echo "✅ Deployment successful!"
echo "SHA: ${GITHUB_SHA::8}"
echo "Time: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
# Add Slack/Discord webhook here if needed
# Example:
# curl -X POST -H 'Content-type: application/json' \
# --data '{"text":"✅ Deployed to Railway: '"${GITHUB_SHA::8}"'"}' \
# ${{ secrets.SLACK_WEBHOOK_URL }}
# ========================================
# 9. Handle deployment failure
# ========================================
- name: Notify deployment failure
if: failure()
run: |
echo "❌ Deployment failed!"
echo "SHA: ${GITHUB_SHA::8}"
echo "Check Railway logs for details"
# Add Slack/Discord webhook here if needed
# ========================================
# 10. Send to Sentry (optional)
# ========================================
- name: Create Sentry release
if: success() && vars.SENTRY_DSN != ''
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: blackroad
SENTRY_PROJECT: blackroad-os
run: |
# Install Sentry CLI
curl -sL https://sentry.io/get-cli/ | bash
# Create release
sentry-cli releases new "${GITHUB_SHA::8}"
sentry-cli releases set-commits "${GITHUB_SHA::8}" --auto
sentry-cli releases finalize "${GITHUB_SHA::8}"
sentry-cli releases deploys "${GITHUB_SHA::8}" new -e production
echo "✅ Sentry release created"
# ========================================
# Optional: Smoke tests after deployment
# ========================================
smoke-tests:
name: Smoke Tests
runs-on: ubuntu-latest
needs: deploy
if: success()
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run smoke tests
run: |
echo "🧪 Running smoke tests..."
# Basic smoke tests
BASE_URL="https://os.blackroad.systems"
# Test 1: Health endpoint
echo "Test 1: Health endpoint"
curl -f "$BASE_URL/health" || exit 1
# Test 2: API documentation
echo "Test 2: API documentation"
curl -f "$BASE_URL/api/docs" || exit 1
# Test 3: Frontend loads
echo "Test 3: Frontend loads"
curl -f "$BASE_URL/" || exit 1
echo "✅ All smoke tests passed"
# ========================================
# Workflow Summary
# ========================================
#
# This workflow:
# 1. Triggers on push to main or manual dispatch
# 2. Installs Railway CLI
# 3. Validates environment
# 4. Deploys to Railway
# 5. Waits for deployment
# 6. Runs health checks
# 7. Sends notifications
# 8. Creates Sentry release (optional)
# 9. Runs smoke tests (optional)
#
# Customize as needed for your specific service!

793
ENV_VARS.md Normal file
View File

@@ -0,0 +1,793 @@
# Environment Variables Documentation
**Version:** 1.0
**Date:** 2025-11-18
**Purpose:** Complete reference for all environment variables across BlackRoad OS infrastructure
---
## Overview
This document provides the **complete list** of environment variables used across the BlackRoad OS ecosystem. It covers:
- **Required variables** for core functionality
- **Optional variables** for integrations and features
- **Where to set them** (Railway, GitHub Actions, local development)
- **How to generate secret values** safely
- **Security best practices**
---
## Table of Contents
1. [Core Application](#core-application)
2. [Database & Cache](#database--cache)
3. [Authentication & Security](#authentication--security)
4. [API Integrations](#api-integrations)
5. [Infrastructure](#infrastructure)
6. [Deployment](#deployment)
7. [Observability](#observability)
8. [Where to Set Variables](#where-to-set-variables)
9. [Security Best Practices](#security-best-practices)
10. [Quick Start Templates](#quick-start-templates)
---
## Core Application
### ENVIRONMENT
**Required:** Yes
**Default:** `development`
**Valid values:** `development`, `staging`, `production`
**Description:** Runtime environment identifier
**Where to set:**
- **Railway (production):** `production`
- **Railway (staging):** `staging`
- **Local:** `development`
**Example:**
```bash
ENVIRONMENT=production
```
### DEBUG
**Required:** No
**Default:** `False`
**Valid values:** `True`, `False`
**Description:** Enable debug mode (verbose logging, stack traces)
**⚠️ Security Warning:** MUST be `False` in production!
**Where to set:**
- **Railway:** `False`
- **Local:** `True` (for development)
**Example:**
```bash
DEBUG=False
```
### SECRET_KEY
**Required:** Yes
**Description:** Secret key for signing sessions, JWT tokens, and encryption
**How to generate:**
```bash
# Option 1: Using openssl
openssl rand -hex 32
# Option 2: Using Python
python -c "import secrets; print(secrets.token_hex(32))"
```
**⚠️ Security:**
- MUST be unique per environment (production ≠ staging ≠ local)
- MUST be at least 32 characters
- NEVER commit to git
- Rotate quarterly or after suspected compromise
**Where to set:**
- **Railway (production):** Generate unique value
- **Railway (staging):** Generate different unique value
- **Local:** Generate local value (in `.env`, gitignored)
**Example:**
```bash
SECRET_KEY=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6
```
### API_BASE_URL
**Required:** Yes
**Description:** Public URL for the backend API (used by frontend for API calls)
**Where to set:**
- **Railway (production):** `https://os.blackroad.systems`
- **Railway (staging):** `https://staging.blackroad.systems`
- **Local:** `http://localhost:8000`
**Example:**
```bash
API_BASE_URL=https://os.blackroad.systems
```
### FRONTEND_URL
**Required:** Yes
**Description:** Public URL for the frontend (used for CORS, redirects, emails)
**Where to set:**
- **Railway (production):** `https://os.blackroad.systems`
- **Railway (staging):** `https://staging.blackroad.systems`
- **Local:** `http://localhost:8000`
**Example:**
```bash
FRONTEND_URL=https://os.blackroad.systems
```
### ALLOWED_ORIGINS
**Required:** Yes
**Description:** Comma-separated list of allowed CORS origins
**Where to set:**
- **Railway (production):** `https://os.blackroad.systems,https://blackroad.ai,https://blackroad.network`
- **Railway (staging):** `https://staging.blackroad.systems`
- **Local:** `http://localhost:8000,http://localhost:3000`
**Example:**
```bash
ALLOWED_ORIGINS=https://os.blackroad.systems,https://blackroad.ai
```
### PORT
**Required:** No (Railway auto-detects)
**Default:** `8000`
**Description:** HTTP port for the backend server
**Where to set:**
- **Railway:** Not needed (Railway sets `$PORT` automatically)
- **Local:** `8000` (or any available port)
**Example:**
```bash
PORT=8000
```
---
## Database & Cache
### DATABASE_URL
**Required:** Yes
**Description:** PostgreSQL connection string (sync driver)
**Format:** `postgresql://user:password@host:port/database`
**Where to set:**
- **Railway:** Auto-injected via `${{Postgres.DATABASE_URL}}`
- **Local:** `postgresql://user:pass@localhost:5432/blackroad_dev`
**Example:**
```bash
DATABASE_URL=postgresql://user:password@localhost:5432/blackroad_dev
```
**How to use Railway reference:**
In Railway dashboard → Service → Variables:
```
DATABASE_URL=${{Postgres.DATABASE_URL}}
```
### DATABASE_ASYNC_URL
**Required:** Yes (for async database operations)
**Description:** PostgreSQL connection string (async driver)
**Format:** `postgresql+asyncpg://user:password@host:port/database`
**Where to set:**
- **Railway:** Convert `DATABASE_URL` to async:
```
DATABASE_ASYNC_URL=${{Postgres.DATABASE_URL_ASYNC}}
```
Or manually construct:
```bash
# If Railway doesn't provide async URL, derive from sync URL
# Replace 'postgresql://' with 'postgresql+asyncpg://'
```
- **Local:** `postgresql+asyncpg://user:pass@localhost:5432/blackroad_dev`
**Example:**
```bash
DATABASE_ASYNC_URL=postgresql+asyncpg://user:password@localhost:5432/blackroad_dev
```
### REDIS_URL
**Required:** Yes
**Description:** Redis connection string for caching and sessions
**Format:** `redis://host:port/db` or `redis://user:password@host:port/db`
**Where to set:**
- **Railway:** Auto-injected via `${{Redis.REDIS_URL}}`
- **Local:** `redis://localhost:6379/0`
**Example:**
```bash
REDIS_URL=redis://localhost:6379/0
```
**How to use Railway reference:**
In Railway dashboard → Service → Variables:
```
REDIS_URL=${{Redis.REDIS_URL}}
```
---
## Authentication & Security
### ACCESS_TOKEN_EXPIRE_MINUTES
**Required:** No
**Default:** `30`
**Description:** JWT access token expiration time (in minutes)
**Recommended values:**
- **Production:** `15` - `30` (shorter = more secure)
- **Development:** `60` - `120` (longer = less login friction)
**Example:**
```bash
ACCESS_TOKEN_EXPIRE_MINUTES=30
```
### REFRESH_TOKEN_EXPIRE_DAYS
**Required:** No
**Default:** `7`
**Description:** JWT refresh token expiration time (in days)
**Recommended values:**
- **Production:** `7` - `14`
- **Development:** `30`
**Example:**
```bash
REFRESH_TOKEN_EXPIRE_DAYS=7
```
### WALLET_MASTER_KEY
**Required:** Yes (for blockchain features)
**Description:** Master key for wallet/blockchain operations
**How to generate:**
```bash
openssl rand -hex 32
```
**⚠️ Security:**
- CRITICAL: Losing this key means losing access to blockchain wallets
- Backup securely (encrypted password manager, hardware security module)
- NEVER expose in logs or error messages
**Example:**
```bash
WALLET_MASTER_KEY=your-generated-master-key-here
```
---
## API Integrations
### OPENAI_API_KEY
**Required:** For AI features (Lucidia, agents)
**Description:** OpenAI API key for GPT models
**How to get:**
1. Go to https://platform.openai.com/api-keys
2. Create new secret key
3. Copy key (starts with `sk-`)
**Example:**
```bash
OPENAI_API_KEY=sk-proj-1234567890abcdef...
```
### ANTHROPIC_API_KEY
**Required:** For Claude integration
**Description:** Anthropic API key for Claude models
**How to get:**
1. Go to https://console.anthropic.com/settings/keys
2. Create new API key
3. Copy key
**Example:**
```bash
ANTHROPIC_API_KEY=sk-ant-1234567890abcdef...
```
### GITHUB_TOKEN
**Required:** For GitHub integrations (agents, automation)
**Description:** GitHub Personal Access Token (PAT)
**How to get:**
1. Go to https://github.com/settings/tokens
2. Generate new token (classic or fine-grained)
3. Required scopes: `repo`, `workflow`, `read:org`
**Example:**
```bash
GITHUB_TOKEN=ghp_1234567890abcdefghijklmnopqrstuvwxyz
```
### STRIPE_SECRET_KEY
**Required:** For payment features
**Description:** Stripe API secret key
**How to get:**
1. Go to https://dashboard.stripe.com/apikeys
2. Copy "Secret key" (starts with `sk_test_` or `sk_live_`)
**⚠️ Use test key for development:**
```bash
# Development/Staging
STRIPE_SECRET_KEY=sk_test_...
# Production
STRIPE_SECRET_KEY=sk_live_...
```
### STRIPE_PUBLISHABLE_KEY
**Required:** For frontend payment UI
**Description:** Stripe publishable key (safe to expose in frontend)
**Example:**
```bash
STRIPE_PUBLISHABLE_KEY=pk_test_1234567890abcdef...
```
### AWS_ACCESS_KEY_ID
**Required:** For AWS S3 storage
**Description:** AWS IAM access key ID
**How to get:**
1. AWS Console → IAM → Users → Security Credentials
2. Create access key
3. Download/save credentials
**Example:**
```bash
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
```
### AWS_SECRET_ACCESS_KEY
**Required:** For AWS S3 storage
**Description:** AWS IAM secret access key
**Example:**
```bash
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
```
### AWS_S3_BUCKET
**Required:** For AWS S3 storage
**Description:** S3 bucket name for file uploads
**Example:**
```bash
AWS_S3_BUCKET=blackroad-uploads-production
```
### AWS_REGION
**Required:** For AWS S3 storage
**Default:** `us-east-1`
**Description:** AWS region for S3 bucket
**Example:**
```bash
AWS_REGION=us-east-1
```
---
## Infrastructure
### CF_API_TOKEN
**Required:** For Cloudflare automation
**Description:** Cloudflare API token for DNS/cache management
**How to get:**
1. Cloudflare dashboard → My Profile → API Tokens
2. Create token with "Zone.DNS" edit permissions
3. Copy token
**Where to set:**
- **GitHub Actions:** As secret `CF_API_TOKEN`
- **Local:** Export when running DNS scripts
**Example:**
```bash
CF_API_TOKEN=your-cloudflare-api-token
```
### CF_ZONE_ID
**Required:** For Cloudflare automation
**Description:** Cloudflare zone ID for a specific domain
**How to get:**
1. Cloudflare dashboard → Select domain
2. Overview page → Right sidebar → Zone ID
**Where to set:**
- **GitHub Actions:** As secret `CF_ZONE_ID`
- **records.yaml:** In domain configuration
**Example:**
```bash
CF_ZONE_ID=1234567890abcdef1234567890abcdef
```
### RAILWAY_TOKEN
**Required:** For Railway CLI/CI deployments
**Description:** Railway API token for deployments
**How to get:**
```bash
railway login
railway tokens create
```
**Where to set:**
- **GitHub Actions:** As secret `RAILWAY_TOKEN`
- **Local:** Export when using Railway CLI
**Example:**
```bash
RAILWAY_TOKEN=your-railway-api-token
```
### RAILWAY_SERVICE_ID
**Required:** For specific service deployments
**Description:** Railway service ID to deploy to
**How to get:**
1. Railway dashboard → Service → Settings
2. Copy Service ID
**Where to set:**
- **GitHub Actions:** As repository variable
**Example:**
```bash
RAILWAY_SERVICE_ID=abc123def456
```
---
## Deployment
### SENTRY_DSN
**Required:** For error monitoring
**Description:** Sentry Data Source Name for error tracking
**How to get:**
1. Go to https://sentry.io
2. Create new project
3. Copy DSN from project settings
**Example:**
```bash
SENTRY_DSN=https://1234567890abcdef@o123456.ingest.sentry.io/1234567
```
### SENTRY_ENVIRONMENT
**Required:** No (if using SENTRY_DSN)
**Default:** Uses `ENVIRONMENT` value
**Description:** Sentry environment tag
**Example:**
```bash
SENTRY_ENVIRONMENT=production
```
---
## Observability
### LOG_LEVEL
**Required:** No
**Default:** `INFO`
**Valid values:** `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
**Description:** Application logging level
**Recommended:**
- **Production:** `WARNING` or `INFO`
- **Staging:** `INFO` or `DEBUG`
- **Development:** `DEBUG`
**Example:**
```bash
LOG_LEVEL=INFO
```
---
## Where to Set Variables
### Railway (Production & Staging)
1. Go to Railway dashboard → Your project
2. Select service (e.g., `backend`)
3. Go to **Variables** tab
4. Click **New Variable**
5. Enter name and value
6. Click **Add**
**Railway Reference Variables:**
Use `${{ServiceName.VARIABLE}}` to reference values from other services:
```
DATABASE_URL=${{Postgres.DATABASE_URL}}
REDIS_URL=${{Redis.REDIS_URL}}
```
**Shared Variables:**
For variables used across services, use **Shared Variables**:
1. Variables tab → **Shared Variables**
2. Add variable (e.g., `SECRET_KEY`)
3. All services can access
### GitHub Actions
1. Go to repository → Settings → Secrets and variables → Actions
2. Click **New repository secret**
3. Enter name and value
4. Click **Add secret**
**Required GitHub Secrets:**
- `RAILWAY_TOKEN` - For Railway deployments
- `CF_API_TOKEN` - For DNS automation
- `SENTRY_DSN` - For error monitoring (optional)
**How to use in workflows:**
```yaml
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
```
### Local Development
1. Copy environment template:
```bash
cp backend/.env.example backend/.env
```
2. Edit `.env` file with your local values:
```bash
nano backend/.env # or use your preferred editor
```
3. The `.env` file is gitignored - safe to add real values
4. FastAPI automatically loads `.env` via `python-dotenv`
**⚠️ Never commit `.env` files to git!**
---
## Security Best Practices
### 1. Secret Generation
- **Always use cryptographically secure random generators**
- Minimum 32 characters for secrets
- Use different secrets per environment
**Good:**
```bash
openssl rand -hex 32
python -c "import secrets; print(secrets.token_hex(32))"
```
**Bad:**
```bash
# Don't use predictable values
SECRET_KEY=password123
SECRET_KEY=my-app-secret
```
### 2. Secret Storage
- ✅ Railway environment variables (encrypted at rest)
- ✅ GitHub Actions secrets (encrypted)
- ✅ `.env` files (gitignored)
- ✅ Password managers (1Password, LastPass, Bitwarden)
- ❌ NEVER commit secrets to git
- ❌ NEVER hardcode in source code
- ❌ NEVER expose in logs or error messages
### 3. Secret Rotation
- Rotate `SECRET_KEY` quarterly
- Rotate API keys after suspected compromise
- Update credentials after team member departure
- Keep backups of old secrets (for rollback)
### 4. Principle of Least Privilege
- Give each service only the permissions it needs
- Use separate database users for different services
- Use read-only API keys where possible
- Limit token scopes (GitHub, Stripe, etc.)
### 5. Verification
Before deploying to production:
- [ ] All required variables are set
- [ ] No placeholder values (e.g., `REPLACE_ME`)
- [ ] Secrets are unique per environment
- [ ] `DEBUG=False` in production
- [ ] CORS origins match production domains
- [ ] Database backups are configured
---
## Quick Start Templates
### Production Railway (backend service)
```bash
# Core
ENVIRONMENT=production
DEBUG=False
SECRET_KEY=[generate-unique-32-char-string]
API_BASE_URL=https://os.blackroad.systems
FRONTEND_URL=https://os.blackroad.systems
ALLOWED_ORIGINS=https://os.blackroad.systems,https://blackroad.ai
# Database (auto-injected by Railway)
DATABASE_URL=${{Postgres.DATABASE_URL}}
DATABASE_ASYNC_URL=${{Postgres.DATABASE_URL_ASYNC}}
REDIS_URL=${{Redis.REDIS_URL}}
# Auth
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_DAYS=7
WALLET_MASTER_KEY=[generate-unique-32-char-string]
# AI (add when ready)
# OPENAI_API_KEY=sk-proj-...
# ANTHROPIC_API_KEY=sk-ant-...
# Observability (add when ready)
# SENTRY_DSN=https://...
# LOG_LEVEL=WARNING
```
### Local Development (.env)
```bash
# Core
ENVIRONMENT=development
DEBUG=True
SECRET_KEY=local-dev-secret-key-not-for-production
API_BASE_URL=http://localhost:8000
FRONTEND_URL=http://localhost:8000
ALLOWED_ORIGINS=http://localhost:8000,http://localhost:3000
# Database (Docker Compose)
DATABASE_URL=postgresql://blackroad:blackroad@localhost:5432/blackroad_dev
DATABASE_ASYNC_URL=postgresql+asyncpg://blackroad:blackroad@localhost:5432/blackroad_dev
REDIS_URL=redis://localhost:6379/0
# Auth
ACCESS_TOKEN_EXPIRE_MINUTES=120
REFRESH_TOKEN_EXPIRE_DAYS=30
WALLET_MASTER_KEY=local-dev-wallet-key
# AI (optional - use your own keys)
# OPENAI_API_KEY=sk-...
# ANTHROPIC_API_KEY=sk-ant-...
```
### GitHub Actions Secrets
```bash
# Required
RAILWAY_TOKEN=[get-from-railway-cli]
CF_API_TOKEN=[get-from-cloudflare]
# Optional
SENTRY_DSN=[get-from-sentry]
```
---
## Validation Script
Use this script to validate your environment variables:
```bash
#!/bin/bash
# File: scripts/validate_env.sh
set -e
echo "Validating environment variables..."
# Check required variables
REQUIRED_VARS=(
"ENVIRONMENT"
"SECRET_KEY"
"DATABASE_URL"
"REDIS_URL"
"API_BASE_URL"
"FRONTEND_URL"
"ALLOWED_ORIGINS"
)
MISSING_VARS=()
for VAR in "${REQUIRED_VARS[@]}"; do
if [ -z "${!VAR}" ]; then
MISSING_VARS+=("$VAR")
fi
done
if [ ${#MISSING_VARS[@]} -gt 0 ]; then
echo "❌ Missing required environment variables:"
printf ' - %s\n' "${MISSING_VARS[@]}"
exit 1
fi
# Check SECRET_KEY length
if [ ${#SECRET_KEY} -lt 32 ]; then
echo "❌ SECRET_KEY must be at least 32 characters"
exit 1
fi
# Check DEBUG in production
if [ "$ENVIRONMENT" = "production" ] && [ "$DEBUG" = "True" ]; then
echo "⚠️ WARNING: DEBUG=True in production environment!"
exit 1
fi
echo "✅ Environment variables validated successfully!"
```
**Run before deploy:**
```bash
chmod +x scripts/validate_env.sh
source backend/.env && ./scripts/validate_env.sh
```
---
## Troubleshooting
### Variable Not Loading
**Problem:** Application doesn't see environment variable
**Solutions:**
1. **Railway:** Check service Variables tab - is variable set?
2. **Local:** Is `.env` file in correct location (`backend/.env`)?
3. **Restart:** Restart application after changing variables
4. **Syntax:** Check for typos in variable names
### Database Connection Fails
**Problem:** `DatabaseConnectionError` or similar
**Solutions:**
1. Check `DATABASE_URL` format is correct
2. Railway: Verify Postgres plugin is attached
3. Local: Check Docker Compose is running (`docker-compose ps`)
4. Check database credentials are correct
### CORS Errors
**Problem:** Frontend can't call API (CORS error in browser console)
**Solutions:**
1. Check `ALLOWED_ORIGINS` includes frontend domain
2. Include protocol: `https://` not just `domain.com`
3. No trailing slash: `https://domain.com` not `https://domain.com/`
4. Railway: Wait 1-2 minutes for variable updates to apply
---
**This document is the single source of truth for environment variables. Keep it updated as new variables are added.**
**Where AI meets the open road.** 🛣️

View File

@@ -1049,6 +1049,77 @@ Month 12: First paying customers ✅
--- ---
## PHASE 1 IMPLEMENTATION FILES
The master orchestration plan is now backed by **concrete implementation files**:
### Infrastructure Implementation
**Cloudflare DNS:**
- `infra/cloudflare/records.yaml` - Complete DNS configuration for all domains
- `infra/cloudflare/migrate_to_cloudflare.md` - Step-by-step migration guide for Alexa
- `infra/cloudflare/cloudflare_dns_sync.py` - Automated DNS sync script (Python)
- `infra/cloudflare/CLOUDFLARE_DNS_BLUEPRINT.md` - Updated with implementation references
**Environment Variables:**
- `ENV_VARS.md` - Complete environment variable documentation for all services
- Covers: Railway, GitHub Actions, Cloudflare, local development
- Includes: Security best practices, validation scripts, troubleshooting
**Frontend Infrastructure:**
- `infra/frontend/LANDING_PAGE_PLAN.md` - Detailed landing page implementation plan
- Includes: Page structure, design system, content guidelines, deployment strategy
### GitHub Actions Workflows
**Templates** (in `.github/workflows/templates/`):
- `railway-deploy-template.yml` - Railway deployment workflow
- `frontend-deploy-template.yml` - Frontend deployment (GitHub Pages, Cloudflare Pages, etc.)
- `codeql-analysis-template.yml` - Security scanning workflow
- `comprehensive-ci-template.yml` - Complete CI pipeline (lint, test, build)
**Active Configuration:**
- `.github/dependabot.yml` - Automated dependency updates
**How to use templates:**
1. Copy template from `.github/workflows/templates/` to `.github/workflows/`
2. Remove `-template` from filename
3. Customize for your specific repo/service
4. Commit and push
### Deployment Documentation
**Environment Map:**
- `infra/env/ENVIRONMENT_MAP.md` - Existing cross-platform variable map
**New Documentation:**
- `ENV_VARS.md` - Comprehensive variable reference
- Railway workflow templates with health checks
- Frontend deployment options (GitHub Pages, Railway, Cloudflare Pages)
### Next Steps for Implementation
**Immediate (Week 1-2):**
1. Follow `infra/cloudflare/migrate_to_cloudflare.md` to migrate DNS
2. Set up GitHub secrets (CF_API_TOKEN, RAILWAY_TOKEN)
3. Copy workflow templates to active `.github/workflows/` directory
4. Deploy backend to Railway with custom domains
5. Deploy frontend to GitHub Pages (or Railway)
**Short-term (Week 3-4):**
1. Implement landing page using `infra/frontend/LANDING_PAGE_PLAN.md`
2. Set up CodeQL security scanning
3. Configure Dependabot for automated updates
4. Create first design partner case study (placeholder)
**Medium-term (Month 2-3):**
1. Refine based on real traffic and feedback
2. Add analytics (Google Analytics/Plausible)
3. Create email drip campaign for demo requests
4. Launch private alpha
---
## READY FOR THE NEXT COMMAND, OPERATOR. ## READY FOR THE NEXT COMMAND, OPERATOR.
This master plan synthesizes: This master plan synthesizes:
@@ -1060,12 +1131,15 @@ This master plan synthesizes:
- ✅ Domain strategy (10+ domains with routing) - ✅ Domain strategy (10+ domains with routing)
- ✅ Agent orchestration (Atlas/Operator/Prism/Lucidia) - ✅ Agent orchestration (Atlas/Operator/Prism/Lucidia)
- ✅ Brand architecture (all sub-brands) - ✅ Brand architecture (all sub-brands)
-**PHASE 1 IMPLEMENTATION** (infrastructure files, workflows, documentation)
**Your next concrete action**: Start with Alexa's Next-Action Checklist, Item #1 (Migrate DNS to Cloudflare). **Your next concrete action**: Start with `infra/cloudflare/migrate_to_cloudflare.md` - migrate DNS to Cloudflare.
**Your north star**: Phase 1, Q1 deliverables - get the foundation solid, then build on it. **Your north star**: Phase 1, Q1 deliverables - get the foundation solid, then build on it.
**Your superpower**: You have the complete vision documented. Now execute methodically, one step at a time. **Your superpower**: You have the complete vision documented AND implemented. Now execute methodically, one step at a time.
**Implementation status**: ✅ Phase 1 infrastructure files ready for deployment
--- ---

View File

@@ -57,7 +57,12 @@ RAILWAY_ENVIRONMENT_ID=00000000-0000-0000-0000-000000000000
RAILWAY_DOMAIN=your-service.up.railway.app RAILWAY_DOMAIN=your-service.up.railway.app
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX/YYY/ZZZ SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX/YYY/ZZZ
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/XXX/YYY DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/XXX/YYY
# Cloudflare DNS and CDN
CLOUDFLARE_API_TOKEN=cloudflare-api-token-placeholder CLOUDFLARE_API_TOKEN=cloudflare-api-token-placeholder
CLOUDFLARE_ACCOUNT_ID=your-cloudflare-account-id
CLOUDFLARE_ZONE_ID=your-cloudflare-zone-id-for-blackroad-systems
CLOUDFLARE_EMAIL=your-cloudflare-email@example.com
# Optional cloud/API integrations # Optional cloud/API integrations
DIGITAL_OCEAN_API_KEY=your-digital-ocean-api-key DIGITAL_OCEAN_API_KEY=your-digital-ocean-api-key

View File

@@ -0,0 +1,770 @@
# Cloudflare DNS Migration Guide
## Complete Step-by-Step Guide for BlackRoad Domains
**Version:** 1.0
**Date:** 2025-11-18
**Estimated Time:** 2-4 hours for all domains
**Skill Level:** Intermediate
---
## Overview
This guide walks you through migrating BlackRoad domains from GoDaddy DNS to Cloudflare DNS. The migration provides:
-**Free SSL certificates** (automatic renewal)
-**Global CDN** (faster page loads worldwide)
-**DDoS protection** (automatic threat mitigation)
-**Better DNS performance** (anycast network)
-**Advanced features** (Workers, Zero Trust, edge functions)
-**Superior analytics** (traffic insights, security events)
**What you'll need:**
- Cloudflare account (free tier is sufficient)
- GoDaddy account with domain access
- 2-4 hours of time
- Basic command line familiarity (optional, for automation)
---
## Table of Contents
1. [Pre-Migration Checklist](#pre-migration-checklist)
2. [Phase 1: Set Up Cloudflare Account](#phase-1-set-up-cloudflare-account)
3. [Phase 2: Add Domains to Cloudflare](#phase-2-add-domains-to-cloudflare)
4. [Phase 3: Update Nameservers at GoDaddy](#phase-3-update-nameservers-at-godaddy)
5. [Phase 4: Configure DNS Records](#phase-4-configure-dns-records)
6. [Phase 5: Configure SSL/TLS](#phase-5-configure-ssltls)
7. [Phase 6: Optimize Performance](#phase-6-optimize-performance)
8. [Phase 7: Verify Migration](#phase-7-verify-migration)
9. [Phase 8: Automation Setup (Optional)](#phase-8-automation-setup-optional)
10. [Troubleshooting](#troubleshooting)
11. [Rollback Plan](#rollback-plan)
---
## Pre-Migration Checklist
Before starting, gather this information:
- [ ] **GoDaddy credentials** - Username and password
- [ ] **Current DNS records** - Document existing configuration
- [ ] **Email access** - For Cloudflare verification
- [ ] **Railway hostname** - Your production app URL (e.g., `your-app.up.railway.app`)
- [ ] **Current uptime** - Note if services are running properly
**⚠️ Important Notes:**
- Migration happens with **zero downtime** if done correctly
- DNS propagation takes 5-60 minutes (sometimes up to 24 hours)
- Keep GoDaddy account active (domain registration stays there)
- Only DNS management moves to Cloudflare
---
## Phase 1: Set Up Cloudflare Account
### Step 1.1: Create Cloudflare Account
1. Go to [https://dash.cloudflare.com/sign-up](https://dash.cloudflare.com/sign-up)
2. Enter your email address
3. Create a strong password
4. Verify your email address
### Step 1.2: Two-Factor Authentication (Recommended)
1. Click on your profile (top right)
2. Go to **My Profile****Authentication**
3. Enable **Two-Factor Authentication**
4. Save backup codes in a secure location
### Step 1.3: Get API Token
1. Go to **My Profile****API Tokens**
2. Click **Create Token**
3. Select **Edit zone DNS** template
4. Configure permissions:
- **Zone - DNS - Edit**
- **Zone - Zone - Read**
5. Select **Specific zones** → (you'll add zones in Phase 2)
6. Click **Continue to summary****Create Token**
7. **Copy the token immediately** (you won't see it again!)
8. Save it securely (we'll use it later for automation)
---
## Phase 2: Add Domains to Cloudflare
We'll start with the primary domain (`blackroad.systems`) and then add others.
### Step 2.1: Add blackroad.systems
1. From Cloudflare dashboard, click **Add a site**
2. Enter: `blackroad.systems`
3. Click **Add site**
4. Select **Free plan** → Click **Continue**
5. Cloudflare will scan existing DNS records from GoDaddy
6. Wait 30-60 seconds for scan to complete
### Step 2.2: Review Scanned Records
Cloudflare should detect existing records. Review them:
- ✅ Check for A records pointing to your server
- ✅ Check for CNAME records
- ✅ Check for MX records (email)
- ✅ Check for TXT records (SPF, verification)
**Common issues:**
- Some records might be missing → We'll add them manually later
- TTL values might be high → We'll adjust them
### Step 2.3: Get Nameservers
After scanning, Cloudflare will show 2 nameservers like:
```
aaaa.ns.cloudflare.com
bbbb.ns.cloudflare.com
```
**⚠️ IMPORTANT:** Copy these nameservers! You'll need them in Phase 3.
**Don't click "Done" yet** - we'll do that after updating nameservers at GoDaddy.
---
## Phase 3: Update Nameservers at GoDaddy
**⏰ Estimated Time:** 10 minutes + 5-60 minutes propagation
### Step 3.1: Log in to GoDaddy
1. Go to [https://account.godaddy.com](https://account.godaddy.com)
2. Sign in with your credentials
3. Go to **My Products****Domains**
### Step 3.2: Update Nameservers for blackroad.systems
1. Find `blackroad.systems` in your domain list
2. Click the three dots (...) → **Manage DNS**
3. Scroll down to **Nameservers** section
4. Click **Change** → Select **Enter my own nameservers (advanced)**
5. Remove existing nameservers
6. Add the 2 Cloudflare nameservers from Phase 2, Step 2.3:
```
aaaa.ns.cloudflare.com
bbbb.ns.cloudflare.com
```
7. Click **Save**
8. Confirm the change
**What happens now:**
- GoDaddy will propagate the nameserver change
- This takes 5-60 minutes (sometimes up to 24 hours)
- Your site will continue working during this time
### Step 3.3: Return to Cloudflare
1. Go back to Cloudflare dashboard
2. Click **Done, check nameservers**
3. Cloudflare will start checking for nameserver changes
4. You'll see status: **Pending Nameserver Update**
**⏳ Wait time:** 5-60 minutes for Cloudflare to detect the change
You can check status by:
- Refreshing the Cloudflare dashboard
- Running: `dig NS blackroad.systems` (should show Cloudflare nameservers)
- Using: [https://dnschecker.org](https://dnschecker.org)
---
## Phase 4: Configure DNS Records
Once Cloudflare shows **Active** status, configure DNS records.
### Step 4.1: Verify Existing Records
1. In Cloudflare dashboard, go to **DNS** → **Records**
2. Review scanned records
3. Remove any incorrect or outdated records
### Step 4.2: Add/Update Primary Records
#### Root Domain (blackroad.systems)
| Type | Name | Target | Proxy | TTL |
|------|------|--------|-------|-----|
| CNAME | @ | `your-app.up.railway.app` | ✅ Proxied | Auto |
**Steps:**
1. Click **Add record**
2. Type: **CNAME**
3. Name: **@** (represents root domain)
4. Target: **your-railway-app.up.railway.app** (replace with actual Railway URL)
5. Proxy status: **Proxied** (orange cloud icon)
6. TTL: **Auto**
7. Click **Save**
#### WWW Subdomain
| Type | Name | Target | Proxy | TTL |
|------|------|--------|-------|-----|
| CNAME | www | blackroad.systems | ✅ Proxied | Auto |
**Steps:**
1. Click **Add record**
2. Type: **CNAME**
3. Name: **www**
4. Target: **blackroad.systems**
5. Proxy status: **Proxied**
6. Click **Save**
#### API Subdomain
| Type | Name | Target | Proxy | TTL |
|------|------|--------|-------|-----|
| CNAME | api | `your-app.up.railway.app` | ✅ Proxied | Auto |
#### OS Subdomain
| Type | Name | Target | Proxy | TTL |
|------|------|--------|-------|-----|
| CNAME | os | blackroad.systems | ✅ Proxied | Auto |
### Step 4.3: Configure Email Records (If Applicable)
If you use Google Workspace, G Suite, or custom email:
#### SPF Record
| Type | Name | Content | TTL |
|------|------|---------|-----|
| TXT | @ | `v=spf1 include:_spf.google.com ~all` | Auto |
#### MX Records
| Type | Name | Content | Priority | TTL |
|------|------|---------|----------|-----|
| MX | @ | aspmx.l.google.com | 1 | Auto |
| MX | @ | alt1.aspmx.l.google.com | 5 | Auto |
| MX | @ | alt2.aspmx.l.google.com | 5 | Auto |
### Step 4.4: Verify Records
```bash
# Check DNS resolution
dig blackroad.systems
dig www.blackroad.systems
dig api.blackroad.systems
# Or use Cloudflare dashboard DNS checker
```
---
## Phase 5: Configure SSL/TLS
### Step 5.1: Set Encryption Mode
1. In Cloudflare dashboard, go to **SSL/TLS**
2. Set **Encryption mode** to **Full (strict)**
- This ensures encryption between Cloudflare and Railway
- Railway automatically provides SSL certificates
**⚠️ Important:** Do NOT use "Flexible" mode (insecure)
### Step 5.2: Enable Always Use HTTPS
1. Go to **SSL/TLS** → **Edge Certificates**
2. Enable **Always Use HTTPS**
- This redirects all HTTP traffic to HTTPS
3. Enable **Automatic HTTPS Rewrites**
- Fixes mixed content warnings
### Step 5.3: Enable HSTS (Optional but Recommended)
1. Still in **Edge Certificates**
2. Enable **HTTP Strict Transport Security (HSTS)**
3. Configuration:
- **Max Age:** 6 months (15768000 seconds)
- **Include subdomains:** ✅ Enabled
- **Preload:** ❌ Disabled (enable later when stable)
- **No-Sniff header:** ✅ Enabled
**⚠️ Warning:** HSTS is irreversible for the max-age period. Only enable when confident.
### Step 5.4: Enable TLS 1.3
1. Go to **SSL/TLS** → **Edge Certificates**
2. **Minimum TLS Version:** Set to **TLS 1.2** (or 1.3 if supported)
3. **TLS 1.3:** ✅ Enabled
### Step 5.5: Verify SSL Configuration
1. Visit: `https://blackroad.systems`
2. Click the padlock icon in browser
3. Verify certificate is valid and issued by Cloudflare
4. Check expiry date (should auto-renew)
**Test with SSL Labs:**
```
https://www.ssllabs.com/ssltest/analyze.html?d=blackroad.systems
```
---
## Phase 6: Optimize Performance
### Step 6.1: Configure Caching
1. Go to **Caching** → **Configuration**
2. **Caching Level:** Standard
3. **Browser Cache TTL:** Respect Existing Headers
### Step 6.2: Enable Auto Minify
1. Go to **Speed** → **Optimization**
2. Enable **Auto Minify**:
- ✅ JavaScript
- ✅ CSS
- ✅ HTML
### Step 6.3: Enable Brotli Compression
1. Still in **Speed** → **Optimization**
2. Enable **Brotli** compression (better than gzip)
### Step 6.4: Create Page Rules
1. Go to **Rules** → **Page Rules**
2. Create rule for API bypass:
**Rule 1: API Cache Bypass**
```
URL: *blackroad.systems/api/*
Settings:
- Cache Level: Bypass
```
**Rule 2: WWW Redirect**
```
URL: www.blackroad.systems/*
Settings:
- Forwarding URL: 301 redirect to https://blackroad.systems/$1
```
**Note:** Free plan allows 3 page rules. Use them wisely!
---
## Phase 7: Verify Migration
### Step 7.1: DNS Verification
```bash
# Check DNS propagation
dig blackroad.systems
# Check with multiple tools
dig @8.8.8.8 blackroad.systems
dig @1.1.1.1 blackroad.systems
# Or use online tool
# https://dnschecker.org
```
**Expected results:**
- Should resolve to Cloudflare IP addresses
- CNAME records should point to Railway
- Nameservers should be Cloudflare
### Step 7.2: HTTP/HTTPS Verification
```bash
# Test HTTP → HTTPS redirect
curl -I http://blackroad.systems
# Should return: 301 Moved Permanently
# Location: https://blackroad.systems/
# Test HTTPS
curl -I https://blackroad.systems
# Should return: 200 OK
# Test WWW → apex redirect
curl -I https://www.blackroad.systems
# Should redirect to https://blackroad.systems
```
### Step 7.3: SSL Certificate Check
```bash
# Check SSL certificate
openssl s_client -connect blackroad.systems:443 -servername blackroad.systems
# Look for:
# - Issuer: Cloudflare
# - Valid dates
# - No errors
```
### Step 7.4: Application Functionality
1. Visit `https://blackroad.systems`
2. Test all major features:
- [ ] Page loads correctly
- [ ] No mixed content warnings
- [ ] API calls work
- [ ] Authentication works
- [ ] Static assets load (CSS, JS, images)
### Step 7.5: Automated Validation
```bash
# Use the validation script
cd /path/to/BlackRoad-Operating-System
python scripts/cloudflare/validate_dns.py --domain blackroad.systems
# This checks:
# - DNS resolution
# - SSL certificate validity
# - HTTP accessibility
# - Redirect configuration
```
---
## Phase 8: Automation Setup (Optional)
### Step 8.1: Install Script Dependencies
```bash
# Navigate to project
cd /path/to/BlackRoad-Operating-System
# Install Python dependencies
pip install -r scripts/cloudflare/requirements.txt
```
### Step 8.2: Set Up Environment Variables
```bash
# Create .env file (DO NOT COMMIT)
cat >> .env << EOF
CF_API_TOKEN=your-cloudflare-api-token
CF_ZONE_ID=your-zone-id
EOF
# Or add to shell profile
echo 'export CF_API_TOKEN="your-token"' >> ~/.bashrc
echo 'export CF_ZONE_ID="your-zone-id"' >> ~/.bashrc
source ~/.bashrc
```
### Step 8.3: Update domains.yaml
Edit `ops/domains.yaml` to reflect your Cloudflare configuration:
```yaml
domains:
- name: "blackroad.systems"
type: "root"
provider: "cloudflare"
mode: "dns"
record:
type: "CNAME"
value: "your-actual-railway-app.up.railway.app"
proxied: true
- name: "blackroad.ai"
type: "root"
provider: "cloudflare"
mode: "dns"
record:
type: "CNAME"
value: "blackroad.systems"
proxied: true
```
### Step 8.4: Test Automation
```bash
# Dry run (shows what would change)
python scripts/cloudflare/sync_dns.py --dry-run
# Apply changes
python scripts/cloudflare/sync_dns.py
# Validate
python scripts/cloudflare/validate_dns.py --all
```
### Step 8.5: Set Up GitHub Actions (Optional)
Add secrets to GitHub:
```bash
gh secret set CF_API_TOKEN
gh secret set CF_ZONE_ID
```
Create workflow file (`.github/workflows/sync-cloudflare-dns.yml`):
```yaml
name: Sync Cloudflare DNS
on:
push:
paths:
- 'ops/domains.yaml'
branches:
- main
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- run: pip install -r scripts/cloudflare/requirements.txt
- run: python scripts/cloudflare/sync_dns.py
env:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
```
---
## Troubleshooting
### Issue: DNS Not Resolving
**Symptoms:** `dig blackroad.systems` returns no results
**Causes:**
- Nameservers not updated at GoDaddy
- DNS propagation not complete
- Records not configured in Cloudflare
**Solutions:**
1. Check nameservers at GoDaddy
2. Wait 5-60 minutes for propagation
3. Check Cloudflare zone status (should be "Active")
4. Verify DNS records exist in Cloudflare
### Issue: SSL Certificate Errors
**Symptoms:** Browser shows "Not Secure" or certificate warning
**Causes:**
- SSL/TLS mode incorrect
- Railway app doesn't have valid certificate
- Certificate provisioning in progress
**Solutions:**
1. Set SSL/TLS mode to "Full (strict)"
2. Verify Railway app has SSL
3. Wait 5-10 minutes for certificate provisioning
4. Clear browser cache and retry
### Issue: Site Not Loading (403/502 Errors)
**Symptoms:** Site returns 403 Forbidden or 502 Bad Gateway
**Causes:**
- Railway app not running
- Incorrect CNAME target
- Cloudflare firewall blocking
**Solutions:**
1. Check Railway app status and logs
2. Verify CNAME points to correct Railway URL
3. Check Cloudflare firewall rules
4. Disable Cloudflare proxy temporarily (DNS-only) to test
### Issue: Mixed Content Warnings
**Symptoms:** Some assets load as insecure (http://)
**Causes:**
- Hard-coded HTTP URLs in code
- External resources using HTTP
**Solutions:**
1. Enable "Automatic HTTPS Rewrites" in Cloudflare
2. Update hard-coded URLs to HTTPS
3. Use protocol-relative URLs: `//example.com/asset.js`
### Issue: Email Not Working
**Symptoms:** Emails not sending/receiving
**Causes:**
- MX records not migrated
- SPF/DKIM records missing
**Solutions:**
1. Add MX records in Cloudflare (from Phase 4.3)
2. Add SPF TXT record
3. Add DKIM records if using custom email
4. Verify with: `dig MX blackroad.systems`
---
## Rollback Plan
If you need to revert to GoDaddy DNS:
### Quick Rollback
1. Go to GoDaddy → Domains → blackroad.systems
2. Nameservers → Change to **GoDaddy defaults**
3. Wait 5-60 minutes for propagation
4. Site will revert to GoDaddy DNS
**⚠️ Note:** You can keep the Cloudflare account and try again later.
### Gradual Rollback
If experiencing issues but want to keep trying:
1. In Cloudflare, change proxy status to **DNS Only** (gray cloud)
2. This bypasses Cloudflare's proxy but keeps DNS
3. Troubleshoot issues
4. Re-enable proxy when fixed
---
## Next Steps After Migration
### For All Remaining Domains
Repeat the process for:
- blackroad.ai
- blackroad.network
- blackroad.me
- lucidia.earth
- aliceqi.com
- blackroadqi.com
- roadwallet.com
- aliceos.io
- blackroadquantum.com
**Pro tip:** After doing blackroad.systems, the others are easier!
### Monitoring and Maintenance
1. **Set up monitoring:**
- Uptime monitoring (UptimeRobot, Pingdom)
- SSL certificate expiry monitoring
- Performance monitoring (Cloudflare Analytics)
2. **Review quarterly:**
- DNS records (remove unused)
- Page rules and caching
- Security settings
- Analytics and performance
3. **Stay updated:**
- Review Cloudflare changelog
- Test new features in sandbox
- Keep API tokens rotated
---
## Migration Checklist
Use this to track your progress:
### Pre-Migration
- [ ] GoDaddy credentials ready
- [ ] Current DNS records documented
- [ ] Railway hostname confirmed
- [ ] Cloudflare account created
### Cloudflare Setup
- [ ] API token generated and saved
- [ ] Domain added to Cloudflare
- [ ] DNS records scanned
- [ ] Nameservers noted
### GoDaddy Update
- [ ] Nameservers updated at GoDaddy
- [ ] Change confirmed
- [ ] Propagation completed (zone shows "Active")
### DNS Configuration
- [ ] Root domain CNAME added
- [ ] WWW subdomain added
- [ ] API subdomain added
- [ ] OS subdomain added
- [ ] Email records added (if applicable)
### SSL/TLS
- [ ] Encryption mode set to Full (strict)
- [ ] Always Use HTTPS enabled
- [ ] Automatic HTTPS Rewrites enabled
- [ ] HSTS configured (optional)
- [ ] TLS 1.3 enabled
### Performance
- [ ] Auto Minify enabled
- [ ] Brotli compression enabled
- [ ] Page rules configured
- [ ] Caching configured
### Verification
- [ ] DNS resolution verified
- [ ] SSL certificate valid
- [ ] HTTP → HTTPS redirect working
- [ ] WWW → apex redirect working
- [ ] Site accessible and functional
- [ ] API endpoints working
- [ ] Email working (if applicable)
### Automation (Optional)
- [ ] Python dependencies installed
- [ ] Environment variables set
- [ ] domains.yaml updated
- [ ] Automation scripts tested
- [ ] GitHub Actions configured (optional)
---
## Resources
### Documentation
- [Cloudflare DNS Blueprint](../infra/cloudflare/CLOUDFLARE_DNS_BLUEPRINT.md)
- [Scripts README](../scripts/cloudflare/README.md)
- [Domain Configuration](../ops/domains.yaml)
### External Links
- [Cloudflare Dashboard](https://dash.cloudflare.com)
- [Cloudflare API Docs](https://developers.cloudflare.com/api/)
- [DNS Checker Tool](https://dnschecker.org)
- [SSL Labs Test](https://www.ssllabs.com/ssltest/)
- [Railway Dashboard](https://railway.app/dashboard)
### Support
- Cloudflare Community: https://community.cloudflare.com/
- Railway Discord: https://discord.gg/railway
- BlackRoad GitHub Issues: https://github.com/blackboxprogramming/BlackRoad-Operating-System/issues
---
**Congratulations!** 🎉
You've successfully migrated to Cloudflare DNS. Your sites now benefit from:
- Global CDN and faster performance
- Free SSL with auto-renewal
- DDoS protection
- Advanced security features
- Better analytics and insights
**Questions or issues?** Check the troubleshooting section or open a GitHub issue.
---
**Last Updated:** 2025-11-18
**Maintainer:** BlackRoad DevOps Team
**Version:** 1.0

View File

@@ -335,118 +335,132 @@ After DNS setup for each domain:
--- ---
## Automation Script ## Implementation Files
**File**: `scripts/cloudflare/sync_dns.py` This blueprint is now implemented in the following files:
```python ### 1. DNS Records Configuration
#!/usr/bin/env python3 **File**: `infra/cloudflare/records.yaml`
"""
Sync DNS records from ops/domains.yaml to Cloudflare
Usage: This file is the **single source of truth** for all DNS records across all BlackRoad domains. It includes:
export CF_API_TOKEN="your-token" - Complete record definitions (type, name, content, TTL, proxy status)
export CF_ZONE_ID="your-zone-id" - Domain metadata (zone ID, phase, priority)
python scripts/cloudflare/sync_dns.py - Human-readable comments for each record
""" - Organized by phase (Phase 1 active domains, Phase 2 future domains)
import os **Quick reference**: See `records.yaml` for the exact DNS configuration to apply.
import sys
import yaml
import requests
from typing import Dict, List
CF_API_TOKEN = os.getenv("CF_API_TOKEN") ### 2. Migration Guide
CF_ZONE_ID = os.getenv("CF_ZONE_ID") **File**: `infra/cloudflare/migrate_to_cloudflare.md`
CF_API_BASE = "https://api.cloudflare.com/client/v4"
def load_domains() -> Dict: A **step-by-step guide** for migrating DNS from GoDaddy to Cloudflare. Designed for human operators (Alexa) with:
"""Load domain config from ops/domains.yaml""" - Detailed instructions with screenshots references
with open("ops/domains.yaml") as f: - Mobile-friendly (can be done from iPhone)
return yaml.safe_load(f) - Troubleshooting section
- Verification steps
- Checklist for tracking progress
def get_existing_records(zone_id: str) -> List[Dict]: **Quick reference**: Follow this guide to migrate each domain from GoDaddy DNS to Cloudflare DNS.
"""Fetch all DNS records for a zone"""
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records"
headers = {
"Authorization": f"Bearer {CF_API_TOKEN}",
"Content-Type": "application/json"
}
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()["result"]
def create_dns_record(zone_id: str, record: Dict) -> Dict: ### 3. Automation Script
"""Create a DNS record""" **File**: `infra/cloudflare/cloudflare_dns_sync.py`
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records"
headers = {
"Authorization": f"Bearer {CF_API_TOKEN}",
"Content-Type": "application/json"
}
response = requests.post(url, headers=headers, json=record)
response.raise_for_status()
return response.json()["result"]
def update_dns_record(zone_id: str, record_id: str, record: Dict) -> Dict: An **idempotent Python script** that syncs DNS records from `records.yaml` to Cloudflare via API. Features:
"""Update a DNS record""" - Reads structured YAML configuration
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records/{record_id}" - Creates, updates, and optionally deletes DNS records
headers = { - Dry-run mode for safe testing
"Authorization": f"Bearer {CF_API_TOKEN}", - Domain and phase filtering
"Content-Type": "application/json" - Comprehensive logging
}
response = requests.put(url, headers=headers, json=record)
response.raise_for_status()
return response.json()["result"]
def sync_records(): **Usage**:
"""Sync DNS records from domains.yaml to Cloudflare"""
if not CF_API_TOKEN or not CF_ZONE_ID:
print("Error: CF_API_TOKEN and CF_ZONE_ID must be set")
sys.exit(1)
config = load_domains()
existing = get_existing_records(CF_ZONE_ID)
# Build index of existing records
existing_index = {
f"{r['type']}:{r['name']}": r for r in existing
}
for domain in config.get("domains", []):
if domain.get("mode") != "dns":
continue
record_data = {
"type": domain["record"]["type"],
"name": domain["name"],
"content": domain["record"]["value"],
"ttl": 1, # Auto
"proxied": True # Enable Cloudflare proxy
}
key = f"{record_data['type']}:{record_data['name']}"
if key in existing_index:
# Update existing
record_id = existing_index[key]["id"]
print(f"Updating: {key}")
update_dns_record(CF_ZONE_ID, record_id, record_data)
else:
# Create new
print(f"Creating: {key}")
create_dns_record(CF_ZONE_ID, record_data)
print("✅ DNS sync complete!")
if __name__ == "__main__":
sync_records()
```
**Make executable**:
```bash ```bash
chmod +x scripts/cloudflare/sync_dns.py # Set Cloudflare API token
export CF_API_TOKEN="your-cloudflare-api-token"
# Dry run (safe - shows changes without applying)
python infra/cloudflare/cloudflare_dns_sync.py --dry-run
# Sync specific domain
python infra/cloudflare/cloudflare_dns_sync.py --domain blackroad.systems
# Sync all Phase 1 domains
python infra/cloudflare/cloudflare_dns_sync.py --phase 1
# Apply all changes
python infra/cloudflare/cloudflare_dns_sync.py
``` ```
**Requirements**:
```bash
pip install pyyaml requests
```
## How These Files Connect to Railway + GitHub
### DNS → Railway Flow
1. **Cloudflare DNS** receives user request for `os.blackroad.systems`
2. **CNAME record** (from `records.yaml`) points to `blackroad-os-production.up.railway.app`
3. **Cloudflare CDN** proxies request (SSL, caching, DDoS protection)
4. **Railway** receives request and routes to FastAPI backend
5. **FastAPI** serves Pocket OS frontend from `backend/static/`
### Railway Custom Domains
For each subdomain pointing to Railway, you must also:
1. Add the custom domain in **Railway dashboard**:
- Service → Settings → Networking → Custom Domains
- Add domain (e.g., `os.blackroad.systems`)
- Railway auto-provisions SSL certificate (Let's Encrypt)
2. Wait for Railway to show **green checkmark** (SSL ready)
3. Verify: Visit `https://os.blackroad.systems` - should show valid SSL 🔒
### GitHub Actions Integration
The DNS sync script can be automated via GitHub Actions:
**Workflow file** (create at `.github/workflows/dns-sync.yml`):
```yaml
name: Sync Cloudflare DNS
on:
push:
paths:
- 'infra/cloudflare/records.yaml'
workflow_dispatch:
jobs:
sync-dns:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: pip install pyyaml requests
- name: Sync DNS records
env:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
run: |
python infra/cloudflare/cloudflare_dns_sync.py --phase 1
```
**Required GitHub Secrets**:
- `CF_API_TOKEN` - Cloudflare API token with Zone.DNS edit permissions
**How it works**:
1. Push changes to `records.yaml`
2. GitHub Action runs automatically
3. Script syncs DNS records to Cloudflare
4. Changes are live within seconds
--- ---
## Troubleshooting ## Troubleshooting

View File

@@ -0,0 +1,484 @@
#!/usr/bin/env python3
"""
Cloudflare DNS Sync Script
===========================
Syncs DNS records from records.yaml to Cloudflare via API.
This script is idempotent - safe to run multiple times.
How to run:
-----------
1. Get your Cloudflare API token:
- Go to Cloudflare dashboard → My Profile → API Tokens
- Create token with "Zone.DNS" edit permissions
- Copy the token
2. Get your zone IDs:
- Go to each domain in Cloudflare dashboard
- Copy the Zone ID from the Overview page (right sidebar)
- Update records.yaml with the zone IDs
3. Set environment variables:
export CF_API_TOKEN="your-cloudflare-api-token"
4. Run the script:
python infra/cloudflare/cloudflare_dns_sync.py
Optional flags:
--------------
--dry-run Show what would change without making changes
--domain NAME Only sync specific domain (e.g., blackroad.systems)
--phase N Only sync domains in specific phase (1, 2, etc.)
--delete-extra Delete DNS records not in records.yaml (use carefully!)
Examples:
---------
# Dry run (safe - shows changes without applying)
python infra/cloudflare/cloudflare_dns_sync.py --dry-run
# Sync only blackroad.systems
python infra/cloudflare/cloudflare_dns_sync.py --domain blackroad.systems
# Sync only Phase 1 domains
python infra/cloudflare/cloudflare_dns_sync.py --phase 1
# Sync and delete extra records (DANGEROUS - be careful!)
python infra/cloudflare/cloudflare_dns_sync.py --delete-extra
Requirements:
------------
pip install pyyaml requests
Author: BlackRoad OS Infrastructure Team
Version: 1.0
Date: 2025-11-18
"""
import os
import sys
import argparse
import logging
from typing import Dict, List, Tuple
from pathlib import Path
try:
import yaml
except ImportError:
print("Error: pyyaml is not installed. Run: pip install pyyaml")
sys.exit(1)
try:
import requests
except ImportError:
print("Error: requests is not installed. Run: pip install requests")
sys.exit(1)
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
# Cloudflare API configuration
CF_API_BASE = "https://api.cloudflare.com/client/v4"
CF_API_TOKEN = os.getenv("CF_API_TOKEN")
class CloudflareAPI:
"""Simple wrapper for Cloudflare API calls."""
def __init__(self, api_token: str):
if not api_token:
raise ValueError("CF_API_TOKEN environment variable not set")
self.api_token = api_token
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
})
def _request(self, method: str, endpoint: str, **kwargs) -> Dict:
"""Make a request to Cloudflare API."""
url = f"{CF_API_BASE}{endpoint}"
response = self.session.request(method, url, **kwargs)
# Parse JSON response
try:
data = response.json()
except ValueError:
logger.error(f"Invalid JSON response from {url}")
response.raise_for_status()
return {}
# Check for API errors
if not data.get("success", False):
errors = data.get("errors", [])
error_msg = ", ".join([e.get("message", "Unknown error") for e in errors])
logger.error(f"Cloudflare API error: {error_msg}")
raise Exception(f"Cloudflare API error: {error_msg}")
return data.get("result", {})
def get_dns_records(self, zone_id: str) -> List[Dict]:
"""Get all DNS records for a zone."""
logger.info(f"Fetching DNS records for zone {zone_id}")
records = []
page = 1
per_page = 100
while True:
result = self._request(
"GET",
f"/zones/{zone_id}/dns_records",
params={"page": page, "per_page": per_page}
)
if isinstance(result, list):
records.extend(result)
if len(result) < per_page:
break
page += 1
elif isinstance(result, dict):
# Newer API returns paginated result
records.extend(result.get("result", []))
info = result.get("result_info", {})
if info.get("page", 1) >= info.get("total_pages", 1):
break
page += 1
else:
break
logger.info(f"Found {len(records)} existing DNS records")
return records
def create_dns_record(self, zone_id: str, record: Dict) -> Dict:
"""Create a new DNS record."""
logger.info(f"Creating {record['type']} record: {record['name']}{record['content']}")
return self._request("POST", f"/zones/{zone_id}/dns_records", json=record)
def update_dns_record(self, zone_id: str, record_id: str, record: Dict) -> Dict:
"""Update an existing DNS record."""
logger.info(f"Updating {record['type']} record: {record['name']}{record['content']}")
return self._request("PUT", f"/zones/{zone_id}/dns_records/{record_id}", json=record)
def delete_dns_record(self, zone_id: str, record_id: str) -> Dict:
"""Delete a DNS record."""
logger.warning(f"Deleting DNS record: {record_id}")
return self._request("DELETE", f"/zones/{zone_id}/dns_records/{record_id}")
def load_records_config() -> List[Dict]:
"""Load DNS records from records.yaml."""
config_path = Path(__file__).parent / "records.yaml"
if not config_path.exists():
logger.error(f"Config file not found: {config_path}")
sys.exit(1)
logger.info(f"Loading DNS configuration from {config_path}")
with open(config_path, 'r') as f:
data = yaml.safe_load(f)
if not isinstance(data, list):
logger.error("Invalid records.yaml format - expected list of domains")
sys.exit(1)
return data
def normalize_record_name(name: str, domain: str) -> str:
"""Normalize record name for comparison."""
if name == '@':
return domain
elif name.endswith(f'.{domain}'):
return name
else:
return f"{name}.{domain}"
def records_equal(r1: Dict, r2: Dict) -> bool:
"""Check if two DNS records are functionally equal."""
# Compare essential fields
if r1.get('type') != r2.get('type'):
return False
if r1.get('content') != r2.get('content'):
return False
if r1.get('proxied', False) != r2.get('proxied', False):
return False
# For MX records, also compare priority
if r1.get('type') == 'MX':
if r1.get('priority') != r2.get('priority'):
return False
return True
def build_cloudflare_record(record: Dict, domain: str) -> Dict:
"""Build a Cloudflare API record payload from our config format."""
cf_record = {
'type': record['type'],
'name': normalize_record_name(record['name'], domain),
'content': record['content'],
'ttl': record.get('ttl', 1),
}
# Add proxied flag (not for MX, TXT, some others)
if record['type'] in ['A', 'AAAA', 'CNAME']:
cf_record['proxied'] = record.get('proxied', False)
# Add priority for MX records
if record['type'] == 'MX':
cf_record['priority'] = record.get('priority', 10)
# Add comment if supported (newer Cloudflare API)
if 'comment' in record:
cf_record['comment'] = record['comment']
return cf_record
def sync_domain(
api: CloudflareAPI,
domain_config: Dict,
dry_run: bool = False,
delete_extra: bool = False
) -> Tuple[int, int, int, int]:
"""
Sync DNS records for a single domain.
Returns: (created, updated, deleted, unchanged) counts
"""
domain = domain_config['domain']
zone_id = domain_config.get('zone_id', '')
desired_records = domain_config.get('records', [])
logger.info(f"\n{'='*60}")
logger.info(f"Syncing domain: {domain}")
logger.info(f"Zone ID: {zone_id}")
logger.info(f"Desired records: {len(desired_records)}")
logger.info(f"{'='*60}")
if not zone_id or 'REPLACE' in zone_id:
logger.error(f"Skipping {domain} - Zone ID not configured in records.yaml")
return (0, 0, 0, 0)
# Get existing records from Cloudflare
try:
existing_records = api.get_dns_records(zone_id)
except Exception as e:
logger.error(f"Failed to fetch DNS records for {domain}: {e}")
return (0, 0, 0, 0)
# Build index of existing records
existing_index = {}
for record in existing_records:
key = f"{record['type']}:{record['name']}"
existing_index[key] = record
# Track changes
created = 0
updated = 0
deleted = 0
unchanged = 0
# Process desired records
for desired_record in desired_records:
cf_record = build_cloudflare_record(desired_record, domain)
record_type = cf_record['type']
record_name = cf_record['name']
key = f"{record_type}:{record_name}"
if key in existing_index:
# Record exists - check if update needed
existing = existing_index[key]
if records_equal(cf_record, existing):
logger.debug(f"✓ Unchanged: {key}")
unchanged += 1
else:
logger.info(f"↻ Update needed: {key}")
logger.info(f" Current: {existing.get('content')}")
logger.info(f" Desired: {cf_record.get('content')}")
if not dry_run:
try:
api.update_dns_record(zone_id, existing['id'], cf_record)
updated += 1
except Exception as e:
logger.error(f"Failed to update {key}: {e}")
else:
logger.info(f" [DRY RUN] Would update record")
updated += 1
# Mark as processed
del existing_index[key]
else:
# Record doesn't exist - create it
logger.info(f"+ Create needed: {key}")
if not dry_run:
try:
api.create_dns_record(zone_id, cf_record)
created += 1
except Exception as e:
logger.error(f"Failed to create {key}: {e}")
else:
logger.info(f" [DRY RUN] Would create record")
created += 1
# Handle extra records not in config
if existing_index:
logger.info(f"\nFound {len(existing_index)} extra records not in config:")
for key, record in existing_index.items():
logger.info(f" - {key}{record.get('content')}")
if delete_extra:
logger.warning("Deleting extra records (--delete-extra flag set)")
for key, record in existing_index.items():
if not dry_run:
try:
api.delete_dns_record(zone_id, record['id'])
deleted += 1
except Exception as e:
logger.error(f"Failed to delete {key}: {e}")
else:
logger.warning(f" [DRY RUN] Would delete {key}")
deleted += 1
else:
logger.info(" (Use --delete-extra to remove these records)")
# Summary
logger.info(f"\nSummary for {domain}:")
logger.info(f" Created: {created}")
logger.info(f" Updated: {updated}")
logger.info(f" Deleted: {deleted}")
logger.info(f" Unchanged: {unchanged}")
return (created, updated, deleted, unchanged)
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Sync DNS records from records.yaml to Cloudflare",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would change without making changes"
)
parser.add_argument(
"--domain",
type=str,
help="Only sync specific domain (e.g., blackroad.systems)"
)
parser.add_argument(
"--phase",
type=int,
choices=[1, 2, 3],
help="Only sync domains in specific phase"
)
parser.add_argument(
"--delete-extra",
action="store_true",
help="Delete DNS records not in records.yaml (use carefully!)"
)
parser.add_argument(
"--verbose",
action="store_true",
help="Enable verbose logging"
)
args = parser.parse_args()
if args.verbose:
logger.setLevel(logging.DEBUG)
if args.dry_run:
logger.info("🔍 DRY RUN MODE - No changes will be made\n")
# Load configuration
try:
domains = load_records_config()
except Exception as e:
logger.error(f"Failed to load configuration: {e}")
sys.exit(1)
# Initialize API client
try:
api = CloudflareAPI(CF_API_TOKEN)
except ValueError as e:
logger.error(str(e))
logger.error("\nTo get your Cloudflare API token:")
logger.error("1. Go to Cloudflare dashboard → My Profile → API Tokens")
logger.error("2. Create token with 'Zone.DNS' edit permissions")
logger.error("3. Set environment variable: export CF_API_TOKEN='your-token'")
sys.exit(1)
# Filter domains
filtered_domains = []
for domain_config in domains:
# Filter by specific domain
if args.domain and domain_config['domain'] != args.domain:
continue
# Filter by phase
if args.phase and domain_config.get('phase') != args.phase:
continue
filtered_domains.append(domain_config)
if not filtered_domains:
logger.warning("No domains matched your filters")
sys.exit(0)
logger.info(f"Processing {len(filtered_domains)} domain(s)\n")
# Sync each domain
total_created = 0
total_updated = 0
total_deleted = 0
total_unchanged = 0
for domain_config in filtered_domains:
created, updated, deleted, unchanged = sync_domain(
api,
domain_config,
dry_run=args.dry_run,
delete_extra=args.delete_extra
)
total_created += created
total_updated += updated
total_deleted += deleted
total_unchanged += unchanged
# Final summary
logger.info(f"\n{'='*60}")
logger.info("OVERALL SUMMARY")
logger.info(f"{'='*60}")
logger.info(f"Domains processed: {len(filtered_domains)}")
logger.info(f"Records created: {total_created}")
logger.info(f"Records updated: {total_updated}")
logger.info(f"Records deleted: {total_deleted}")
logger.info(f"Records unchanged: {total_unchanged}")
logger.info(f"{'='*60}")
if args.dry_run:
logger.info("\n🔍 This was a DRY RUN - no changes were made")
logger.info("Run without --dry-run to apply changes")
else:
logger.info("\n✅ DNS sync complete!")
sys.exit(0)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,578 @@
# Migrate DNS to Cloudflare - Step-by-Step Guide
**Version:** 1.0
**Date:** 2025-11-18
**For:** Alexa Louise Amundson (Cadillac)
**Time Required:** 30-60 minutes per domain
**Difficulty:** Beginner-friendly
---
## Overview
This guide will walk you through migrating DNS management from GoDaddy to Cloudflare for all BlackRoad domains. This migration will give you:
-**Free SSL certificates** (automatic)
-**CDN** (content delivery network for faster loading)
-**DDoS protection** (automatic security)
-**Better performance** (global anycast network)
-**Advanced features** (Workers, Zero Trust, analytics)
**Important**: You'll keep your domain registered with GoDaddy. We're only moving the DNS management to Cloudflare.
---
## Prerequisites
Before you start, make sure you have:
- [x] Access to your **GoDaddy account** (where domains are registered)
- [x] A **Cloudflare account** (create free at https://cloudflare.com if you don't have one)
- [x] Access to your **Railway account** (to get your app URLs)
- [x] About **30-60 minutes** per domain (can do one at a time)
- [x] Your **iPhone** or computer (both work fine)
---
## Part 1: Get Your Railway App URLs
Before configuring DNS, you need to know your Railway app URLs.
### Step 1.1: Log into Railway
1. Go to https://railway.app
2. Log in with your account
3. Select your **BlackRoad-Operating-System** project
### Step 1.2: Find Your Production Backend URL
1. Click on your **backend** service
2. Click **Settings****Networking**
3. Look for **Public Networking** section
4. You'll see a URL like: `blackroad-os-production.up.railway.app`
5. **Copy this URL** - you'll need it later
**Write it down here:**
```
Production Backend URL: _________________________________
```
### Step 1.3: Find Your Staging Backend URL (if exists)
If you have a staging environment:
1. Select your staging service
2. Repeat the same steps
3. Copy the staging URL
**Write it down here:**
```
Staging Backend URL: _________________________________
```
---
## Part 2: Add Your First Domain to Cloudflare
We'll start with **blackroad.systems** (the main domain). Once you understand the process, you can repeat for other domains.
### Step 2.1: Add Site to Cloudflare
1. Log into **Cloudflare** (https://dash.cloudflare.com)
2. Click **"Add a site"** (big blue button on the right)
3. Enter your domain: `blackroad.systems`
4. Click **"Add site"**
### Step 2.2: Select Free Plan
1. Cloudflare shows you plan options
2. Select **"Free"** plan ($0/month)
3. Click **"Continue"**
### Step 2.3: Review DNS Records
1. Cloudflare automatically **scans** your existing DNS records from GoDaddy
2. You'll see a list of records it found
3. Review them - **don't worry if it doesn't look perfect** yet
4. Click **"Continue"** at the bottom
**Note:** We'll configure the correct DNS records later using `records.yaml`.
### Step 2.4: Get Your Cloudflare Nameservers
This is the most important step!
1. Cloudflare will show you **2 nameservers** like:
```
aaaa.ns.cloudflare.com
bbbb.ns.cloudflare.com
```
(The actual names will be different - they're unique to your account)
2. **Write them down carefully:**
```
Nameserver 1: _________________________________
Nameserver 2: _________________________________
```
3. **Keep this tab open** - you'll come back to it!
---
## Part 3: Update Nameservers at GoDaddy
Now we'll tell GoDaddy to use Cloudflare's nameservers instead of its own.
### Step 3.1: Log into GoDaddy
1. Open a **new tab** (keep Cloudflare tab open)
2. Go to https://godaddy.com
3. Log in with your account
4. Click **"My Domains"** (or go to https://dcc.godaddy.com/manage/domains)
### Step 3.2: Manage Domain Settings
1. Find **blackroad.systems** in your domain list
2. Click the **three dots** (•••) next to it
3. Click **"Manage DNS"**
### Step 3.3: Change Nameservers
1. Scroll down to the **"Nameservers"** section
2. Click **"Change"** button
3. Select **"I'll use my own nameservers"** (or "Custom")
4. You'll see text boxes for nameservers
5. **Enter your Cloudflare nameservers:**
- Nameserver 1: (paste the first Cloudflare nameserver)
- Nameserver 2: (paste the second Cloudflare nameserver)
6. Click **"Save"**
7. GoDaddy will show a warning: *"Changing nameservers will affect your DNS..."*
- This is normal - click **"Continue"** or **"Yes, I'm sure"**
**Important:** DNS propagation can take 5-60 minutes. Usually it's faster (5-15 minutes).
---
## Part 4: Verify DNS is Active in Cloudflare
Now we wait for DNS propagation and verify everything works.
### Step 4.1: Return to Cloudflare Tab
1. Go back to your **Cloudflare tab**
2. After changing nameservers, click **"Done, check nameservers"**
3. Cloudflare will start checking (this can take 5-60 minutes)
### Step 4.2: Wait for "Active" Status
**Option A: Wait for Email**
- Cloudflare will email you when the domain is active
- Subject: "Your site is now active on a Cloudflare Free plan"
**Option B: Check Dashboard**
1. Go to Cloudflare dashboard → Your site
2. Look at the status at the top
3. Wait for it to change from **"Pending"** to **"Active"**
**While you wait:** This is a good time to grab coffee ☕ or work on something else for 15-30 minutes.
### Step 4.3: Verify with DNS Checker
Once Cloudflare shows "Active", verify DNS is working:
1. Go to https://dnschecker.org
2. Enter your domain: `blackroad.systems`
3. Select record type: **CNAME** (or A)
4. Click **"Search"**
5. You should see Cloudflare IPs across different locations
**Green checkmarks** = DNS has propagated in that region 🎉
---
## Part 5: Configure DNS Records
Now that Cloudflare is managing your DNS, let's set up the correct records.
### Step 5.1: Get Your Zone ID
1. In Cloudflare dashboard, select **blackroad.systems**
2. Scroll down on the **Overview** page
3. On the right sidebar, find **"Zone ID"**
4. Click to **copy** it
**Write it down:**
```
Zone ID for blackroad.systems: _________________________________
```
### Step 5.2: Update records.yaml
1. Open `infra/cloudflare/records.yaml` in your code editor
2. Find the `blackroad.systems` section
3. Replace `REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE` with your actual Zone ID
4. Replace `blackroad-os-production.up.railway.app` with your actual Railway URL (from Part 1)
5. **Save the file**
### Step 5.3: Configure Records Manually (Option A - Browser)
**For each record in `records.yaml`:**
1. Go to Cloudflare → **DNS** → **Records**
2. Click **"Add record"**
3. Fill in:
- **Type**: (e.g., CNAME)
- **Name**: (e.g., @ for root, www for www subdomain)
- **Target**: (e.g., your Railway URL)
- **Proxy status**: Click the cloud icon to make it **orange** (proxied) ✅
- **TTL**: Select **Auto**
4. Click **"Save"**
5. Repeat for all records
**Pro tip:** Delete any old records Cloudflare imported from GoDaddy that you don't need.
### Step 5.4: Configure Records Automatically (Option B - Script)
If you're comfortable with command line:
```bash
# Set your Cloudflare credentials
export CF_API_TOKEN="your-cloudflare-api-token"
export CF_ZONE_ID="your-zone-id"
# Run the sync script
python infra/cloudflare/cloudflare_dns_sync.py
```
See `cloudflare_dns_sync.py` documentation for details.
---
## Part 6: Configure SSL/TLS
Cloudflare provides free SSL certificates, but we need to configure the encryption mode.
### Step 6.1: Set SSL/TLS Mode
1. In Cloudflare dashboard, go to **SSL/TLS** (left menu)
2. Under **Overview**, set encryption mode to:
- **"Full (strict)"** ✅
**Why?** This ensures end-to-end encryption:
- User → Cloudflare: Encrypted with Cloudflare cert
- Cloudflare → Railway: Encrypted with Railway cert
3. Cloudflare saves this automatically
### Step 6.2: Enable Always Use HTTPS
1. Go to **SSL/TLS** → **Edge Certificates**
2. Turn on **"Always Use HTTPS"** ✅
- This redirects all HTTP traffic to HTTPS automatically
3. Turn on **"Automatic HTTPS Rewrites"** ✅
- This fixes mixed content warnings
4. Turn on **"Certificate Transparency Monitoring"** ✅
- This monitors your SSL certificate health
### Step 6.3: Enable HSTS (Optional but Recommended)
1. Scroll down to **"HTTP Strict Transport Security (HSTS)"**
2. Click **"Enable HSTS"**
3. Read the warning, then configure:
- **Max Age**: 6 months (15768000 seconds)
- **Include subdomains**: ✅ Yes
- **Preload**: ❌ No (enable this later when stable)
4. Click **"Next"** → **"I understand"**
**Warning:** HSTS is a security feature that forces browsers to only use HTTPS. Don't enable "Preload" until you're 100% sure SSL is working perfectly.
---
## Part 7: Optimize Performance
Let's configure caching and performance features.
### Step 7.1: Enable Auto Minify
1. Go to **Speed** → **Optimization** (left menu)
2. Under **Auto Minify**, check:
- ✅ JavaScript
- ✅ CSS
- ✅ HTML
3. Cloudflare saves automatically
### Step 7.2: Enable Brotli Compression
1. In the same **Speed** → **Optimization** page
2. Turn on **"Brotli"** ✅
- This compresses your files even more than gzip
### Step 7.3: Set Caching Level
1. Go to **Caching** → **Configuration**
2. Set **Caching Level** to **"Standard"**
3. Set **Browser Cache TTL** to **"Respect Existing Headers"**
- This lets your backend control cache timing
---
## Part 8: Add Custom Domain to Railway
Now we need to tell Railway about your custom domain.
### Step 8.1: Add Custom Domain in Railway
1. Go to **Railway dashboard**
2. Select your **backend** service
3. Go to **Settings** → **Networking**
4. Under **Custom Domains**, click **"Add Domain"**
5. Enter: `os.blackroad.systems`
6. Click **"Add"**
**Railway will:**
- Automatically provision an SSL certificate (via Let's Encrypt)
- Show a green checkmark when ready (usually takes 1-2 minutes)
### Step 8.2: Repeat for Other Subdomains
Add these custom domains to Railway:
- `api.blackroad.systems`
- `prism.blackroad.systems`
**Note:** Each subdomain that points to Railway needs to be added in Railway's custom domains.
---
## Part 9: Test Everything
Let's verify everything is working!
### Step 9.1: Test HTTPS
Open these URLs in your browser (or on your iPhone):
1. https://blackroad.systems
2. https://www.blackroad.systems (should redirect to above)
3. https://os.blackroad.systems
4. https://api.blackroad.systems/health
5. https://docs.blackroad.systems
**Check for:**
- ✅ Green padlock in browser (🔒)
- ✅ Page loads correctly
- ✅ No certificate warnings
### Step 9.2: Test HTTP → HTTPS Redirect
Try loading without HTTPS:
1. http://blackroad.systems (should redirect to https://)
**Should automatically redirect** to HTTPS version.
### Step 9.3: Test DNS Propagation
Use these tools to verify DNS is working globally:
1. **DNS Checker**: https://dnschecker.org
- Enter: `blackroad.systems`
- Should show Cloudflare IPs
2. **Cloudflare DNS Lookup**: https://1.1.1.1/dns/
- Enter: `os.blackroad.systems`
- Should resolve correctly
### Step 9.4: Check Cloudflare Analytics
1. Go to Cloudflare → **Analytics & Logs** → **Traffic**
2. You should start seeing traffic data within a few minutes
3. This confirms traffic is flowing through Cloudflare
---
## Part 10: Repeat for Other Domains
Now that you've done `blackroad.systems`, repeat the same process for:
**Phase 1 Domains** (do these now):
- [ ] `blackroad.ai`
- [ ] `blackroad.network`
- [ ] `blackroad.me`
**Phase 2 Domains** (do these later):
- [ ] `lucidia.earth`
- [ ] `aliceqi.com`
- [ ] `blackroadqi.com`
- [ ] `roadwallet.com`
- [ ] `aliceos.io`
- [ ] `blackroadquantum.com`
**For each domain, follow the same steps:**
1. Part 2: Add domain to Cloudflare
2. Part 3: Update nameservers at GoDaddy
3. Part 4: Wait for "Active" status
4. Part 5: Configure DNS records
5. Part 6: Configure SSL/TLS
6. Part 7: Optimize performance
7. Part 8: Add custom domains to Railway (if needed)
8. Part 9: Test
**Pro tip:** You can start the process for multiple domains in parallel (add them all to Cloudflare and change nameservers), then configure them one at a time while DNS propagates.
---
## Troubleshooting
### DNS Not Resolving
**Problem:** Domain doesn't load after changing nameservers
**Solutions:**
1. **Wait longer** - DNS can take up to 48 hours (usually 5-60 minutes)
2. **Clear your browser cache** - Hard refresh (Cmd+Shift+R on Mac, Ctrl+Shift+R on PC)
3. **Check nameservers** - Go to https://www.whatsmydns.net and enter your domain
4. **Verify at GoDaddy** - Make sure nameservers are saved correctly
### SSL Certificate Error
**Problem:** Browser shows "Not Secure" or certificate warning
**Solutions:**
1. **Check SSL/TLS mode** - Should be "Full (strict)" in Cloudflare
2. **Wait for Railway SSL** - Check Railway dashboard for green checkmark
3. **Check custom domain** - Make sure domain is added in Railway settings
4. **Try incognito mode** - Sometimes browser cache causes issues
### Site Not Loading
**Problem:** Domain resolves but site doesn't load (blank page, 502 error)
**Solutions:**
1. **Check Railway app** - Make sure backend is deployed and healthy
2. **Check Railway logs** - Look for errors: Railway dashboard → Logs
3. **Test Railway URL directly** - Try `your-app.up.railway.app` to isolate issue
4. **Check DNS records** - Make sure CNAME points to correct Railway URL
### Mixed Content Warnings
**Problem:** Page loads but some assets show as insecure (broken padlock)
**Solutions:**
1. **Enable "Automatic HTTPS Rewrites"** - In Cloudflare SSL/TLS settings
2. **Check your code** - Make sure no hard-coded `http://` URLs
3. **Use relative URLs** - In your HTML/JS, use `/api/...` instead of full URLs
### Email Stopped Working
**Problem:** Can't send/receive emails after DNS migration
**Solutions:**
1. **Check MX records** - Make sure they're configured in Cloudflare DNS
2. **MX records must be DNS-only** - Turn OFF proxy (grey cloud) for MX records
3. **Verify SPF/DKIM** - Make sure TXT records for email are present
---
## Getting Help
If you run into issues:
1. **Cloudflare Community**: https://community.cloudflare.com
2. **Railway Discord**: https://discord.gg/railway
3. **Check Cloudflare Status**: https://www.cloudflarestatus.com
4. **Check Railway Status**: https://status.railway.app
**For BlackRoad-specific issues:**
- Open an issue in the repo
- Check `CLAUDE.md` for developer docs
- Review `MASTER_ORCHESTRATION_PLAN.md`
---
## Next Steps
After DNS migration is complete:
- [ ] Set up **Page Rules** for WWW redirects (see Part 11 below)
- [ ] Enable **Web Analytics** in Cloudflare
- [ ] Set up **Firewall Rules** (optional)
- [ ] Configure **Workers** for edge functions (Phase 2)
- [ ] Set up **Cloudflare Access** for zero-trust security (Phase 2)
---
## Part 11: Set Up Page Rules (Optional but Recommended)
Page Rules let you configure special behaviors for specific URLs.
### Step 11.1: Create WWW Redirect Rule
1. Go to Cloudflare → **Rules** → **Page Rules**
2. Click **"Create Page Rule"**
3. Enter URL: `www.blackroad.systems/*`
4. Click **"Add a Setting"** → **"Forwarding URL"**
5. Select **"301 - Permanent Redirect"**
6. Enter destination: `https://blackroad.systems/$1`
7. Click **"Save and Deploy"**
**What this does:** Redirects www.blackroad.systems to blackroad.systems (keeps the path)
### Step 11.2: Bypass API Caching
Create a rule to prevent API responses from being cached:
1. Click **"Create Page Rule"**
2. Enter URL: `*blackroad.systems/api/*`
3. Add settings:
- **Cache Level** → Bypass
- **Disable Performance** (optional)
4. Click **"Save and Deploy"**
**What this does:** Ensures API calls always hit your backend (no stale cached data)
---
## Checklist: Migration Complete
Mark these when done:
- [ ] Domain added to Cloudflare
- [ ] Nameservers updated at GoDaddy
- [ ] DNS status shows "Active" in Cloudflare
- [ ] DNS records configured (from records.yaml)
- [ ] SSL/TLS set to "Full (strict)"
- [ ] "Always Use HTTPS" enabled
- [ ] Auto Minify enabled
- [ ] Brotli compression enabled
- [ ] Custom domains added to Railway
- [ ] HTTPS works (green padlock 🔒)
- [ ] HTTP → HTTPS redirect works
- [ ] WWW → apex redirect works
- [ ] API endpoint responding
- [ ] Docs subdomain works
- [ ] No console errors in browser
- [ ] Cloudflare Analytics showing traffic
---
## Success! 🎉
You've successfully migrated DNS to Cloudflare! Your domains now benefit from:
- ✅ Free SSL certificates
- ✅ CDN (faster loading worldwide)
- ✅ DDoS protection
- ✅ Better security
- ✅ Advanced features ready for Phase 2
**What's next?**
- Move on to Phase 1 infrastructure tasks
- Set up GitHub Actions secrets
- Configure Railway environment variables
- Deploy your first updates through the new infrastructure
**Where AI meets the open road.** 🛣️

View File

@@ -0,0 +1,426 @@
# Cloudflare DNS Records Configuration
# Version: 1.0
# Purpose: Structured DNS configuration for all BlackRoad domains
#
# This file is the single source of truth for DNS records.
# Use cloudflare_dns_sync.py to apply these records to Cloudflare.
#
# Format:
# domain: Domain name being managed
# zone_id: Cloudflare zone ID (get from Cloudflare dashboard)
# records: List of DNS records for this domain
# - type: Record type (A, AAAA, CNAME, TXT, MX, etc.)
# name: Record name (@ for root, subdomain for others)
# content: Target value
# ttl: Time to live (1 = Auto)
# proxied: Whether to proxy through Cloudflare (true/false)
# priority: MX priority (for MX records only)
# comment: Human-readable description
---
# ============================================================================
# PHASE 1 DOMAINS (Active Now)
# ============================================================================
- domain: blackroad.systems
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: true
phase: 1
records:
# Root domain → Railway backend
- type: CNAME
name: '@'
content: blackroad-os-production.up.railway.app
ttl: 1
proxied: true
comment: Root domain points to Railway production (CNAME flattening)
# WWW redirect
- type: CNAME
name: www
content: blackroad.systems
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
# OS subdomain (explicit alias)
- type: CNAME
name: os
content: blackroad.systems
ttl: 1
proxied: true
comment: Explicit OS subdomain (alternative entry point)
# API subdomain
- type: CNAME
name: api
content: blackroad-os-production.up.railway.app
ttl: 1
proxied: true
comment: Explicit API subdomain for developers
# Prism Console subdomain
- type: CNAME
name: prism
content: blackroad-os-production.up.railway.app
ttl: 1
proxied: true
comment: Prism Console admin interface
# Documentation subdomain → GitHub Pages
- type: CNAME
name: docs
content: blackboxprogramming.github.io
ttl: 1
proxied: true
comment: Documentation hosted on GitHub Pages
# CDN subdomain (for future asset delivery)
- type: CNAME
name: cdn
content: blackroad.systems
ttl: 1
proxied: true
comment: CDN alias for static assets
# Email records (Google Workspace configuration)
- type: TXT
name: '@'
content: v=spf1 include:_spf.google.com ~all
ttl: 1
proxied: false
comment: SPF record for Google Workspace email
- type: MX
name: '@'
content: aspmx.l.google.com
ttl: 1
proxied: false
priority: 1
comment: Google Workspace MX record (priority 1)
- type: MX
name: '@'
content: alt1.aspmx.l.google.com
ttl: 1
proxied: false
priority: 5
comment: Google Workspace MX record (priority 5)
- type: MX
name: '@'
content: alt2.aspmx.l.google.com
ttl: 1
proxied: false
priority: 5
comment: Google Workspace MX record (priority 5)
# Verification records (add as needed)
# - type: TXT
# name: '@'
# content: google-site-verification=YOUR_CODE_HERE
# ttl: 1
# proxied: false
# comment: Google Search Console verification
- domain: blackroad.ai
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 1
records:
# Root → Alias to main OS
- type: CNAME
name: '@'
content: os.blackroad.systems
ttl: 1
proxied: true
comment: Product console entry point
# WWW redirect
- type: CNAME
name: www
content: blackroad.ai
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
# Console subdomain (explicit)
- type: CNAME
name: console
content: os.blackroad.systems
ttl: 1
proxied: true
comment: Explicit console subdomain
- domain: blackroad.network
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 1
records:
# Root → GitHub Pages (developer docs)
- type: CNAME
name: '@'
content: blackboxprogramming.github.io
ttl: 1
proxied: true
comment: Developer hub and documentation
# WWW redirect
- type: CNAME
name: www
content: blackroad.network
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
# API subdomain (for developer API access)
- type: CNAME
name: api
content: blackroad-os-production.up.railway.app
ttl: 1
proxied: true
comment: API access for developers
# Sandbox subdomain → Railway staging
- type: CNAME
name: sandbox
content: blackroad-os-staging.up.railway.app
ttl: 1
proxied: true
comment: Staging/sandbox environment for testing
- domain: blackroad.me
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 1
records:
# Root → Identity portal (via main OS)
- type: CNAME
name: '@'
content: os.blackroad.systems
ttl: 1
proxied: true
comment: Personal identity portal
# WWW redirect
- type: CNAME
name: www
content: blackroad.me
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
# ID subdomain (explicit identity)
- type: CNAME
name: id
content: os.blackroad.systems
ttl: 1
proxied: true
comment: Explicit identity subdomain
# ============================================================================
# PHASE 2 DOMAINS (Launch Later)
# ============================================================================
- domain: lucidia.earth
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 2
records:
# Root → GitHub Pages (narrative site)
- type: CNAME
name: '@'
content: blackboxprogramming.github.io
ttl: 1
proxied: true
comment: Narrative experiences and interactive storytelling
# WWW redirect
- type: CNAME
name: www
content: lucidia.earth
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
# Studio subdomain (Phase 3 - creative tools)
- type: CNAME
name: studio
content: lucidia-studio.vercel.app
ttl: 1
proxied: true
comment: Lucidia Studio creative production platform
- domain: aliceqi.com
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 2
records:
# Root → GitHub Pages (research showcase)
- type: CNAME
name: '@'
content: blackboxprogramming.github.io
ttl: 1
proxied: true
comment: ALICE QI research showcase
# WWW redirect
- type: CNAME
name: www
content: aliceqi.com
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
# Research subdomain
- type: CNAME
name: research
content: aliceqi.com
ttl: 1
proxied: true
comment: Research portal
# Docs subdomain
- type: CNAME
name: docs
content: aliceqi.com
ttl: 1
proxied: true
comment: Technical documentation
- domain: blackroadqi.com
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 2
records:
# Root → Dedicated QI app on Railway
- type: CNAME
name: '@'
content: blackroadqi-app.up.railway.app
ttl: 1
proxied: true
comment: Financial/quantitative intelligence product
# WWW redirect
- type: CNAME
name: www
content: blackroadqi.com
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
# API subdomain
- type: CNAME
name: api
content: blackroadqi-api.up.railway.app
ttl: 1
proxied: true
comment: QI API endpoint
- domain: roadwallet.com
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 2
records:
# Root → Alias to main OS wallet interface
- type: CNAME
name: '@'
content: os.blackroad.systems
ttl: 1
proxied: true
comment: Wallet interface via main OS
# WWW redirect
- type: CNAME
name: www
content: roadwallet.com
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
- domain: aliceos.io
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 2
records:
# Root → Legacy alias to main OS
- type: CNAME
name: '@'
content: os.blackroad.systems
ttl: 1
proxied: true
comment: Legacy domain alias
# WWW redirect
- type: CNAME
name: www
content: aliceos.io
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
- domain: blackroadquantum.com
zone_id: REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE
primary: false
phase: 2
records:
# Root → GitHub Pages (research hub)
- type: CNAME
name: '@'
content: blackboxprogramming.github.io
ttl: 1
proxied: true
comment: Quantum research hub
# WWW redirect
- type: CNAME
name: www
content: blackroadquantum.com
ttl: 1
proxied: true
comment: WWW subdomain redirects to apex
# Lab subdomain → Dedicated Quantum Lab app
- type: CNAME
name: lab
content: quantum-lab.up.railway.app
ttl: 1
proxied: true
comment: Quantum Lab application
# ============================================================================
# NOTES
# ============================================================================
#
# CNAME Flattening:
# Cloudflare allows CNAME records at the root (@) level via CNAME flattening.
# This is not standard DNS but works with Cloudflare proxy enabled.
#
# Railway Targets:
# Replace 'blackroad-os-production.up.railway.app' with your actual Railway
# app URL from Railway dashboard → Service → Settings → Domains.
#
# Zone IDs:
# Get zone IDs from Cloudflare dashboard → Domain → Overview (right sidebar).
# Replace all 'REPLACE_WITH_ZONE_ID_FROM_CLOUDFLARE' with actual zone IDs.
#
# GitHub Pages CNAME:
# For GitHub Pages domains, you must also create a CNAME file in your
# GitHub Pages repo root containing the custom domain:
# echo "blackroad.network" > CNAME
#
# Email Configuration:
# The MX/SPF records are examples for Google Workspace. Update or remove
# if using a different email provider.
#
# SSL/TLS:
# Set Cloudflare SSL/TLS mode to "Full (strict)" for all domains.
# Railway automatically provides SSL certificates.
#
# Proxied vs DNS-only:
# - proxied: true → Traffic goes through Cloudflare (CDN, DDoS protection)
# - proxied: false → Direct DNS resolution (required for MX, some TXT records)
#
# Automation:
# Use cloudflare_dns_sync.py to apply these records automatically via API.

View File

@@ -0,0 +1,699 @@
# BlackRoad Landing Page Implementation Plan
**Version:** 1.0
**Date:** 2025-11-18
**Purpose:** Detailed plan for implementing blackroad.systems landing page
**Target:** Phase 1 - Production MVP
---
## Overview
Create a professional, conversion-focused landing page for `blackroad.systems` that:
- Positions BlackRoad OS as the future of AI orchestration
- Drives design partner signups
- Provides clear value proposition
- Showcases key capabilities
- Links to documentation and product
---
## Site Structure
### Pages (Phase 1 MVP)
1. **Homepage** (`/`) - Hero, value prop, CTA
2. **Architecture** (`/architecture`) - Technical overview
3. **Solutions** (`/solutions/financial-services`) - First industry vertical
4. **Pricing** (`/pricing`) - 3-tier model
5. **Contact** (`/contact`) - Demo request form
**Total:** 5 pages minimum for Phase 1
---
## Page 1: Homepage
### Hero Section
**Visual:** Full-width hero with animated gradient background (Windows 95 aesthetic meets modern design)
**Headline:**
```
Where AI Meets the Open Road
```
**Subheadline:**
```
The operating system for autonomous AI orchestration.
200+ agents. Zero black boxes. Complete auditability.
```
**CTA Buttons:**
- Primary: "Request Design Partner Access" (links to /contact)
- Secondary: "View Architecture" (links to /architecture)
- Tertiary: "Explore Docs" (links to blackroad.network)
**Background Elements:**
- Subtle animated grid (like Windows 95 desktop pattern)
- Floating "windows" with code snippets
- Smooth gradient (teal → purple)
### Capabilities Section
**Headline:** "Built for the Future of AI Work"
**Three Columns:**
1. **Multi-Agent Orchestration**
- Icon: 🤖 (or custom agent icon)
- Description: "200+ autonomous agents working together. From code reviews to compliance audits, orchestrated intelligently."
- Link: "Learn more →"
2. **Provable & Auditable**
- Icon: 🔒 (or blockchain icon)
- Description: "Every action logged on RoadChain. Full tamper-evident audit trails for compliance and governance."
- Link: "See architecture →"
3. **Human-in-the-Loop**
- Icon: 👤 (or human + AI icon)
- Description: "Humans orchestrate, agents execute. Approval gates, review steps, and full transparency."
- Link: "View workflow →"
### Use Cases Section
**Headline:** "Powering AI-First Organizations"
**Three Cards (with hover effects):**
1. **Financial Services**
- "Deploy 500 trading agents with complete regulatory compliance"
- CTA: "Read case study →"
2. **Healthcare**
- "Ensure HIPAA compliance across all AI operations"
- CTA: "Learn how →"
3. **Enterprise**
- "Replace black-box AI with deterministic, auditable intelligence"
- CTA: "See enterprise features →"
### Social Proof (Placeholder for Now)
**Headline:** "Trusted by Forward-Thinking Organizations"
**Logos:** (Placeholder boxes with "Design Partner" text)
- 5 placeholder boxes
- Text: "Join our design partner program →"
### Tech Stack Section
**Headline:** "Built on Modern Infrastructure"
**Badges/Icons:**
- Python / FastAPI
- PostgreSQL
- Redis
- Railway
- Cloudflare
- Blockchain (RoadChain)
- ALICE QI (AI engine)
### Final CTA
**Large, centered section with gradient background**
**Headline:** "Ready to Orchestrate AI Without Limits?"
**Buttons:**
- Primary: "Request Design Partner Access"
- Secondary: "Schedule a Demo"
**Footer Note:**
"Available for select design partners in 2025. Early access includes dedicated support and custom integrations."
---
## Page 2: Architecture
### Header
**Headline:** "The BlackRoad OS Architecture"
**Subheadline:** "Seven layers of deterministic, auditable AI orchestration"
### The Stack Diagram
Visual representation of the 7 layers (vertical stack):
```
┌─────────────────────────────────────────┐
│ 7. USER EXPERIENCE │
│ blackroad.systems • blackroad.ai │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 6. APPLICATION LAYER (Pocket OS) │
│ Native Apps • Win95 UI • WebSocket │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 5. API GATEWAY & ROUTING │
│ FastAPI • REST • GraphQL │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 4. ORCHESTRATION & INTELLIGENCE │
│ Lucidia • Prism • Operator Engine │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 3. DATA & STATE │
│ Postgres • Redis • RoadChain • Vault │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 2. COMPUTE & INFRASTRUCTURE │
│ Railway • CloudWay • Edge Functions │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 1. DNS & CDN │
│ Cloudflare • Global Distribution │
└─────────────────────────────────────────┘
```
### Core Components
**Expandable accordions for each layer:**
1. **Lucidia Layer**
- Multi-model AI orchestration
- Claude, GPT, Llama integration
- Long-term memory
- Tool calling
2. **Prism Layer**
- Job queue & event log
- Metrics & monitoring
- Backpressure control
- Scheduler
3. **RoadChain**
- Tamper-evident audit trail
- Cryptographic provenance
- Compliance-ready
- Immutable logs
4. **PS-SHA∞ Identity**
- Sovereign identity for agents
- Recursive self-description
- Unique agent signatures
### Technical Specs
**Table format:**
| Component | Technology | Purpose |
|-----------|-----------|---------|
| Backend | FastAPI (Python) | Async API server |
| Database | PostgreSQL 15 | Relational data |
| Cache | Redis 7 | Sessions, pub/sub |
| Frontend | Vanilla JS | Zero-dependency UI |
| Deployment | Railway | Cloud platform |
| CDN | Cloudflare | Global edge network |
| Blockchain | RoadChain | Audit ledger |
### CTA
"Dive deeper into our technical documentation →"
Link to: blackroad.network
---
## Page 3: Solutions - Financial Services
### Hero
**Headline:** "AI Orchestration for Financial Services"
**Subheadline:** "Deploy trading agents, compliance bots, and risk analysis—all with complete auditability."
### The Challenge
**Section:** "The Problem with Black-Box AI"
**Pain points:**
- ❌ No audit trail for compliance
- ❌ Can't explain model decisions to regulators
- ❌ Single-model risk (vendor lock-in)
- ❌ No human oversight in critical decisions
### The BlackRoad Solution
**Section:** "How BlackRoad Solves It"
**Benefits:**
- ✅ Complete audit trail via RoadChain
- ✅ Multi-model orchestration (no vendor lock-in)
- ✅ Human-in-the-loop approval gates
- ✅ Deterministic, reproducible results
### Use Cases
**Three cards:**
1. **Trading Agents**
- Deploy 500 autonomous trading agents
- Full regulatory compliance (MiFID II, Dodd-Frank)
- Real-time risk monitoring
- Instant rollback on market anomalies
2. **Compliance Automation**
- Automated KYC/AML checks
- Transaction monitoring
- Suspicious activity reports
- Regulatory filing automation
3. **Risk Analysis**
- Portfolio risk assessment
- Stress testing
- Scenario analysis
- Real-time dashboards
### Case Study (Placeholder)
**Headline:** "How [Bank Name] Deployed 500 AI Agents with Zero Compliance Issues"
**Stats:**
- 90% reduction in manual compliance work
- 100% audit trail coverage
- 24/7 automated monitoring
- Zero regulatory findings
**Quote:**
"BlackRoad gave us the confidence to deploy AI at scale without sacrificing regulatory compliance."
— [CTO, Major Bank]
### CTA
"Ready to transform your financial operations?"
Button: "Request a Custom Demo"
---
## Page 4: Pricing
### Hero
**Headline:** "Simple, Transparent Pricing"
**Subheadline:** "Choose the plan that fits your organization"
### Pricing Tiers (3 columns)
#### 1. **Developer** (Free)
**Price:** $0/month
**For:** Individual developers, open source projects
**Features:**
- ✅ Up to 10 agents
- ✅ 1 GB storage
- ✅ Community support
- ✅ Public documentation access
- ✅ Basic API access
- ❌ No SLA
- ❌ No custom integrations
**CTA:** "Start Building" (link to docs)
#### 2. **Team** ($499/month)
**Price:** $499/month
**For:** Small teams, startups
**Features:**
- ✅ Up to 100 agents
- ✅ 50 GB storage
- ✅ Email support (48h response)
- ✅ Advanced monitoring
- ✅ Multi-user access
- ✅ Private repositories
- ✅ 99.9% SLA
**CTA:** "Start Free Trial" (14-day trial)
#### 3. **Enterprise** (Custom)
**Price:** Custom pricing
**For:** Large organizations, design partners
**Features:**
- ✅ Unlimited agents
- ✅ Unlimited storage
- ✅ Dedicated support (24/7)
- ✅ Custom integrations
- ✅ On-premise deployment option
- ✅ Custom SLA (up to 99.99%)
- ✅ White-label options
- ✅ Dedicated account manager
- ✅ Custom training
**CTA:** "Contact Sales"
### Add-Ons
**Optional services:**
- Professional services: $200/hour
- Custom agent development: Custom quote
- Training workshops: $5,000/day
- Managed services: Starting at $2,000/month
### FAQ Section
**Common questions:**
**Q: What payment methods do you accept?**
A: Credit card, ACH, wire transfer, and purchase orders for Enterprise customers.
**Q: Can I upgrade/downgrade anytime?**
A: Yes! Plans are flexible and can be changed at any time.
**Q: What's included in support?**
A: Team tier gets email support (48h). Enterprise gets dedicated Slack channel + 24/7 on-call.
**Q: Is there a discount for annual billing?**
A: Yes! Get 2 months free when you pay annually.
**Q: Do you offer academic/nonprofit pricing?**
A: Yes! Contact us for special pricing.
### CTA
"Not sure which plan is right? Let's talk."
Button: "Schedule a Consultation"
---
## Page 5: Contact / Demo Request
### Hero
**Headline:** "Let's Build the Future Together"
**Subheadline:** "Join our design partner program and shape the future of AI orchestration"
### Form (Left Side)
**Fields:**
1. **Your Name** (required)
2. **Email Address** (required)
3. **Company Name** (required)
4. **Job Title** (optional)
5. **Company Size** (dropdown)
- 1-10 employees
- 11-50 employees
- 51-200 employees
- 201-1,000 employees
- 1,000+ employees
6. **Industry** (dropdown)
- Financial Services
- Healthcare
- Technology
- Manufacturing
- Other
7. **What are you interested in?** (checkboxes)
- [ ] Design partner program
- [ ] Enterprise pilot
- [ ] Technical demo
- [ ] Custom integration
- [ ] Just exploring
8. **Tell us about your use case** (textarea, optional)
9. **Preferred contact method** (radio)
- ( ) Email
- ( ) Phone call
- ( ) Video meeting
**Submit Button:** "Request Access"
**Privacy note:**
"We respect your privacy. Your information will only be used to respond to your inquiry. See our Privacy Policy."
### Info (Right Side)
**Contact Information:**
**Email:**
alexa@blackroad.systems
**Office Hours:**
Monday - Friday, 9am - 5pm PST
**Response Time:**
We typically respond within 24 hours
**Design Partner Program:**
Looking for 5-10 design partners to pilot BlackRoad OS in production environments. Design partners receive:
- Early access to new features
- Dedicated engineering support
- Custom integrations
- Discounted pricing
- Co-marketing opportunities
### Map (Optional)
Placeholder for office location (if applicable)
### CTA
After form submission:
"Thank you! We'll be in touch within 24 hours."
---
## Design System
### Colors
**Primary Palette:**
```css
--blackroad-teal: #00D9FF;
--blackroad-purple: #9D4EDD;
--blackroad-dark: #1a1a2e;
--blackroad-light: #f8f9fa;
```
**Accent Colors:**
```css
--accent-green: #06FFA5;
--accent-orange: #FF6B35;
--accent-blue: #4EA8DE;
```
**Gradients:**
```css
--gradient-hero: linear-gradient(135deg, #00D9FF 0%, #9D4EDD 100%);
--gradient-cta: linear-gradient(90deg, #06FFA5 0%, #00D9FF 100%);
```
### Typography
**Fonts:**
- **Headings:** Inter (Bold, 700)
- **Body:** Inter (Regular, 400)
- **Code:** JetBrains Mono
**Sizes:**
```css
--h1: 4rem (64px)
--h2: 3rem (48px)
--h3: 2rem (32px)
--h4: 1.5rem (24px)
--body: 1rem (16px)
--small: 0.875rem (14px)
```
### Components
**Buttons:**
- Primary: Gradient background, white text, rounded corners
- Secondary: Outline only, hover fills
- Ghost: Text only, underline on hover
**Cards:**
- White background
- Subtle shadow
- Rounded corners (8px)
- Hover: Lift effect (translateY)
**Forms:**
- Clean, minimal design
- Labels above inputs
- Focus states with accent color
- Validation messages
### Responsive Breakpoints
```css
--mobile: 320px - 768px
--tablet: 769px - 1024px
--desktop: 1025px+
```
---
## Technical Implementation
### Tech Stack
**Frontend:**
- HTML5
- CSS3 (with CSS Grid and Flexbox)
- Vanilla JavaScript (no framework needed for MVP)
- Optional: Alpine.js for interactivity
**Hosting:**
- **Option A:** Railway (same as backend)
- **Option B:** GitHub Pages (static)
- **Option C:** Cloudflare Pages
**Recommended:** Railway for unified deployment
### Build Process
**Simple approach:**
- No build step needed for Phase 1
- Direct HTML/CSS/JS
- Minify before deploy (optional)
**Future (Phase 2):**
- Add Vite for bundling
- Add Tailwind CSS for utility-first styling
- Add TypeScript for type safety
### Deployment
**Via Railway:**
1. Create static site service in Railway
2. Point to `/landing` directory
3. Configure custom domain: `blackroad.systems`
4. Deploy on push to main
**Via GitHub Pages:**
1. Create `gh-pages` branch
2. Copy landing page files
3. Configure custom domain CNAME
4. Deploy via GitHub Actions
### Performance
**Optimization checklist:**
- [ ] Compress images (WebP format)
- [ ] Minify CSS/JS
- [ ] Enable Cloudflare caching
- [ ] Lazy load images
- [ ] Use CDN for assets
- [ ] Implement service worker (optional)
**Target metrics:**
- Lighthouse score: 90+
- First Contentful Paint: < 1.5s
- Time to Interactive: < 3s
---
## Content Guidelines
### Voice & Tone
**Voice:**
- Confident but not arrogant
- Technical but accessible
- Future-focused but practical
- Authoritative but friendly
**Tone Examples:**
**Good:**
- "Where AI meets the open road"
- "200+ agents. Zero black boxes."
- "Humans orchestrate. Agents execute."
**Avoid:**
- "Revolutionary AI platform" (too generic)
- "Cutting-edge machine learning" (too buzzwordy)
- "Industry-leading solution" (meaningless)
### Copywriting Principles
1. **Lead with benefits, not features**
- Not: "We have 200 agents"
- Instead: "Deploy 200 agents that work together autonomously"
2. **Use concrete numbers**
- Not: "Fast deployment"
- Instead: "Deploy in under 5 minutes"
3. **Show, don't tell**
- Not: "Easy to use"
- Instead: "One command to deploy: `railway up`"
4. **Address objections directly**
- Pain point → Solution → Proof
---
## Timeline
### Week 1: Design
- [ ] Create wireframes (Figma)
- [ ] Design hero section
- [ ] Design component library
- [ ] Get feedback from team
### Week 2: Development
- [ ] Build HTML structure
- [ ] Implement CSS styles
- [ ] Add JavaScript interactions
- [ ] Test responsiveness
### Week 3: Content
- [ ] Write all page copy
- [ ] Create placeholder images
- [ ] Set up contact form backend
- [ ] Add analytics (Google Analytics/Plausible)
### Week 4: Launch
- [ ] Deploy to Railway/GitHub Pages
- [ ] Configure custom domain
- [ ] Test across browsers
- [ ] Launch announcement
---
## Success Metrics
### Phase 1 Goals (First 3 Months)
**Traffic:**
- 1,000 unique visitors/month
- 2,000 page views/month
- 3-minute average session duration
**Conversions:**
- 50 demo requests
- 10 design partner applications
- 5 qualified design partners
**Engagement:**
- 20% docs link click-through
- 10% contact form submission rate
- 30% return visitor rate
### Tracking
**Tools:**
- Google Analytics (or Plausible for privacy-friendly alternative)
- Hotjar for heatmaps
- Form analytics via backend
**Key Events:**
- Demo request submitted
- Pricing page viewed
- Architecture page viewed
- Docs link clicked
- Contact form submitted
---
## Next Steps
### Immediate Actions
1. **Create landing page repository** (or use existing)
2. **Set up basic HTML structure** (5 pages)
3. **Design hero section** (highest impact)
4. **Write homepage copy** (can iterate later)
5. **Deploy to staging** (test before production)
### Follow-Up
- Create email drip campaign for demo requests
- Set up automated demo scheduling (Calendly)
- Create sales playbook for design partner outreach
- Build case study template for early customers
---
**This plan provides everything needed to build a professional, conversion-focused landing page for blackroad.systems. Execute in phases, measure results, and iterate based on real user feedback.**
**Where AI meets the open road.** 🛣️

View File

@@ -0,0 +1,313 @@
# Cloudflare DNS Management Scripts
This directory contains automation scripts for managing BlackRoad domains via the Cloudflare API.
## Scripts
### `sync_dns.py`
Synchronizes DNS records from `ops/domains.yaml` to Cloudflare. Handles creating new records and updating existing ones.
**Features:**
- Automated DNS record synchronization
- Dry-run mode to preview changes
- Colored output for easy scanning
- Support for multiple record types (A, CNAME, MX, TXT)
- Automatic proxying configuration
**Usage:**
```bash
# Set environment variables
export CF_API_TOKEN="your-cloudflare-api-token"
export CF_ZONE_ID="your-zone-id"
# Preview changes (dry run)
python scripts/cloudflare/sync_dns.py --dry-run
# Apply changes
python scripts/cloudflare/sync_dns.py
# Or with command-line arguments
python scripts/cloudflare/sync_dns.py \
--token "your-token" \
--zone-id "your-zone-id" \
--zone-name "blackroad.systems"
```
### `validate_dns.py`
Validates DNS configuration and checks propagation status across the internet.
**Features:**
- DNS resolution verification
- SSL certificate validation
- HTTP/HTTPS accessibility testing
- Redirect verification (www → apex, HTTP → HTTPS)
- Support for checking multiple domains
**Usage:**
```bash
# Check single domain (default: blackroad.systems)
python scripts/cloudflare/validate_dns.py
# Check specific domain
python scripts/cloudflare/validate_dns.py --domain blackroad.ai
# Check all BlackRoad domains
python scripts/cloudflare/validate_dns.py --all
# DNS-only check (skip SSL and HTTP)
python scripts/cloudflare/validate_dns.py --dns-only
```
## Installation
### Prerequisites
- Python 3.8 or higher
- Cloudflare account with API token
- Domain(s) added to Cloudflare
### Install Dependencies
```bash
# Install required packages
pip install -r scripts/cloudflare/requirements.txt
# Or install individually
pip install requests pyyaml dnspython colorama
```
## Configuration
### Getting Cloudflare API Token
1. Log in to [Cloudflare Dashboard](https://dash.cloudflare.com)
2. Go to **My Profile****API Tokens**
3. Click **Create Token**
4. Use the **Edit zone DNS** template
5. Select the zones you want to manage
6. Create token and copy it
### Getting Zone ID
1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com)
2. Select your domain (e.g., `blackroad.systems`)
3. Scroll down to **API** section on the right sidebar
4. Copy the **Zone ID**
### Environment Variables
```bash
# Add to your shell profile (~/.bashrc, ~/.zshrc, etc.)
export CF_API_TOKEN="your-cloudflare-api-token-here"
export CF_ZONE_ID="your-zone-id-here"
# Or create a .env file (DO NOT COMMIT THIS)
echo "CF_API_TOKEN=your-token" >> .env
echo "CF_ZONE_ID=your-zone-id" >> .env
source .env
```
## Domain Configuration
DNS records are defined in `ops/domains.yaml`. Example:
```yaml
domains:
- name: "blackroad.systems"
type: "root"
provider: "cloudflare"
mode: "dns"
record:
type: "CNAME"
value: "blackroad-os-production.up.railway.app"
ttl: 1 # Auto
proxied: true
- name: "api.blackroad.systems"
type: "subdomain"
provider: "cloudflare"
mode: "dns"
record:
type: "CNAME"
value: "blackroad-os-production.up.railway.app"
proxied: true
```
## Workflow
### Initial Migration
1. **Add domain to Cloudflare** (manual step via dashboard)
```
- Go to Cloudflare → Add a site
- Enter domain name
- Choose Free plan
- Follow setup wizard
```
2. **Update nameservers at registrar** (GoDaddy, etc.)
```
- Copy nameservers from Cloudflare
- Update at domain registrar
- Wait 5-60 minutes for propagation
```
3. **Configure DNS records**
```bash
# Update ops/domains.yaml with your records
# Preview changes
python scripts/cloudflare/sync_dns.py --dry-run
# Apply changes
python scripts/cloudflare/sync_dns.py
```
4. **Verify configuration**
```bash
# Check DNS propagation
python scripts/cloudflare/validate_dns.py
# Or check specific domain
python scripts/cloudflare/validate_dns.py --domain blackroad.systems
```
### Regular Updates
When updating DNS records:
1. Edit `ops/domains.yaml`
2. Run dry-run to preview: `python scripts/cloudflare/sync_dns.py --dry-run`
3. Apply changes: `python scripts/cloudflare/sync_dns.py`
4. Validate: `python scripts/cloudflare/validate_dns.py`
## Troubleshooting
### DNS Not Resolving
**Problem:** Domain doesn't resolve
**Solutions:**
```bash
# Check DNS with dig
dig blackroad.systems
# Check with validation script
python scripts/cloudflare/validate_dns.py --domain blackroad.systems
# Wait for propagation (5-60 minutes after nameserver change)
```
### API Authentication Errors
**Problem:** `401 Unauthorized` or `403 Forbidden`
**Solutions:**
- Verify API token is correct
- Check token has "Edit DNS" permission for the zone
- Ensure token hasn't expired
- Verify zone ID is correct
### Script Errors
**Problem:** Import errors or missing dependencies
**Solutions:**
```bash
# Install all dependencies
pip install -r scripts/cloudflare/requirements.txt
# Or install missing package
pip install <package-name>
```
### Configuration Drift
**Problem:** Cloudflare records don't match `domains.yaml`
**Solutions:**
```bash
# Run sync to update Cloudflare to match config
python scripts/cloudflare/sync_dns.py
# Or manually update records in Cloudflare dashboard
```
## Security Best Practices
1. **Never commit API tokens**
- Add `.env` to `.gitignore`
- Use environment variables
- Rotate tokens periodically
2. **Use scoped tokens**
- Create tokens with minimum required permissions
- Use zone-specific tokens when possible
- Avoid using Global API Key
3. **Audit regularly**
- Review DNS records monthly
- Check token usage in Cloudflare dashboard
- Remove unused tokens
## Integration with CI/CD
### GitHub Actions Example
```yaml
name: Sync DNS Records
on:
push:
paths:
- 'ops/domains.yaml'
branches:
- main
jobs:
sync-dns:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install -r scripts/cloudflare/requirements.txt
- name: Sync DNS records
env:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
run: |
python scripts/cloudflare/sync_dns.py
```
Add secrets to GitHub:
```bash
gh secret set CF_API_TOKEN
gh secret set CF_ZONE_ID
```
## Additional Resources
- [Cloudflare API Documentation](https://developers.cloudflare.com/api/)
- [Cloudflare DNS Documentation](https://developers.cloudflare.com/dns/)
- [DNS Blueprint](../../infra/cloudflare/CLOUDFLARE_DNS_BLUEPRINT.md)
- [Domain Configuration](../../ops/domains.yaml)
## Support
For issues or questions:
- Check the [CLOUDFLARE_DNS_BLUEPRINT.md](../../infra/cloudflare/CLOUDFLARE_DNS_BLUEPRINT.md)
- Review Cloudflare dashboard for zone status
- Check script output for error messages
- Verify API token permissions
---
**Last Updated:** 2025-11-18
**Maintained by:** BlackRoad DevOps Team

View File

@@ -0,0 +1,14 @@
# Cloudflare DNS Management Scripts Requirements
# Install with: pip install -r scripts/cloudflare/requirements.txt
# HTTP client for API requests
requests>=2.31.0
# YAML parsing for domains.yaml
PyYAML>=6.0.1
# DNS resolution verification
dnspython>=2.4.2
# Colored terminal output
colorama>=0.4.6

359
scripts/cloudflare/sync_dns.py Executable file
View File

@@ -0,0 +1,359 @@
#!/usr/bin/env python3
"""
Sync DNS records from ops/domains.yaml to Cloudflare
This script automates the migration and synchronization of DNS records
from the domain configuration file to Cloudflare. It handles:
- Creating new DNS records
- Updating existing DNS records
- Detecting and reporting configuration drift
Usage:
export CF_API_TOKEN="your-cloudflare-api-token"
export CF_ZONE_ID="your-zone-id" # For blackroad.systems
python scripts/cloudflare/sync_dns.py
Or with command-line arguments:
python scripts/cloudflare/sync_dns.py --zone-id <zone_id> --token <token>
Requirements:
pip install requests pyyaml colorama
"""
import os
import sys
import argparse
import yaml
import requests
from typing import Dict, List, Optional
from datetime import datetime
try:
from colorama import init, Fore, Style
init()
HAS_COLOR = True
except ImportError:
HAS_COLOR = False
# Fallback to no colors
class Fore:
GREEN = RED = YELLOW = CYAN = RESET = ""
class Style:
BRIGHT = RESET_ALL = ""
# Configuration
CF_API_BASE = "https://api.cloudflare.com/client/v4"
DOMAINS_FILE = "ops/domains.yaml"
def print_status(message: str, status: str = "info"):
"""Print colored status messages"""
if status == "success":
prefix = f"{Fore.GREEN}{Fore.RESET}"
elif status == "error":
prefix = f"{Fore.RED}{Fore.RESET}"
elif status == "warning":
prefix = f"{Fore.YELLOW}{Fore.RESET}"
else:
prefix = f"{Fore.CYAN}{Fore.RESET}"
print(f"{prefix} {message}")
def get_api_headers(api_token: str) -> Dict[str, str]:
"""Get headers for Cloudflare API requests"""
return {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
def load_domains() -> Dict:
"""Load domain configuration from ops/domains.yaml"""
try:
with open(DOMAINS_FILE) as f:
return yaml.safe_load(f)
except FileNotFoundError:
print_status(f"Error: {DOMAINS_FILE} not found", "error")
sys.exit(1)
except yaml.YAMLError as e:
print_status(f"Error parsing {DOMAINS_FILE}: {e}", "error")
sys.exit(1)
def get_existing_records(zone_id: str, api_token: str) -> List[Dict]:
"""Fetch all DNS records for a zone"""
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records"
headers = get_api_headers(api_token)
all_records = []
page = 1
per_page = 100
while True:
params = {"page": page, "per_page": per_page}
response = requests.get(url, headers=headers, params=params)
if response.status_code != 200:
print_status(f"Error fetching DNS records: {response.text}", "error")
sys.exit(1)
data = response.json()
if not data.get("success"):
print_status(f"API error: {data.get('errors')}", "error")
sys.exit(1)
records = data.get("result", [])
all_records.extend(records)
# Check if there are more pages
result_info = data.get("result_info", {})
if page * per_page >= result_info.get("total_count", 0):
break
page += 1
return all_records
def create_dns_record(zone_id: str, api_token: str, record: Dict) -> Dict:
"""Create a DNS record"""
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records"
headers = get_api_headers(api_token)
response = requests.post(url, headers=headers, json=record)
if response.status_code not in [200, 201]:
print_status(f"Error creating DNS record: {response.text}", "error")
return None
data = response.json()
if not data.get("success"):
print_status(f"API error: {data.get('errors')}", "error")
return None
return data.get("result")
def update_dns_record(zone_id: str, api_token: str, record_id: str, record: Dict) -> Dict:
"""Update a DNS record"""
url = f"{CF_API_BASE}/zones/{zone_id}/dns_records/{record_id}"
headers = get_api_headers(api_token)
response = requests.put(url, headers=headers, json=record)
if response.status_code != 200:
print_status(f"Error updating DNS record: {response.text}", "error")
return None
data = response.json()
if not data.get("success"):
print_status(f"API error: {data.get('errors')}", "error")
return None
return data.get("result")
def normalize_record_name(name: str, zone_name: str) -> str:
"""Normalize record name for comparison
Cloudflare returns full domain names (e.g., 'blackroad.systems' or 'www.blackroad.systems')
while config may use '@' for apex or just subdomain names.
"""
if name == "@":
return zone_name
elif not name.endswith(zone_name):
return f"{name}.{zone_name}"
return name
def records_match(config_record: Dict, cf_record: Dict, zone_name: str) -> bool:
"""Check if a config record matches a Cloudflare record"""
config_name = normalize_record_name(config_record.get("name", ""), zone_name)
cf_name = cf_record.get("name", "")
return (
config_record.get("type") == cf_record.get("type") and
config_name == cf_name and
config_record.get("content") == cf_record.get("content")
)
def sync_records(zone_id: str, api_token: str, zone_name: str, dry_run: bool = False):
"""Sync DNS records from domains.yaml to Cloudflare"""
print_status(f"Starting DNS sync for zone: {zone_name}")
print_status(f"Zone ID: {zone_id}")
if dry_run:
print_status("DRY RUN MODE - No changes will be made", "warning")
# Load configuration
config = load_domains()
# Get existing records from Cloudflare
print_status("Fetching existing DNS records from Cloudflare...")
existing = get_existing_records(zone_id, api_token)
print_status(f"Found {len(existing)} existing DNS records")
# Build index of existing records
existing_index = {}
for record in existing:
key = f"{record['type']}:{record['name']}"
existing_index[key] = record
# Process domains from config
created = 0
updated = 0
skipped = 0
errors = 0
for domain in config.get("domains", []):
# Only process domains configured for Cloudflare DNS mode
if domain.get("provider") != "cloudflare" or domain.get("mode") != "dns":
continue
# Skip if no record config
if "record" not in domain:
print_status(f"Skipping {domain.get('name')}: No record configuration", "warning")
continue
# Extract domain name (handle both root and subdomain)
domain_name = domain.get("name", "")
# Build record data
record_config = domain["record"]
record_type = record_config.get("type", "CNAME")
record_value = record_config.get("value", "")
# Determine record name for Cloudflare
# For root domains matching zone name, use "@"
if domain_name == zone_name:
record_name = "@"
else:
record_name = domain_name
record_data = {
"type": record_type,
"name": record_name,
"content": record_value,
"ttl": record_config.get("ttl", 1), # 1 = Auto
"proxied": record_config.get("proxied", True)
}
# For MX records, add priority
if record_type == "MX":
record_data["priority"] = record_config.get("priority", 10)
# Build key for lookup
full_name = normalize_record_name(record_name, zone_name)
key = f"{record_type}:{full_name}"
# Check if record exists
if key in existing_index:
existing_record = existing_index[key]
# Check if update is needed
needs_update = (
existing_record.get("content") != record_value or
existing_record.get("proxied") != record_data.get("proxied")
)
if needs_update:
print_status(f"Updating: {key} -> {record_value}", "warning")
if not dry_run:
result = update_dns_record(zone_id, api_token, existing_record["id"], record_data)
if result:
updated += 1
print_status(f" Updated successfully", "success")
else:
errors += 1
else:
print_status(f" [DRY RUN] Would update", "info")
updated += 1
else:
print_status(f"Unchanged: {key}", "info")
skipped += 1
else:
# Create new record
print_status(f"Creating: {key} -> {record_value}", "warning")
if not dry_run:
result = create_dns_record(zone_id, api_token, record_data)
if result:
created += 1
print_status(f" Created successfully", "success")
else:
errors += 1
else:
print_status(f" [DRY RUN] Would create", "info")
created += 1
# Summary
print("\n" + "="*60)
print_status("DNS Sync Complete!", "success")
print("="*60)
print(f" {Fore.GREEN}Created:{Fore.RESET} {created}")
print(f" {Fore.YELLOW}Updated:{Fore.RESET} {updated}")
print(f" {Fore.CYAN}Unchanged:{Fore.RESET} {skipped}")
print(f" {Fore.RED}Errors:{Fore.RESET} {errors}")
print("="*60)
if dry_run:
print_status("This was a DRY RUN - no actual changes were made", "warning")
print_status("Run without --dry-run to apply changes", "info")
def main():
"""Main entry point"""
parser = argparse.ArgumentParser(
description="Sync DNS records from ops/domains.yaml to Cloudflare"
)
parser.add_argument(
"--token",
help="Cloudflare API token (or set CF_API_TOKEN env var)"
)
parser.add_argument(
"--zone-id",
help="Cloudflare zone ID (or set CF_ZONE_ID env var)"
)
parser.add_argument(
"--zone-name",
default="blackroad.systems",
help="Zone name (default: blackroad.systems)"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be changed without making actual changes"
)
args = parser.parse_args()
# Get credentials
api_token = args.token or os.getenv("CF_API_TOKEN")
zone_id = args.zone_id or os.getenv("CF_ZONE_ID")
if not api_token:
print_status("Error: CF_API_TOKEN environment variable or --token argument required", "error")
print_status("Get your token at: https://dash.cloudflare.com/profile/api-tokens", "info")
sys.exit(1)
if not zone_id:
print_status("Error: CF_ZONE_ID environment variable or --zone-id argument required", "error")
print_status("Find your zone ID in the Cloudflare dashboard", "info")
sys.exit(1)
# Run sync
try:
sync_records(zone_id, api_token, args.zone_name, dry_run=args.dry_run)
except KeyboardInterrupt:
print("\n")
print_status("Interrupted by user", "warning")
sys.exit(1)
except Exception as e:
print_status(f"Unexpected error: {e}", "error")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,313 @@
#!/usr/bin/env python3
"""
Validate DNS configuration and check propagation status
This script helps verify that DNS records have been properly configured
and propagated across the internet. It performs:
- DNS resolution checks
- SSL certificate validation
- HTTP/HTTPS accessibility tests
- Redirect verification
Usage:
python scripts/cloudflare/validate_dns.py
python scripts/cloudflare/validate_dns.py --domain blackroad.systems
python scripts/cloudflare/validate_dns.py --all # Check all domains
Requirements:
pip install requests dnspython colorama
"""
import argparse
import socket
import ssl
import sys
from datetime import datetime
from typing import List, Dict, Optional
import requests
try:
import dns.resolver
HAS_DNS = True
except ImportError:
HAS_DNS = False
print("Warning: dnspython not installed. Install with: pip install dnspython")
try:
from colorama import init, Fore, Style
init()
except ImportError:
class Fore:
GREEN = RED = YELLOW = CYAN = RESET = ""
class Style:
BRIGHT = RESET_ALL = ""
def print_status(message: str, status: str = "info"):
"""Print colored status messages"""
if status == "success":
prefix = f"{Fore.GREEN}{Fore.RESET}"
elif status == "error":
prefix = f"{Fore.RED}{Fore.RESET}"
elif status == "warning":
prefix = f"{Fore.YELLOW}{Fore.RESET}"
else:
prefix = f"{Fore.CYAN}{Fore.RESET}"
print(f"{prefix} {message}")
def check_dns_resolution(domain: str) -> Dict:
"""Check DNS resolution for a domain"""
result = {
"domain": domain,
"resolved": False,
"ip_addresses": [],
"cname": None,
"error": None
}
if not HAS_DNS:
result["error"] = "dnspython not installed"
return result
try:
resolver = dns.resolver.Resolver()
resolver.timeout = 5
resolver.lifetime = 5
# Try CNAME first
try:
cname_answers = resolver.resolve(domain, 'CNAME')
if cname_answers:
result["cname"] = str(cname_answers[0].target).rstrip('.')
print_status(f" CNAME: {result['cname']}", "info")
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
pass
# Try A record
try:
answers = resolver.resolve(domain, 'A')
result["ip_addresses"] = [str(rdata) for rdata in answers]
result["resolved"] = True
for ip in result["ip_addresses"]:
print_status(f" A: {ip}", "info")
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN) as e:
result["error"] = str(e)
except Exception as e:
result["error"] = str(e)
return result
def check_ssl_certificate(domain: str) -> Dict:
"""Check SSL certificate for a domain"""
result = {
"domain": domain,
"valid": False,
"issuer": None,
"expires": None,
"days_remaining": None,
"error": None
}
try:
context = ssl.create_default_context()
with socket.create_connection((domain, 443), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=domain) as ssock:
cert = ssock.getpeercert()
result["issuer"] = dict(x[0] for x in cert['issuer'])
result["expires"] = cert['notAfter']
# Parse expiry date
expire_date = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
days_remaining = (expire_date - datetime.now()).days
result["days_remaining"] = days_remaining
result["valid"] = days_remaining > 0
print_status(f" Issuer: {result['issuer'].get('organizationName', 'Unknown')}", "info")
print_status(f" Expires: {result['expires']} ({days_remaining} days)",
"success" if days_remaining > 30 else "warning")
except Exception as e:
result["error"] = str(e)
return result
def check_http_accessibility(domain: str, check_redirects: bool = True) -> Dict:
"""Check HTTP/HTTPS accessibility and redirects"""
result = {
"domain": domain,
"http_accessible": False,
"https_accessible": False,
"redirects_to_https": False,
"www_redirects": False,
"status_code": None,
"error": None
}
try:
# Check HTTP -> HTTPS redirect
http_response = requests.get(f"http://{domain}", allow_redirects=True, timeout=10)
result["http_accessible"] = True
result["redirects_to_https"] = http_response.url.startswith("https://")
if result["redirects_to_https"]:
print_status(f" HTTP → HTTPS redirect: ✓", "success")
else:
print_status(f" HTTP → HTTPS redirect: ✗", "warning")
except Exception as e:
result["error"] = f"HTTP check failed: {e}"
try:
# Check HTTPS accessibility
https_response = requests.get(f"https://{domain}", timeout=10)
result["https_accessible"] = https_response.status_code == 200
result["status_code"] = https_response.status_code
if result["https_accessible"]:
print_status(f" HTTPS accessible: ✓ (Status: {https_response.status_code})", "success")
else:
print_status(f" HTTPS status: {https_response.status_code}", "warning")
except Exception as e:
if not result["error"]:
result["error"] = f"HTTPS check failed: {e}"
# Check www redirect if requested
if check_redirects and not domain.startswith("www."):
try:
www_response = requests.get(f"https://www.{domain}", allow_redirects=True, timeout=10)
result["www_redirects"] = www_response.url == f"https://{domain}/" or www_response.url == f"https://{domain}"
if result["www_redirects"]:
print_status(f" www → apex redirect: ✓", "success")
else:
print_status(f" www redirect: ✗ (goes to {www_response.url})", "warning")
except Exception as e:
print_status(f" www redirect check failed: {e}", "info")
return result
def validate_domain(domain: str, full_check: bool = True) -> bool:
"""Validate a single domain"""
print(f"\n{'='*60}")
print(f"{Style.BRIGHT}Validating: {domain}{Style.RESET_ALL}")
print(f"{'='*60}")
all_passed = True
# DNS Resolution
print(f"\n{Fore.CYAN}[1/3] DNS Resolution{Fore.RESET}")
dns_result = check_dns_resolution(domain)
if dns_result["resolved"]:
print_status(f"DNS resolved successfully", "success")
else:
print_status(f"DNS resolution failed: {dns_result.get('error', 'Unknown error')}", "error")
all_passed = False
if not full_check:
return all_passed
# SSL Certificate
print(f"\n{Fore.CYAN}[2/3] SSL Certificate{Fore.RESET}")
ssl_result = check_ssl_certificate(domain)
if ssl_result["valid"]:
print_status(f"SSL certificate is valid", "success")
else:
print_status(f"SSL certificate check failed: {ssl_result.get('error', 'Unknown error')}", "error")
all_passed = False
# HTTP Accessibility
print(f"\n{Fore.CYAN}[3/3] HTTP Accessibility{Fore.RESET}")
http_result = check_http_accessibility(domain)
if http_result["https_accessible"]:
print_status(f"Site is accessible via HTTPS", "success")
else:
print_status(f"Site accessibility check failed: {http_result.get('error', 'Unknown error')}", "error")
all_passed = False
# Summary
print(f"\n{'='*60}")
if all_passed:
print_status(f"{domain}: All checks passed! ✓", "success")
else:
print_status(f"{domain}: Some checks failed", "warning")
print(f"{'='*60}")
return all_passed
def main():
"""Main entry point"""
parser = argparse.ArgumentParser(
description="Validate DNS configuration and check propagation status"
)
parser.add_argument(
"--domain",
help="Domain to validate (default: blackroad.systems)"
)
parser.add_argument(
"--all",
action="store_true",
help="Check all BlackRoad domains"
)
parser.add_argument(
"--dns-only",
action="store_true",
help="Only check DNS resolution (skip SSL and HTTP checks)"
)
args = parser.parse_args()
# List of BlackRoad domains
all_domains = [
"blackroad.systems",
"blackroad.ai",
"blackroad.network",
"blackroad.me",
"lucidia.earth",
"aliceqi.com",
"blackroadqi.com",
"roadwallet.com",
"aliceos.io",
"blackroadquantum.com"
]
if args.all:
domains = all_domains
elif args.domain:
domains = [args.domain]
else:
domains = ["blackroad.systems"]
full_check = not args.dns_only
all_passed = True
for domain in domains:
passed = validate_domain(domain, full_check=full_check)
if not passed:
all_passed = False
# Final summary
print(f"\n{'='*60}")
print(f"{Style.BRIGHT}VALIDATION SUMMARY{Style.RESET_ALL}")
print(f"{'='*60}")
print(f"Domains checked: {len(domains)}")
if all_passed:
print_status("All validations passed!", "success")
sys.exit(0)
else:
print_status("Some validations failed", "warning")
sys.exit(1)
if __name__ == "__main__":
main()