Skip to content

Overview

Optics Terminal is a Laravel 13 application with a React 19 front end served through Inertia. There is no separate API tier: controllers render Inertia pages directly with typed props.

The components the app runs on, and where each one lives.

architecture-beta
    group azure(cloud)[Microsoft Azure]
    group vm(azure:virtual-machine)[Virtual Machine] in azure

    service app(server)[Laravel app] in vm
    service redis(database)[Redis] in vm
    service worker1(server)[Queue worker 1] in vm
    service worker2(server)[Queue worker 2] in vm

    service db(azure:postgresql)[Azure Database for PostgreSQL] in azure
    service blob(azure:blob-storage)[Azure Blob Storage] in azure
    service mail(azure:communication-services)[Communication Services Email] in azure

    service browser(internet)[Browser]
    service workos(internet)[WorkOS SSO]

    browser:R --> L:app
    workos:R --> L:app
    app:T --> B:db
    app:B --> T:blob
    app:R --> L:redis
    redis:R --> L:worker1
    redis:R --> L:worker2
    worker1:R --> L:mail
    worker2:R --> L:mail

WorkOS is the only external dependency; everything else runs in Azure. Redis backs the queue, cache and session store, and the two workers consume jobs from it.

Layer Technology
Backend Laravel 13, PHP 8.4
Database Icon-databases-131PostgreSQL — Azure Database for PostgreSQL in production
Frontend React 19 + Inertia 3, Mantine UI
Admin Filament 5 at /admin, staff only
Auth WorkOS SSO
Files Icon-storage-86Laravel filesystem abstraction — Azure Blob Storage in production
Mail Azure Communication Services Email (azure mailer); SMTP or log locally
Queue Redis, drained by two workers on the VM
Cache & sessions Redis

All business data is scoped to an Organization. Users belong to one organization, and every model holding business data carries an organization_id. Isolation is enforced at the query level rather than by a package.

sequenceDiagram
    participant B as Browser
    participant R as Route
    participant C as Controller
    participant S as Service
    participant P as pages/{name}.tsx

    B->>R: Inertia visit
    R->>C: single-action controller
    C->>S: business logic
    S-->>C: data
    C->>P: Inertia::render('page-name', $props)
    P-->>B: typed props, client render

Controllers follow the single-action pattern — one class per action, grouped into feature subdirectories under app/Http/Controllers/. Business logic lives in app/Services/.

The core entities and how they hang together. Every model is documented individually in the Entities section.

erDiagram
    ORGANIZATION ||--o{ USER : "has"
    ORGANIZATION ||--o{ PROJECT : "owns"
    ORGANIZATION ||--o{ REPORT : "owns"
    ORGANIZATION ||--o{ INVOICE : "billed"
    ORGANIZATION ||--o{ SUPPORT_REQUEST : "raises"
    ORGANIZATION ||--o{ CONFIGURATION_RESOURCE : "supplies"
    PROJECT ||--o{ REPORT : "produces"
    PROJECT ||--o{ FOCUS_INDIVIDUAL : "profiles"
    PROJECT ||--o{ PROJECT_CITY : "spans"
    PROJECT ||--o{ PROJECT_EXTRACTION : "derives"
    REPORT }o--|| ATTACHMENT : "file"
    ATTACHMENT ||--o| DOCUMENT_TEXT : "extracted text"
    USER ||--o{ AGENT_SESSION : "starts"
    AGENT_SESSION ||--o{ AGENT_RETRIEVAL : "retrieves"
    USER ||--o{ FEEDBACK : "leaves"