# TECHNICAL ARCHITECTURE: Spark Platform
> **BLUF**: Spark uses Astro SSR + React Islands frontend, Directus headless CMS backend, PostgreSQL with PostGIS for data, and Redis-backed BullMQ for async processing. Multi-tenant via site_id foreign keys.
---
## 1. System Diagram
```mermaid
graph TB
subgraph "Client Layer"
Browser[Browser]
PWA[PWA Service Worker]
end
subgraph "Edge Layer"
Traefik[Traefik Reverse Proxy]
end
subgraph "Application Layer"
Frontend["Astro SSR
Port 4321"]
Directus["Directus CMS
Port 8055"]
end
subgraph "Data Layer"
PostgreSQL["PostgreSQL 16
+ PostGIS 3.4"]
Redis["Redis 7
Sessions + Queue"]
end
subgraph "Extension Layer"
Endpoints["Custom Endpoints
/god/*"]
Hooks["Event Hooks
on:create, on:update"]
end
Browser --> Traefik
PWA --> Traefik
Traefik -->|"/*.admin, /api/*"| Frontend
Traefik -->|"/items/*, /collections/*"| Directus
Frontend -->|"REST API"| Directus
Directus --> PostgreSQL
Directus --> Redis
Directus --> Endpoints
Directus --> Hooks
```
---
## 2. Component Specifications
### 2.1 Frontend (Astro)
| Property | Value |
|----------|-------|
| Framework | Astro 4.7 |
| Rendering | SSR (Server-Side Rendering) |
| Hydration | Islands Architecture (partial) |
| UI Library | React 18.3 |
| Styling | Tailwind CSS 3.4 |
| State | React Query + Zustand |
| Build | Vite |
**SSR URL Detection Logic**:
```typescript
const isServer = import.meta.env.SSR || typeof window === 'undefined';
const DIRECTUS_URL = isServer
? 'http://directus:8055' // Docker internal
: import.meta.env.PUBLIC_DIRECTUS_URL; // Public HTTPS
```
**Rationale**: Server-side requests use Docker network DNS. Browser requests use public URL.
### 2.2 Backend (Directus)
| Property | Value |
|----------|-------|
| Version | Directus 11 |
| API | REST + GraphQL |
| Auth | JWT + Static Tokens |
| Storage | Local filesystem (uploads volume) |
| Extensions | Endpoints + Hooks |
**Extension Structure**:
```
directus-extensions/
├── endpoints/
│ ├── god-schema/ # Schema operations
│ ├── god-data/ # Bulk data ops
│ └── god-utils/ # Utility endpoints
└── hooks/
├── work-log/ # Activity logging
└── cache-bust/ # Invalidation
```
### 2.3 Database (PostgreSQL)
| Property | Value |
|----------|-------|
| Version | PostgreSQL 16 |
| Extensions | uuid-ossp, pgcrypto, PostGIS 3.4 |
| Collections | 30+ tables |
| Schema Order | Harris Matrix (Foundation → Walls → Roof) |
**Schema Dependency Order**:
| Batch | Tables | Dependencies |
|-------|--------|--------------|
| 1: Foundation | sites, campaign_masters, avatar_*, geo_*, offer_blocks | None |
| 2: Walls | generated_articles, generation_jobs, pages, posts, leads, headline_*, content_* | Batch 1 only |
| 3: Roof | link_targets, globals, navigation | Batch 1-2 |
### 2.4 Cache/Queue (Redis)
| Property | Value |
|----------|-------|
| Version | Redis 7 |
| Mode | Append-only (persistent) |
| Uses | Session cache, BullMQ backing |
**Queue Configuration**:
```typescript
// BullMQ job options
{
attempts: 3,
backoff: { type: 'exponential', delay: 1000 },
removeOnComplete: 100,
removeOnFail: 1000
}
```
---
## 3. Data Flow
### 3.1 Page Request Flow
```
1. Browser → GET /blog/article-slug
2. Traefik → Route to Frontend (port 4321)
3. Astro SSR → Determine site from domain
4. Astro → GET http://directus:8055/items/posts?filter[slug]=...
5. Directus → PostgreSQL query
6. Directus → Return JSON
7. Astro → Render HTML with React components
8. Astro → Return HTML to browser
9. Browser → Hydrate interactive islands
```
### 3.2 Article Generation Flow
```
1. Admin → POST /api/seo/generate-article
2. Astro API → Create generation_job record
3. Astro API → Queue job in BullMQ
4. BullMQ Worker → Dequeue job
5. Worker → Fetch campaign config from Directus
6. Worker → Compute Cartesian products
7. Worker → For each permutation:
a. Replace tokens
b. Process spintax
c. Generate SEO meta
d. Create generated_articles record
8. Worker → Update job status: completed
9. Admin → Kanban reflects new articles
```
### 3.3 Multi-Tenant Request Isolation
```
1. Request → https://client-a.example.com/page
2. Middleware → Extract hostname
3. Middleware → Query sites WHERE url LIKE %hostname%
4. Middleware → Set site_id in context
5. All queries → Filter by site_id
6. Response → Only tenant data returned
```
---
## 4. API Surface
### 4.1 Public Endpoints
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/api/lead` | POST | Form submission |
| `/api/forms/submit` | POST | Generic form handler |
| `/api/track/pageview` | POST | Analytics |
| `/api/track/event` | POST | Custom events |
| `/api/track/conversion` | POST | Conversion recording |
### 4.2 Admin Endpoints
| Endpoint | Method | Auth | Purpose |
|----------|--------|------|---------|
| `/api/seo/generate-headlines` | POST | Token | Spintax permutation |
| `/api/seo/generate-article` | POST | Token | Article creation |
| `/api/seo/approve-batch` | POST | Token | Bulk approval |
| `/api/seo/publish-article` | POST | Token | Single publish |
| `/api/seo/scan-duplicates` | POST | Token | Duplicate detection |
| `/api/seo/insert-links` | POST | Token | Internal linking |
| `/api/seo/process-queue` | POST | Token | Queue advancement |
| `/api/campaigns` | GET/POST | Token | Campaign CRUD |
| `/api/admin/import-blueprint` | POST | Token | Site import |
| `/api/admin/worklog` | GET | Token | Activity log |
### 4.3 God-Mode Endpoints
| Endpoint | Method | Header | Purpose |
|----------|--------|--------|---------|
| `/god/schema/collections/create` | POST | X-God-Token | Create collection |
| `/god/schema/relations/create` | POST | X-God-Token | Create relation |
| `/god/schema/snapshot` | GET | X-God-Token | Export schema YAML |
| `/god/data/bulk-insert` | POST | X-God-Token | Mass data insert |
---
## 5. Authentication Model
### 5.1 Directus Auth
| Method | Use Case |
|--------|----------|
| JWT | User login sessions |
| Static Token | API integrations |
| God-Mode Token | Administrative operations |
### 5.2 Token Hierarchy
```
┌─────────────────────────────────────┐
│ GOD_MODE_TOKEN │ ← Full schema access
│ X-God-Token header │
└─────────────────────────────────────┘
▼
┌─────────────────────────────────────┐
│ DIRECTUS_ADMIN_TOKEN │ ← All collections CRUD
│ Authorization: Bearer │
└─────────────────────────────────────┘
▼
┌─────────────────────────────────────┐
│ Site-Scoped Token │ ← Single site access
│ Generated per tenant │
└─────────────────────────────────────┘
▼
┌─────────────────────────────────────┐
│ Public Access │ ← Read-only published
│ No token required │
└─────────────────────────────────────┘
```
---
## 6. Security Configuration
### 6.1 CORS Policy
```yaml
CORS_ORIGIN: 'https://spark.jumpstartscaling.com,https://launch.jumpstartscaling.com,http://localhost:4321'
CORS_ENABLED: 'true'
```
### 6.2 Rate Limiting
```yaml
RATE_LIMITER_ENABLED: 'false' # Disabled for internal use
```
### 6.3 Payload Limits
```yaml
MAX_PAYLOAD_SIZE: '500mb' # Large batch operations
```
---
## 7. Deployment Configuration
### 7.1 Docker Compose Services
| Service | Image | Port | Volume |
|---------|-------|------|--------|
| postgresql | postgis/postgis:16-3.4-alpine | 5432 | postgres-data-fresh |
| redis | redis:7-alpine | 6379 | redis-data |
| directus | directus/directus:11 | 8055 | directus-uploads |
| frontend | Built from Dockerfile | 4321 | None |
### 7.2 Environment Variables
| Variable | Purpose | Where Set |
|----------|---------|-----------|
| PUBLIC_DIRECTUS_URL | Client-side API URL | docker-compose.yaml |
| DIRECTUS_ADMIN_TOKEN | SSR API auth | Coolify secrets |
| GOD_MODE_TOKEN | Schema operations | Coolify secrets |
| FORCE_FRESH_INSTALL | Wipe + rebuild schema | Coolify secrets |
| CORS_ORIGIN | Allowed origins | docker-compose.yaml |
### 7.3 Coolify Labels
```yaml
labels:
coolify.managed: 'true'
coolify.name: 'directus'
coolify.fqdn: 'spark.jumpstartscaling.com'
coolify.port: '8055'
```
---
## 8. Extension Points
### 8.1 Adding New Collections
1. Define in `complete_schema.sql` (Harris Matrix order)
2. Add TypeScript interface to `schemas.ts`
3. Create API endpoint if needed
4. Add admin page component
### 8.2 Adding New Blocks
1. Create component in `frontend/src/components/blocks/`
2. Register in `BlockRenderer.tsx` switch statement
3. Add schema to Page Builder config
### 8.3 Adding New Endpoints
1. Create file in `frontend/src/pages/api/`
2. Export async handler function
3. Add to API_REFERENCE.md
### 8.4 Adding Custom Directus Extensions
1. Create in `directus-extensions/endpoints/` or `hooks/`
2. Restart Directus container
3. Extensions auto-load from mounted volume
---
## 9. Performance Considerations
### 9.1 SSR Caching
| Strategy | Implementation |
|----------|----------------|
| ISR | Not used (dynamic content) |
| Edge Cache | Traefik level (CDN potential) |
| API Cache | Redis TTL on queries |
### 9.2 Database Optimization
| Technique | Application |
|------------|-------------|
| Indexes | FK columns, status, slug |
| Pagination | Offset-based with limits |
| Field Selection | Only request needed fields |
### 9.3 Bundle Optimization
| Technique | Implementation |
|-----------|----------------|
| Islands | Only hydrate interactive components |
| Code Splitting | Vite automatic chunks |
| Compression | Brotli via Astro adapter |
---
## 10. Monitoring & Logging
### 10.1 Log Locations
| Service | Location |
|---------|----------|
| Directus | Container stdout (Coolify UI) |
| Frontend | Container stdout (Coolify UI) |
| PostgreSQL | Container stdout |
### 10.2 Health Checks
| Service | Endpoint | Interval |
|---------|----------|----------|
| PostgreSQL | pg_isready | 5s |
| Redis | redis-cli ping | 5s |
| Directus | /server/health | 10s |
### 10.3 Work Log Table
```sql
SELECT * FROM work_log
ORDER BY timestamp DESC
LIMIT 100;
```
Fields: action, entity_type, entity_id, details, level, user, timestamp