The BENED Ecosystem

Every project built on the same foundation: databases, interfaces, API calls. Here's how they all connect.

Active Domains

Domain Purpose Stack
praxis.bened.works This site β€” architectural teaching and documentation PHP, PostgreSQL
devtradecraft.bened.works TradeCraft development β€” algorithmic trading platform PHP, TimescaleDB, Keycloak
tradecraft.bened.works TradeCraft production Same stack, same database, different code path
stagingtrailers.bened.works Trailer rental platform (staging) Next.js, PostgreSQL, Stripe Connect
trailers.bened.works Trailer rental platform (production) Next.js, PostgreSQL, Stripe Connect
api.bened.works Platform API β€” identity verification spine Express, TypeScript, PostgreSQL
auth.bened.works Keycloak SSO β€” one login for everything Keycloak, Docker
finance.beneficialeducationworks.com Finance tracker β€” personal accounting with AI Next.js, Python API, OpenAI

Data Flow Patterns

Pattern 1: PHP Direct (TradeCraft, Praxis)

Browser ──GET /strategies──▢ Nginx ──▢ PHP-FPM (host)
                                           β”‚
                                           β–Ό
                                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                   β”‚  TimescaleDB  β”‚
                                   β”‚    :5433      β”‚
                                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                           β”‚
                                           β–Ό
                              PHP renders full HTML page
                                           β”‚
                                           β–Ό
Browser ◀───────────────────────── Complete HTML response

Why: Simple, fast, no build step. Direct database queries.
Works great for content-heavy sites and admin panels.

Pattern 2: Docker + Next.js (Rental Platform, Finance Tracker)

Browser ──GET /listings──▢ Nginx ──proxy_pass──▢ Docker Container
                                                      β”‚
                                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”
                                              β”‚   Next.js     β”‚
                                              β”‚   Server      β”‚
                                              β”‚  (Node.js)    β”‚
                                              β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                                      β”‚
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                    β”‚                 β”‚                 β”‚
                                    β–Ό                 β–Ό                 β–Ό
                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                             β”‚ PostgreSQLβ”‚    β”‚  Stripe   β”‚    β”‚    B2     β”‚
                             β”‚   :5432   β”‚    β”‚   API     β”‚    β”‚  Storage  β”‚
                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Response: Server-rendered HTML + React hydration for interactivity

Why this pattern: Complex UI interactions, real-time updates, component reuse. Stripe integration works better with React.

Pattern 3: TimescaleDB + Heavy Queries (TradeCraft)

Browser ──GET /backtest──▢ Nginx ──▢ PHP
                                       β”‚
                                       β–Ό
                               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                               β”‚  TimescaleDB  β”‚
                               β”‚    :5433      β”‚
                               β”‚               β”‚
                               β”‚  100M+ rows   β”‚
                               β”‚  Hypertables  │◀── Automatic time partitioning
                               β”‚  Compression  │◀── 90%+ storage savings
                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                       β”‚
        Query: SELECT * FROM candles 
               WHERE symbol = 'AAPL'
               AND timestamp BETWEEN '2024-01-01' AND '2024-12-31'
               
        Result: Sub-second response on millions of rows

Why TimescaleDB: Stock candles are time-series data. MySQL would choke on 100M rows with time-range queries. TimescaleDB partitions by time automatically.

Shared Infrastructure

Service

/var/www/private/.env

Single source of truth for all secrets. Every app reads from here. Never committed to git.

Service

Nginx

Routes traffic based on domain/path. Terminates SSL. Proxies to PHP-FPM or Docker containers.

Service

Keycloak SSO

Centralizes authentication. Users log in once, access all BENED apps. OAuth2/OIDC protocol.

External

Stripe

Payments for rental platform (and eventually TradeCraft subscriptions). Webhooks notify your app of events.

Repository Map

Repository Language Database Deployment
benedllc/bened-tradecraft PHP TimescaleDB (5433) Direct to VPS
benedllc/bened-rental TypeScript/Next.js PostgreSQL (5432) Docker container
benedllc/bened-main-site TBD TBD TBD
benedllc/bened-platform TBD TBD TBD
Case Portal (local) PHP MySQL (3306) Direct to VPS
Praxis (local) PHP MySQL (3306) Direct to VPS

Key Architectural Decisions

Decision: PHP for content, Next.js for SaaS

PHP apps (case portal, praxis, tradecraft) are content-focused with forms. Next.js (rental) needs complex UI interactions and real-time updates. Use the right tool for the job.

Decision: One .env file for all secrets

Every app reads from /var/www/private/.env. This means one place to update credentials, one place to secure, one pattern to understand.

Decision: Keycloak for SSO

Rather than building auth into each app, centralize it. Users get one login. Apps get proven security. You don't reinvent the wheel.

Decision: TimescaleDB for time-series

TradeCraft deals with millions of stock candles. TimescaleDB handles this natively. MySQL would require manual partitioning and still be slower.

See all architectural decisions β†’