mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 05:57:21 -05:00
Prevent BlackRoad-Operating-System monorepo from being added to Railway services
CRITICAL CHANGES: - Add comprehensive deployment architecture documentation - Prevent misconfiguration where monorepo is deployed instead of satellites - Clarify monorepo-to-satellite sync model across all docs CHANGES: 1. railway.toml - Add critical warning banner at top of file - Mark config as local development/testing only - Explain correct deployment model (satellites, not monorepo) 2. DEPLOYMENT_ARCHITECTURE.md (NEW) - Complete 500+ line deployment guide - Monorepo vs satellite model explained in detail - Critical rules: NEVER add monorepo to Railway - Service-to-repository mapping - Environment configuration guide - Cloudflare DNS configuration - Common mistakes and troubleshooting 3. README.md - Add prominent deployment warning box - Clarify monorepo is source of truth, not deployable - List satellite repos that should be deployed - Reference DEPLOYMENT_ARCHITECTURE.md 4. CLAUDE.md - Add critical deployment model section - Clarify Railway deployment is satellite-only - Update deployment workflow explanation - Add key rules for deployment 5. backend/.env.example - Fix ALLOWED_ORIGINS to reference satellites - Remove monorepo Railway URL reference - Add correct satellite service URLs 6. ops/domains.yaml - Fix os.blackroad.systems DNS target - Point to blackroad-os-core-production (satellite) - Remove incorrect monorepo Railway URL 7. scripts/validate_deployment_config.py (NEW) - Automated validation script - Checks for monorepo references in configs - Validates railway.toml, env files, DNS configs - Ensures DEPLOYMENT_ARCHITECTURE.md exists - Exit code 0 = pass, 1 = fail WHY THIS MATTERS: - Adding monorepo to Railway creates circular deploy loops - Environment variables break (wrong service URLs) - Cloudflare routing fails - Service dependencies misconfigured - Prevents production outages from misconfiguration CORRECT MODEL: - Monorepo = source of truth (orchestration only) - Satellites = deployable services (Railway deployment) - Code flows: monorepo → sync → satellite → Railway See: DEPLOYMENT_ARCHITECTURE.md for complete details
This commit is contained in:
69
CLAUDE.md
69
CLAUDE.md
@@ -578,29 +578,55 @@ async def test_create_user(client: AsyncClient):
|
||||
|
||||
## Deployment
|
||||
|
||||
### Railway (Backend)
|
||||
### ⚠️ CRITICAL: Monorepo vs Satellite Deployment Model
|
||||
|
||||
**Configuration** (`railway.toml`):
|
||||
```toml
|
||||
[build]
|
||||
builder = "DOCKERFILE"
|
||||
dockerfilePath = "backend/Dockerfile"
|
||||
**This repository (`BlackRoad-Operating-System`) is NOT deployed to production.**
|
||||
|
||||
[deploy]
|
||||
startCommand = "cd backend && uvicorn app.main:app --host 0.0.0.0 --port $PORT"
|
||||
healthcheck.path = "/health"
|
||||
```
|
||||
BlackRoad OS uses a **monorepo-to-satellite sync architecture**:
|
||||
|
||||
**Deployment Steps**:
|
||||
1. Push to main branch
|
||||
2. GitHub Action triggers Railway deploy
|
||||
3. Railway builds Docker image
|
||||
4. Runs Alembic migrations
|
||||
5. Starts FastAPI server
|
||||
6. Health check validation
|
||||
7. Traffic cutover
|
||||
**Monorepo Role** (`BlackRoad-Operating-System`):
|
||||
- ❌ **NOT deployed** to Railway or any production environment
|
||||
- ✅ Source of truth for all service code
|
||||
- ✅ Syncs code to satellite repos via GitHub Actions
|
||||
- ✅ Orchestration, prompts, and infrastructure configs
|
||||
|
||||
**Manual Deploy**:
|
||||
**Satellite Role** (Deployable Services):
|
||||
- ✅ **ONLY satellites are deployed** to Railway
|
||||
- Each satellite = one deployable service
|
||||
- Satellites: `blackroad-os-core`, `blackroad-os-api`, `blackroad-os-operator`, `blackroad-os-prism-console`, `blackroad-os-docs`, `blackroad-os-web`
|
||||
|
||||
**Key Rules**:
|
||||
1. ❌ **NEVER** add `BlackRoad-Operating-System` as a Railway service
|
||||
2. ❌ **NEVER** reference monorepo in env vars or service configs
|
||||
3. ❌ **NEVER** point Cloudflare to monorepo URLs
|
||||
4. ✅ **ALWAYS** deploy satellite repos individually
|
||||
5. ✅ **ALWAYS** edit code in monorepo (syncs to satellites automatically)
|
||||
|
||||
**See**: `DEPLOYMENT_ARCHITECTURE.md` for complete deployment model and troubleshooting.
|
||||
|
||||
---
|
||||
|
||||
### Railway (Satellite Deployment)
|
||||
|
||||
**IMPORTANT**: The `railway.toml` in this repo is for **local development/testing only**.
|
||||
|
||||
**Production deployment** is done via satellite repositories:
|
||||
- `BlackRoad-OS/blackroad-os-core` → `blackroad-os-core-production` (Railway service)
|
||||
- `BlackRoad-OS/blackroad-os-api` → `blackroad-os-api-production` (Railway service)
|
||||
- `BlackRoad-OS/blackroad-os-operator` → `blackroad-os-operator-production` (Railway service)
|
||||
- `BlackRoad-OS/blackroad-os-prism-console` → `blackroad-os-prism-console-production` (Railway service)
|
||||
- `BlackRoad-OS/blackroad-os-docs` → `blackroad-os-docs-production` (Railway service)
|
||||
|
||||
**Deployment Flow**:
|
||||
1. Edit code in monorepo (e.g., `services/core-api/`)
|
||||
2. Commit and push to `main`
|
||||
3. GitHub Action syncs to satellite (e.g., `BlackRoad-OS/blackroad-os-core`)
|
||||
4. Satellite triggers Railway deployment
|
||||
5. Railway builds Docker image
|
||||
6. Runs migrations
|
||||
7. Deploys to production
|
||||
|
||||
**Local Railway Testing** (monorepo only):
|
||||
```bash
|
||||
# Install Railway CLI
|
||||
curl -fsSL https://railway.app/install.sh | sh
|
||||
@@ -608,10 +634,13 @@ curl -fsSL https://railway.app/install.sh | sh
|
||||
# Login
|
||||
railway login
|
||||
|
||||
# Deploy
|
||||
# Deploy locally (NOT for production)
|
||||
railway up
|
||||
```
|
||||
|
||||
**Production Railway Deploy** (satellites):
|
||||
Done automatically via GitHub Actions when satellite repos update.
|
||||
|
||||
### GitHub Pages (Frontend)
|
||||
|
||||
**Deployment**:
|
||||
|
||||
523
DEPLOYMENT_ARCHITECTURE.md
Normal file
523
DEPLOYMENT_ARCHITECTURE.md
Normal file
@@ -0,0 +1,523 @@
|
||||
# BlackRoad OS Deployment Architecture
|
||||
|
||||
> **Last Updated**: 2025-11-19
|
||||
> **Status**: Canonical deployment model for all BlackRoad OS services
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [The Monorepo vs Satellite Model](#the-monorepo-vs-satellite-model)
|
||||
3. [Critical Rules](#critical-rules)
|
||||
4. [Deployment Topology](#deployment-topology)
|
||||
5. [Service-to-Repository Mapping](#service-to-repository-mapping)
|
||||
6. [Environment Configuration](#environment-configuration)
|
||||
7. [Cloudflare DNS Configuration](#cloudflare-dns-configuration)
|
||||
8. [Common Mistakes to Avoid](#common-mistakes-to-avoid)
|
||||
9. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
BlackRoad OS uses a **monorepo-to-satellite sync architecture** where:
|
||||
|
||||
- **`BlackRoad-Operating-System`** (this repo) = Source of truth, orchestration, sync logic
|
||||
- **Satellite repos** (`blackroad-os-core`, `blackroad-os-api`, etc.) = Deployable services
|
||||
|
||||
This document establishes the canonical deployment model to prevent misconfiguration.
|
||||
|
||||
---
|
||||
|
||||
## The Monorepo vs Satellite Model
|
||||
|
||||
### BlackRoad-Operating-System (Monorepo)
|
||||
|
||||
**Purpose**: Control plane and source of truth
|
||||
|
||||
**Role**:
|
||||
- Houses all service code in `services/`, `apps/`, `docs/`
|
||||
- Syncs code to satellite repos via GitHub Actions
|
||||
- Stores orchestration logic, prompts, and infrastructure configs
|
||||
- Serves as the "brain" - NOT the compute
|
||||
|
||||
**Deployment Status**: ❌ **NEVER DEPLOYED TO PRODUCTION**
|
||||
|
||||
**Why NOT deployable**:
|
||||
- No single entry point (contains multiple services)
|
||||
- Would create circular deployment dependencies
|
||||
- Not designed for runtime execution
|
||||
- Would break service discovery and routing
|
||||
|
||||
### Satellite Repositories
|
||||
|
||||
**Purpose**: Deployable, runtime services
|
||||
|
||||
**Satellites**:
|
||||
| Repository | Purpose | Railway Service | Cloudflare Domain |
|
||||
|------------|---------|-----------------|-------------------|
|
||||
| `blackroad-os-core` | Core API & business logic | `blackroad-os-core-production` | `core.blackroad.systems` |
|
||||
| `blackroad-os-api` | Public API gateway | `blackroad-os-api-production` | `api.blackroad.systems` |
|
||||
| `blackroad-os-operator` | Agent runtime & orchestrator | `blackroad-os-operator-production` | `operator.blackroad.systems` |
|
||||
| `blackroad-os-prism-console` | Status console frontend | `blackroad-os-prism-console-production` | `prism.blackroad.systems` |
|
||||
| `blackroad-os-docs` | Documentation site | `blackroad-os-docs-production` | `docs.blackroad.systems` |
|
||||
| `blackroad-os-web` | Public website | `blackroad-os-web-production` | `blackroad.systems` |
|
||||
|
||||
**Deployment Status**: ✅ **EACH DEPLOYED INDEPENDENTLY TO RAILWAY**
|
||||
|
||||
**Sync Process**:
|
||||
1. Developer edits code in monorepo (e.g., `services/core-api/`)
|
||||
2. GitHub Action syncs changes to satellite (`BlackRoad-OS/blackroad-os-core`)
|
||||
3. Satellite triggers Railway deployment
|
||||
4. Railway deploys to production
|
||||
|
||||
**See**: `docs/os/monorepo-sync.md` for sync details
|
||||
|
||||
---
|
||||
|
||||
## Critical Rules
|
||||
|
||||
### ❌ NEVER DO THIS
|
||||
|
||||
1. **Never add `BlackRoad-Operating-System` to Railway as a service**
|
||||
- Not in production environments
|
||||
- Not in staging environments
|
||||
- Not in development environments (unless explicitly testing locally)
|
||||
|
||||
2. **Never reference the monorepo in service configurations**
|
||||
- Don't add it to env vars (e.g., `MONOREPO_URL`)
|
||||
- Don't add it as a dependency in other services
|
||||
- Don't point Cloudflare to the monorepo
|
||||
|
||||
3. **Never deploy the monorepo to production**
|
||||
- It's not designed to run as a service
|
||||
- It will break everything
|
||||
|
||||
### ✅ ALWAYS DO THIS
|
||||
|
||||
1. **Deploy ONLY satellite repos to Railway**
|
||||
- Each satellite = one Railway service
|
||||
- Each service = one environment (dev, staging, production)
|
||||
|
||||
2. **Edit code ONLY in the monorepo**
|
||||
- Satellites are read-only mirrors
|
||||
- All changes flow: monorepo → sync → satellite → deploy
|
||||
|
||||
3. **Point Cloudflare ONLY to satellite Railway URLs**
|
||||
- `core.blackroad.systems` → `blackroad-os-core-production.up.railway.app`
|
||||
- `api.blackroad.systems` → `blackroad-os-api-production.up.railway.app`
|
||||
- etc.
|
||||
|
||||
4. **Create production environments for ALL satellites**
|
||||
- Each satellite needs: dev, staging, production
|
||||
- Example: `blackroad-os-core-dev`, `blackroad-os-core-staging`, `blackroad-os-core-production`
|
||||
|
||||
---
|
||||
|
||||
## Deployment Topology
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ BlackRoad-Operating-System (Monorepo) │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ services/ │ │ apps/ │ │ docs/ │ │
|
||||
│ │ core-api/ │ │ prism-console│ │ site/ │ │
|
||||
│ │ public-api/ │ │ web/ │ │ │ │
|
||||
│ │ operator/ │ │ │ │ │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ └──────────────────┴──────────────────┘ │
|
||||
│ GitHub Actions Sync │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Satellite Repositories (Deployable) │
|
||||
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐│
|
||||
│ │ blackroad-os- │ │ blackroad-os- │ │ blackroad-os- ││
|
||||
│ │ core │ │ api │ │ operator ││
|
||||
│ └────────────────┘ └────────────────┘ └────────────────┘│
|
||||
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐│
|
||||
│ │ blackroad-os- │ │ blackroad-os- │ │ blackroad-os- ││
|
||||
│ │ prism-console │ │ web │ │ docs ││
|
||||
│ └────────────────┘ └────────────────┘ └────────────────┘│
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Railway (Deployment Platform) │
|
||||
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐│
|
||||
│ │ core-production│ │ api-production │ │ operator-prod ││
|
||||
│ │ .up.railway.app│ │ .up.railway.app│ │ .up.railway.app││
|
||||
│ └────────────────┘ └────────────────┘ └────────────────┘│
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Cloudflare (DNS & Routing) │
|
||||
│ core.blackroad.systems → blackroad-os-core-production │
|
||||
│ api.blackroad.systems → blackroad-os-api-production │
|
||||
│ operator.blackroad.systems → blackroad-os-operator-production│
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Service-to-Repository Mapping
|
||||
|
||||
### Canonical Mapping
|
||||
|
||||
Defined in `infra/github/sync-config.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
core-api:
|
||||
monorepo_path: "services/core-api"
|
||||
target_repo: "BlackRoad-OS/blackroad-os-core"
|
||||
target_branch: "main"
|
||||
public-api:
|
||||
monorepo_path: "services/public-api"
|
||||
target_repo: "BlackRoad-OS/blackroad-os-api"
|
||||
target_branch: "main"
|
||||
operator:
|
||||
monorepo_path: "services/operator"
|
||||
target_repo: "BlackRoad-OS/blackroad-os-operator"
|
||||
target_branch: "main"
|
||||
|
||||
apps:
|
||||
prism-console:
|
||||
monorepo_path: "apps/prism-console"
|
||||
target_repo: "BlackRoad-OS/blackroad-os-prism-console"
|
||||
target_branch: "main"
|
||||
web:
|
||||
monorepo_path: "apps/web"
|
||||
target_repo: "BlackRoad-OS/blackroad-os-web"
|
||||
target_branch: "main"
|
||||
|
||||
docs:
|
||||
site:
|
||||
monorepo_path: "docs/site"
|
||||
target_repo: "BlackRoad-OS/blackroad-os-docs"
|
||||
target_branch: "main"
|
||||
```
|
||||
|
||||
### Development Workflow
|
||||
|
||||
1. **Edit in monorepo**: `services/core-api/app/main.py`
|
||||
2. **Commit to monorepo**: `git commit -m "Add health endpoint"`
|
||||
3. **Push to monorepo**: `git push origin main`
|
||||
4. **GitHub Action syncs**: Copies `services/core-api/` → `BlackRoad-OS/blackroad-os-core`
|
||||
5. **Railway deploys**: Deploys `blackroad-os-core` to production
|
||||
|
||||
---
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
### Required Railway Environments
|
||||
|
||||
Each satellite repository needs THREE environments:
|
||||
|
||||
1. **Development** (`-dev`)
|
||||
- Connected to: `dev` or `develop` branch
|
||||
- Example: `blackroad-os-core-dev.up.railway.app`
|
||||
|
||||
2. **Staging** (`-staging`)
|
||||
- Connected to: `staging` branch
|
||||
- Example: `blackroad-os-core-staging.up.railway.app`
|
||||
|
||||
3. **Production** (`-production`)
|
||||
- Connected to: `main` branch
|
||||
- Example: `blackroad-os-core-production.up.railway.app`
|
||||
|
||||
### Environment Variables by Service
|
||||
|
||||
#### Core API (`blackroad-os-core`)
|
||||
|
||||
```bash
|
||||
# Database
|
||||
DATABASE_URL=postgresql+asyncpg://...
|
||||
REDIS_URL=redis://...
|
||||
|
||||
# Auth
|
||||
SECRET_KEY=...
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
|
||||
# Service URLs
|
||||
API_URL=https://api.blackroad.systems
|
||||
OPERATOR_URL=https://operator.blackroad.systems
|
||||
PRISM_URL=https://prism.blackroad.systems
|
||||
|
||||
# Environment
|
||||
ENVIRONMENT=production
|
||||
DEBUG=False
|
||||
```
|
||||
|
||||
#### Public API (`blackroad-os-api`)
|
||||
|
||||
```bash
|
||||
# Core API reference
|
||||
CORE_API_URL=https://core.blackroad.systems
|
||||
|
||||
# Auth
|
||||
JWT_SECRET=...
|
||||
|
||||
# Environment
|
||||
ENVIRONMENT=production
|
||||
```
|
||||
|
||||
#### Operator (`blackroad-os-operator`)
|
||||
|
||||
```bash
|
||||
# Core API reference
|
||||
CORE_API_URL=https://core.blackroad.systems
|
||||
API_URL=https://api.blackroad.systems
|
||||
|
||||
# GitHub
|
||||
GITHUB_TOKEN=...
|
||||
GITHUB_APP_ID=...
|
||||
|
||||
# Environment
|
||||
ENVIRONMENT=production
|
||||
```
|
||||
|
||||
#### Prism Console (`blackroad-os-prism-console`)
|
||||
|
||||
```bash
|
||||
# Frontend env vars (Next.js)
|
||||
NEXT_PUBLIC_CORE_API_URL=https://core.blackroad.systems
|
||||
NEXT_PUBLIC_API_URL=https://api.blackroad.systems
|
||||
NEXT_PUBLIC_OPERATOR_URL=https://operator.blackroad.systems
|
||||
```
|
||||
|
||||
### ❌ NEVER Add These Vars
|
||||
|
||||
```bash
|
||||
# ❌ DO NOT ADD THESE - THEY WILL BREAK EVERYTHING
|
||||
MONOREPO_URL=https://github.com/blackboxprogramming/BlackRoad-Operating-System
|
||||
BLACKROAD_OS_REPO=BlackRoad-Operating-System
|
||||
SOURCE_REPO=BlackRoad-Operating-System
|
||||
```
|
||||
|
||||
**Why?** These vars reference the non-deployable monorepo, creating confusion and breaking service discovery.
|
||||
|
||||
---
|
||||
|
||||
## Cloudflare DNS Configuration
|
||||
|
||||
### Canonical DNS Records
|
||||
|
||||
| Subdomain | Type | Target | Proxied |
|
||||
|-----------|------|--------|---------|
|
||||
| `blackroad.systems` | CNAME | `blackroad-os-web-production.up.railway.app` | ✅ Yes |
|
||||
| `core.blackroad.systems` | CNAME | `blackroad-os-core-production.up.railway.app` | ✅ Yes |
|
||||
| `api.blackroad.systems` | CNAME | `blackroad-os-api-production.up.railway.app` | ✅ Yes |
|
||||
| `operator.blackroad.systems` | CNAME | `blackroad-os-operator-production.up.railway.app` | ✅ Yes |
|
||||
| `prism.blackroad.systems` | CNAME | `blackroad-os-prism-console-production.up.railway.app` | ✅ Yes |
|
||||
| `docs.blackroad.systems` | CNAME | `blackroad-os-docs-production.up.railway.app` | ✅ Yes |
|
||||
| `os.blackroad.systems` | CNAME | `prism.blackroad.systems` | ✅ Yes |
|
||||
|
||||
### ❌ NEVER Point Cloudflare To
|
||||
|
||||
```
|
||||
# ❌ WRONG - Monorepo is not deployed
|
||||
core.blackroad.systems → blackroad-operating-system.up.railway.app
|
||||
|
||||
# ❌ WRONG - Monorepo doesn't exist on Railway
|
||||
api.blackroad.systems → blackroad-os-monorepo-production.up.railway.app
|
||||
```
|
||||
|
||||
**See**: `CLOUDFLARE_DNS_BLUEPRINT.md` for complete DNS configuration
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
### Mistake #1: Adding Monorepo to Railway
|
||||
|
||||
**Problem**:
|
||||
```bash
|
||||
# ❌ Creating Railway service for monorepo
|
||||
railway link blackboxprogramming/BlackRoad-Operating-System
|
||||
railway up
|
||||
```
|
||||
|
||||
**Why it's wrong**:
|
||||
- Monorepo contains multiple services (not a single deployable)
|
||||
- No single entry point or health check
|
||||
- Will create circular dependencies
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# ✅ Deploy satellite repos instead
|
||||
railway link BlackRoad-OS/blackroad-os-core
|
||||
railway up
|
||||
```
|
||||
|
||||
### Mistake #2: Adding Monorepo to Service Env Vars
|
||||
|
||||
**Problem**:
|
||||
```bash
|
||||
# ❌ In blackroad-os-core environment
|
||||
MONOREPO_URL=https://github.com/blackboxprogramming/BlackRoad-Operating-System
|
||||
```
|
||||
|
||||
**Why it's wrong**:
|
||||
- Services should reference other services, not the source repo
|
||||
- Creates confusion between source code and runtime services
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# ✅ Reference other services instead
|
||||
API_URL=https://api.blackroad.systems
|
||||
OPERATOR_URL=https://operator.blackroad.systems
|
||||
```
|
||||
|
||||
### Mistake #3: Pointing Cloudflare to Monorepo
|
||||
|
||||
**Problem**:
|
||||
```
|
||||
# ❌ Cloudflare DNS
|
||||
core.blackroad.systems → blackroad-operating-system.up.railway.app
|
||||
```
|
||||
|
||||
**Why it's wrong**:
|
||||
- Monorepo is not deployed to Railway
|
||||
- URL doesn't exist
|
||||
- Will result in DNS failures
|
||||
|
||||
**Solution**:
|
||||
```
|
||||
# ✅ Point to satellite Railway URL
|
||||
core.blackroad.systems → blackroad-os-core-production.up.railway.app
|
||||
```
|
||||
|
||||
### Mistake #4: Editing Satellite Repos Directly
|
||||
|
||||
**Problem**:
|
||||
```bash
|
||||
# ❌ Editing blackroad-os-core directly
|
||||
cd blackroad-os-core
|
||||
git commit -m "Fix bug"
|
||||
git push
|
||||
```
|
||||
|
||||
**Why it's wrong**:
|
||||
- Satellites are read-only mirrors
|
||||
- Changes will be overwritten by next sync
|
||||
- Creates drift between monorepo and satellite
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# ✅ Edit in monorepo
|
||||
cd BlackRoad-Operating-System/services/core-api
|
||||
# Make changes
|
||||
git commit -m "Fix bug"
|
||||
git push
|
||||
# Sync workflow automatically updates satellite
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: "Service not deploying after code change"
|
||||
|
||||
**Diagnosis**:
|
||||
1. Check if code was edited in monorepo (not satellite)
|
||||
2. Verify sync workflow ran successfully
|
||||
3. Check Railway deployment logs
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# 1. Verify sync workflow
|
||||
gh workflow view sync-core-api
|
||||
gh run list --workflow=sync-core-api
|
||||
|
||||
# 2. Manually trigger sync if needed
|
||||
gh workflow run sync-core-api
|
||||
|
||||
# 3. Check Railway deployment
|
||||
railway logs -s blackroad-os-core-production
|
||||
```
|
||||
|
||||
### Issue: "Environment variables not resolving correctly"
|
||||
|
||||
**Diagnosis**:
|
||||
1. Check if vars reference monorepo (wrong)
|
||||
2. Verify service-to-service URLs are correct
|
||||
3. Confirm Railway environment is set up properly
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# 1. List current env vars
|
||||
railway variables
|
||||
|
||||
# 2. Remove monorepo references
|
||||
railway variables delete MONOREPO_URL
|
||||
|
||||
# 3. Add correct service URLs
|
||||
railway variables set CORE_API_URL=https://core.blackroad.systems
|
||||
```
|
||||
|
||||
### Issue: "Cloudflare returning 522 errors"
|
||||
|
||||
**Diagnosis**:
|
||||
1. Check if DNS points to correct Railway URL
|
||||
2. Verify Railway service is running
|
||||
3. Confirm health check is passing
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# 1. Check Railway service status
|
||||
railway status
|
||||
|
||||
# 2. Verify health endpoint
|
||||
curl https://blackroad-os-core-production.up.railway.app/health
|
||||
|
||||
# 3. Update Cloudflare DNS if needed
|
||||
# (via Cloudflare dashboard: DNS → Edit CNAME)
|
||||
```
|
||||
|
||||
### Issue: "Circular deployment loops"
|
||||
|
||||
**Diagnosis**:
|
||||
- Monorepo may be configured as a dependency
|
||||
- Service may be triggering its own deployment
|
||||
|
||||
**Solution**:
|
||||
1. Remove monorepo from Railway services
|
||||
2. Ensure satellites deploy independently
|
||||
3. Check GitHub Actions triggers
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### The Golden Rules
|
||||
|
||||
1. **Monorepo = Source of Truth** (not deployed)
|
||||
2. **Satellites = Deployable Services** (deployed to Railway)
|
||||
3. **Edit in monorepo** → Sync to satellites → Deploy automatically
|
||||
4. **Never add monorepo to Railway** or service configurations
|
||||
5. **Cloudflare points to satellites**, not monorepo
|
||||
|
||||
### Quick Reference
|
||||
|
||||
**When you want to...**
|
||||
|
||||
- **Deploy a service**: Use satellite repo (`blackroad-os-core`), not monorepo
|
||||
- **Edit code**: Edit in monorepo (`services/core-api`), not satellite
|
||||
- **Configure DNS**: Point to satellite Railway URL, not monorepo
|
||||
- **Set env vars**: Reference other services, not monorepo
|
||||
- **Create new service**: Add to monorepo, create sync workflow, deploy satellite
|
||||
|
||||
---
|
||||
|
||||
**For questions or issues**, see:
|
||||
- `docs/os/monorepo-sync.md` - Sync process details
|
||||
- `CLOUDFLARE_DNS_BLUEPRINT.md` - DNS configuration
|
||||
- `infra/railway/ENVIRONMENT_GUIDE.md` - Railway setup
|
||||
- `CLAUDE.md` - Development guide
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-11-19*
|
||||
*This document is canonical for all BlackRoad OS deployments.*
|
||||
20
README.md
20
README.md
@@ -31,6 +31,26 @@ BlackRoad OS is a fully functional web-based operating system interface that bri
|
||||
|
||||
All BlackRoad services, apps, and docs now live in this monorepo and sync out automatically to their mirror repositories under the `BlackRoad-OS` GitHub organization. Edit here; automation mirrors to the satellites.
|
||||
|
||||
> ### ⚠️ CRITICAL DEPLOYMENT WARNING ⚠️
|
||||
>
|
||||
> **This repository is NOT deployed to production.**
|
||||
>
|
||||
> - ❌ **DO NOT** add `BlackRoad-Operating-System` to Railway as a service
|
||||
> - ❌ **DO NOT** deploy this monorepo to any production environment
|
||||
> - ❌ **DO NOT** reference this repo in service configurations or env vars
|
||||
>
|
||||
> **Deploy ONLY the satellite repositories:**
|
||||
> - `blackroad-os-core` (Core API)
|
||||
> - `blackroad-os-api` (Public API)
|
||||
> - `blackroad-os-operator` (Agent Runtime)
|
||||
> - `blackroad-os-prism-console` (Status Console)
|
||||
> - `blackroad-os-docs` (Documentation)
|
||||
> - `blackroad-os-web` (Public Website)
|
||||
>
|
||||
> **This repo is the source of truth for code**, but **satellites are the deployable services**.
|
||||
>
|
||||
> **See**: `DEPLOYMENT_ARCHITECTURE.md` for complete deployment model.
|
||||
|
||||
### Canonical layout
|
||||
|
||||
- `services/core-api` → `BlackRoad-OS/blackroad-os-core`
|
||||
|
||||
@@ -24,7 +24,7 @@ ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
WALLET_MASTER_KEY=changeme-wallet-master-key
|
||||
ALLOWED_ORIGINS=https://blackroad.systems,https://www.blackroad.systems,https://os.blackroad.systems,https://blackroad-operating-system-production.up.railway.app,http://localhost:3000,http://localhost:8000
|
||||
ALLOWED_ORIGINS=https://blackroad.systems,https://www.blackroad.systems,https://os.blackroad.systems,https://core.blackroad.systems,https://api.blackroad.systems,https://blackroad-os-core-production.up.railway.app,http://localhost:3000,http://localhost:8000
|
||||
JWT_SECRET=changeme-jwt-secret
|
||||
SESSION_SECRET=changeme-session-secret
|
||||
NEXTAUTH_SECRET=changeme-nextauth-secret
|
||||
|
||||
@@ -32,9 +32,8 @@ domains:
|
||||
mode: "dns"
|
||||
record:
|
||||
type: "CNAME"
|
||||
value: "blackroad-operating-system-production.up.railway.app"
|
||||
value: "YOUR-PROD-RAILWAY-APP.up.railway.app" # replace with your Railway host
|
||||
notes: "Alternative subdomain alias for the OS application"
|
||||
value: "blackroad-os-core-production.up.railway.app" # Core API satellite service
|
||||
notes: "Alternative subdomain alias for the OS application - points to Core API satellite"
|
||||
|
||||
- name: "blackroad.ai"
|
||||
type: "root"
|
||||
|
||||
30
railway.toml
30
railway.toml
@@ -1,5 +1,33 @@
|
||||
# ==============================================================================
|
||||
# ⚠️ CRITICAL WARNING - DO NOT USE THIS CONFIG IN PRODUCTION RAILWAY ⚠️
|
||||
# ==============================================================================
|
||||
#
|
||||
# This railway.toml is for LOCAL DEVELOPMENT AND TESTING ONLY.
|
||||
#
|
||||
# ❌ DO NOT add "BlackRoad-Operating-System" as a Railway service
|
||||
# ❌ DO NOT deploy this monorepo to production Railway environments
|
||||
# ❌ DO NOT add this repo to service configurations or env vars
|
||||
#
|
||||
# ✅ CORRECT DEPLOYMENT MODEL:
|
||||
# - This repo is the SOURCE OF TRUTH (orchestration/sync only)
|
||||
# - Deploy ONLY the satellite repos to Railway:
|
||||
# • blackroad-os-core (from BlackRoad-OS/blackroad-os-core)
|
||||
# • blackroad-os-api (from BlackRoad-OS/blackroad-os-api)
|
||||
# • blackroad-os-operator (from BlackRoad-OS/blackroad-os-operator)
|
||||
# • blackroad-os-prism-console (from BlackRoad-OS/blackroad-os-prism-console)
|
||||
# • blackroad-os-docs (from BlackRoad-OS/blackroad-os-docs)
|
||||
#
|
||||
# WHY THIS MATTERS:
|
||||
# - Adding the monorepo to Railway will create circular deploy loops
|
||||
# - Environment variables will break (wrong service URLs)
|
||||
# - Cloudflare routing will fail
|
||||
# - Service dependencies will be misconfigured
|
||||
#
|
||||
# See: docs/os/monorepo-sync.md and DEPLOYMENT_ARCHITECTURE.md
|
||||
# ==============================================================================
|
||||
|
||||
# BlackRoad OS Monorepo - Railway Configuration
|
||||
# This configures multiple services from a single repository
|
||||
# This configures multiple services from a single repository FOR LOCAL DEV ONLY
|
||||
|
||||
# Backend (Core API)
|
||||
[[services]]
|
||||
|
||||
373
scripts/validate_deployment_config.py
Executable file
373
scripts/validate_deployment_config.py
Executable file
@@ -0,0 +1,373 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Deployment Configuration Validator
|
||||
|
||||
This script validates that the BlackRoad-Operating-System monorepo
|
||||
is NOT being incorrectly added to Railway configurations or service
|
||||
environment variables.
|
||||
|
||||
Usage:
|
||||
python scripts/validate_deployment_config.py
|
||||
|
||||
Exit codes:
|
||||
0 - All validations passed
|
||||
1 - Validation failures detected
|
||||
|
||||
Author: BlackRoad OS Team
|
||||
Last Updated: 2025-11-19
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple, Dict
|
||||
|
||||
# ANSI color codes
|
||||
GREEN = "\033[92m"
|
||||
RED = "\033[91m"
|
||||
YELLOW = "\033[93m"
|
||||
BLUE = "\033[94m"
|
||||
RESET = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
|
||||
# Repository root
|
||||
REPO_ROOT = Path(__file__).parent.parent
|
||||
|
||||
# Patterns that indicate monorepo is being incorrectly referenced
|
||||
FORBIDDEN_PATTERNS = [
|
||||
r"BlackRoad-Operating-System",
|
||||
r"blackroad-operating-system",
|
||||
r"BLACKROAD_OPERATING_SYSTEM",
|
||||
r"monorepo\.up\.railway\.app",
|
||||
r"blackroad-os-monorepo",
|
||||
]
|
||||
|
||||
# Allowed contexts where monorepo reference is OK
|
||||
ALLOWED_FILES = [
|
||||
"README.md",
|
||||
"DEPLOYMENT_ARCHITECTURE.md",
|
||||
"CLAUDE.md",
|
||||
"docs/",
|
||||
".git/",
|
||||
".github/",
|
||||
"infra/github/",
|
||||
"scripts/",
|
||||
".md", # All markdown files (usually docs)
|
||||
"IMPLEMENTATION", # Implementation plan docs
|
||||
"PHASE", # Phase summary docs
|
||||
"ORG_STRUCTURE.md",
|
||||
"CODEBASE_STATUS.md",
|
||||
]
|
||||
|
||||
# Files to check for forbidden patterns
|
||||
CHECK_PATTERNS = [
|
||||
"**/.env",
|
||||
"**/.env.example",
|
||||
"**/.env.production",
|
||||
"**/.env.staging",
|
||||
"**/.env.development",
|
||||
"**/railway.json",
|
||||
"**/vercel.json",
|
||||
"**/netlify.toml",
|
||||
]
|
||||
|
||||
|
||||
class ValidationResult:
|
||||
"""Stores validation results"""
|
||||
|
||||
def __init__(self):
|
||||
self.errors: List[Tuple[str, str]] = []
|
||||
self.warnings: List[Tuple[str, str]] = []
|
||||
self.passed: List[str] = []
|
||||
|
||||
def add_error(self, check: str, message: str):
|
||||
"""Add a validation error"""
|
||||
self.errors.append((check, message))
|
||||
|
||||
def add_warning(self, check: str, message: str):
|
||||
"""Add a validation warning"""
|
||||
self.warnings.append((check, message))
|
||||
|
||||
def add_pass(self, check: str):
|
||||
"""Add a passing check"""
|
||||
self.passed.append(check)
|
||||
|
||||
def has_failures(self) -> bool:
|
||||
"""Check if there are any failures"""
|
||||
return len(self.errors) > 0
|
||||
|
||||
def print_summary(self):
|
||||
"""Print validation summary"""
|
||||
print(f"\n{BOLD}{'=' * 70}{RESET}")
|
||||
print(f"{BOLD}Deployment Configuration Validation Results{RESET}")
|
||||
print(f"{BOLD}{'=' * 70}{RESET}\n")
|
||||
|
||||
# Print errors
|
||||
if self.errors:
|
||||
print(f"{RED}{BOLD}❌ ERRORS ({len(self.errors)}):{RESET}")
|
||||
for check, message in self.errors:
|
||||
print(f"{RED} • {check}:{RESET} {message}")
|
||||
print()
|
||||
|
||||
# Print warnings
|
||||
if self.warnings:
|
||||
print(f"{YELLOW}{BOLD}⚠️ WARNINGS ({len(self.warnings)}):{RESET}")
|
||||
for check, message in self.warnings:
|
||||
print(f"{YELLOW} • {check}:{RESET} {message}")
|
||||
print()
|
||||
|
||||
# Print passed checks
|
||||
if self.passed:
|
||||
print(f"{GREEN}{BOLD}✅ PASSED ({len(self.passed)}):{RESET}")
|
||||
for check in self.passed:
|
||||
print(f"{GREEN} • {check}{RESET}")
|
||||
print()
|
||||
|
||||
# Overall status
|
||||
print(f"{BOLD}{'=' * 70}{RESET}")
|
||||
if self.has_failures():
|
||||
print(f"{RED}{BOLD}❌ VALIDATION FAILED{RESET}")
|
||||
print(f"\nThe monorepo is being incorrectly referenced in deployment configs.")
|
||||
print(f"See {BLUE}DEPLOYMENT_ARCHITECTURE.md{RESET} for correct deployment model.\n")
|
||||
return 1
|
||||
elif self.warnings:
|
||||
print(f"{YELLOW}{BOLD}⚠️ VALIDATION PASSED WITH WARNINGS{RESET}\n")
|
||||
return 0
|
||||
else:
|
||||
print(f"{GREEN}{BOLD}✅ ALL VALIDATIONS PASSED{RESET}\n")
|
||||
return 0
|
||||
|
||||
|
||||
def is_allowed_file(file_path: Path) -> bool:
|
||||
"""Check if file is in allowed list for monorepo references"""
|
||||
file_str = str(file_path)
|
||||
for allowed in ALLOWED_FILES:
|
||||
if allowed in file_str:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def check_railway_toml(result: ValidationResult):
|
||||
"""Validate railway.toml is marked for local dev only"""
|
||||
railway_toml = REPO_ROOT / "railway.toml"
|
||||
|
||||
if not railway_toml.exists():
|
||||
result.add_warning("railway.toml", "File not found (OK if not using Railway)")
|
||||
return
|
||||
|
||||
content = railway_toml.read_text()
|
||||
|
||||
# Check for warning banner
|
||||
if "CRITICAL WARNING" not in content:
|
||||
result.add_error(
|
||||
"railway.toml",
|
||||
"Missing CRITICAL WARNING banner at top of file"
|
||||
)
|
||||
|
||||
# Check for "LOCAL DEV" or similar marker
|
||||
if "LOCAL DEV" not in content and "DEVELOPMENT" not in content:
|
||||
result.add_error(
|
||||
"railway.toml",
|
||||
"Not clearly marked as local development only"
|
||||
)
|
||||
|
||||
if not result.errors:
|
||||
result.add_pass("railway.toml has proper warnings")
|
||||
|
||||
|
||||
def check_env_files(result: ValidationResult):
|
||||
"""Check environment files for monorepo references"""
|
||||
env_files = []
|
||||
|
||||
for pattern in CHECK_PATTERNS:
|
||||
env_files.extend(REPO_ROOT.glob(pattern))
|
||||
|
||||
found_issues = False
|
||||
checked_count = 0
|
||||
|
||||
for env_file in env_files:
|
||||
if is_allowed_file(env_file):
|
||||
continue
|
||||
|
||||
checked_count += 1
|
||||
content = env_file.read_text()
|
||||
|
||||
for pattern in FORBIDDEN_PATTERNS:
|
||||
matches = re.finditer(pattern, content, re.IGNORECASE)
|
||||
for match in matches:
|
||||
# Get line number
|
||||
line_num = content[:match.start()].count('\n') + 1
|
||||
result.add_error(
|
||||
f"{env_file.name}:{line_num}",
|
||||
f"Contains forbidden reference: '{match.group()}'"
|
||||
)
|
||||
found_issues = True
|
||||
|
||||
if checked_count == 0:
|
||||
result.add_warning("env files", "No environment files found to check")
|
||||
elif not found_issues:
|
||||
result.add_pass(f"Environment files clean ({checked_count} checked)")
|
||||
|
||||
|
||||
def check_satellite_configs(result: ValidationResult):
|
||||
"""Check if satellites are properly configured"""
|
||||
sync_config = REPO_ROOT / "infra/github/sync-config.yml"
|
||||
|
||||
if not sync_config.exists():
|
||||
result.add_warning(
|
||||
"sync-config.yml",
|
||||
"Satellite sync config not found"
|
||||
)
|
||||
return
|
||||
|
||||
with open(sync_config) as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
# Expected satellites
|
||||
expected_services = ["core-api", "public-api", "operator"]
|
||||
expected_apps = ["prism-console", "web"]
|
||||
|
||||
services = config.get("services", {})
|
||||
apps = config.get("apps", {})
|
||||
|
||||
# Check all expected services are configured
|
||||
missing_services = [s for s in expected_services if s not in services]
|
||||
missing_apps = [a for a in expected_apps if a not in apps]
|
||||
|
||||
if missing_services:
|
||||
result.add_warning(
|
||||
"sync-config.yml",
|
||||
f"Missing service configs: {', '.join(missing_services)}"
|
||||
)
|
||||
|
||||
if missing_apps:
|
||||
result.add_warning(
|
||||
"sync-config.yml",
|
||||
f"Missing app configs: {', '.join(missing_apps)}"
|
||||
)
|
||||
|
||||
if not missing_services and not missing_apps:
|
||||
result.add_pass("Satellite sync configuration complete")
|
||||
|
||||
|
||||
def check_cloudflare_docs(result: ValidationResult):
|
||||
"""Check Cloudflare documentation for correct DNS setup"""
|
||||
cloudflare_doc = REPO_ROOT / "CLOUDFLARE_DNS_BLUEPRINT.md"
|
||||
|
||||
if not cloudflare_doc.exists():
|
||||
result.add_warning(
|
||||
"Cloudflare docs",
|
||||
"CLOUDFLARE_DNS_BLUEPRINT.md not found"
|
||||
)
|
||||
return
|
||||
|
||||
content = cloudflare_doc.read_text()
|
||||
|
||||
# Check for incorrect monorepo references in DNS
|
||||
forbidden_dns = [
|
||||
"blackroad-operating-system.up.railway.app",
|
||||
"monorepo.up.railway.app",
|
||||
]
|
||||
|
||||
found_issues = False
|
||||
for forbidden in forbidden_dns:
|
||||
if forbidden in content.lower():
|
||||
result.add_error(
|
||||
"CLOUDFLARE_DNS_BLUEPRINT.md",
|
||||
f"Contains forbidden DNS target: {forbidden}"
|
||||
)
|
||||
found_issues = True
|
||||
|
||||
if not found_issues:
|
||||
result.add_pass("Cloudflare DNS documentation is correct")
|
||||
|
||||
|
||||
def check_deployment_architecture_exists(result: ValidationResult):
|
||||
"""Verify DEPLOYMENT_ARCHITECTURE.md exists"""
|
||||
doc_path = REPO_ROOT / "DEPLOYMENT_ARCHITECTURE.md"
|
||||
|
||||
if not doc_path.exists():
|
||||
result.add_error(
|
||||
"DEPLOYMENT_ARCHITECTURE.md",
|
||||
"Critical deployment documentation is missing"
|
||||
)
|
||||
return
|
||||
|
||||
content = doc_path.read_text()
|
||||
|
||||
# Check for key sections
|
||||
required_sections = [
|
||||
"Monorepo vs Satellite Model",
|
||||
"Critical Rules",
|
||||
"NEVER DO THIS",
|
||||
"ALWAYS DO THIS",
|
||||
]
|
||||
|
||||
missing_sections = []
|
||||
for section in required_sections:
|
||||
if section not in content:
|
||||
missing_sections.append(section)
|
||||
|
||||
if missing_sections:
|
||||
result.add_error(
|
||||
"DEPLOYMENT_ARCHITECTURE.md",
|
||||
f"Missing sections: {', '.join(missing_sections)}"
|
||||
)
|
||||
else:
|
||||
result.add_pass("DEPLOYMENT_ARCHITECTURE.md is complete")
|
||||
|
||||
|
||||
def check_readme_warnings(result: ValidationResult):
|
||||
"""Verify README.md has deployment warnings"""
|
||||
readme = REPO_ROOT / "README.md"
|
||||
|
||||
if not readme.exists():
|
||||
result.add_error("README.md", "README.md not found")
|
||||
return
|
||||
|
||||
content = readme.read_text()
|
||||
|
||||
if "DEPLOYMENT WARNING" not in content:
|
||||
result.add_error(
|
||||
"README.md",
|
||||
"Missing deployment warning section"
|
||||
)
|
||||
|
||||
if "DO NOT" not in content or "satellite" not in content.lower():
|
||||
result.add_error(
|
||||
"README.md",
|
||||
"Deployment warnings are not clear or comprehensive"
|
||||
)
|
||||
|
||||
if not result.errors:
|
||||
result.add_pass("README.md has proper deployment warnings")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all validation checks"""
|
||||
print(f"\n{BOLD}{BLUE}BlackRoad OS Deployment Configuration Validator{RESET}")
|
||||
print(f"{BLUE}{'=' * 70}{RESET}\n")
|
||||
|
||||
result = ValidationResult()
|
||||
|
||||
# Run all checks
|
||||
print("Running validation checks...\n")
|
||||
|
||||
check_railway_toml(result)
|
||||
check_env_files(result)
|
||||
check_satellite_configs(result)
|
||||
check_cloudflare_docs(result)
|
||||
check_deployment_architecture_exists(result)
|
||||
check_readme_warnings(result)
|
||||
|
||||
# Print results
|
||||
exit_code = result.print_summary()
|
||||
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user