Analytics Exports & FORJD exports Object Store

Reading Progress82%

Appendix O: Analytics Exports & FORJD exports Object Store

Status: FORJD-owned export jobs + object store; DEML BFF adapts stable /api/v1/exports paths; Analytics UI panel. Local DEML ExportJob model/worker is retired (0053). Object store: FORJD exports (Apache 2.0, S3-compatible) Ops: docs/FORJD_INTEGRATION.md

Why FORJD exports (not MinIO, not a lakehouse)

Option Decision
MinIO CE Avoid — upstream community repo archived / unmaintained (2026)
Garage Solid Rust alternative; we chose FORJD exports for S3-shaped DX + Apache 2.0
Delta / Iceberg / Unity Not for downloads — lake table formats, not tenant PDF storage
S3 / R2 Valid managed fallback; same client path-style
FORJD exports Selected S3-shaped store for report blobs

Facts vs files

  • FORJD analytics + Postgres = analytics truth (BI queries, CES, telemetry aggregates).
  • FORJD exports = durable files (PDF / CSV / Parquet / JSON) after a FORJD job runs.
  • FORJD projections = live UI reads via the Django BFF — not an archive for multi‑MB reports.

Architecture

  Angular ──POST /api/v1/exports/──► deml-backend (Django BFF)
                                          │ tenant-bound fjsvc_
                                          ▼
                              FORJD POST /api/v1/exports (durable job)
                               render + put_bytes → FORJD object store
                                          │
  Angular ◄── download URL (TTL) ──────── deml-backend ◄── FORJD GET …/download

Object key layout

tenants/{forjd_tenant_id}/exports/{job_id}/{filename}

(DEML may still log account_id for audit; object isolation is FORJD-tenant-scoped.)

Client

Surface Module
BFF backend/forjd/ adapters for /api/v1/exports
Config FORJD_API_URL, export-related FORJD settings / secret refs
UI Angular Analytics exports panel → Django only (never FORJD directly)

Local development

Use FORJD export APIs (no local object-store broker required)
# S3 API   http://localhost:9100
# Console  http://localhost:9101  (dev keys from compose — not for prod)

Backend / workers (compose) receive:

FORJD_API_URL=https://backend.forjd.co
FORJD_EXPORT_ACCESS_KEY=…
FORJD_EXPORT_SECRET_KEY=…
FORJD_EXPORT_BUCKET=deml-exports
FORJD_EXPORT_REGION=us-east-1
FORJD_EXPORT_USE_SSL=false

Host processes outside compose use FORJD_API_URL=https://backend.forjd.co.

Create the bucket once (console or AWS CLI):

export AWS_ACCESS_KEY_ID=FORJD exports-dev
export AWS_SECRET_ACCESS_KEY=FORJD exports-dev-secret-change-me
aws --endpoint-url http://localhost:9100 s3 mb s3://deml-exports

Or from Django shell after ensure_bucket() when credentials are set.

FORJD export object storage

Service name: FORJD exports

  1. Deploy from docs/FORJD_INTEGRATION.md (pinned FORJD exports image).

  2. Attach a volume at /data.

  3. Set strong FORJD_EXPORT_ACCESS_KEY / FORJD_EXPORT_SECRET_KEY.

  4. Point backend + workers:

    FORJD_API_URL=https://backend.forjd.co
    FORJD_EXPORT_BUCKET=deml-exports
    FORJD_EXPORT_USE_SSL=false
    
  5. Keep console disabled publicly (FORJD_EXPORT_CONSOLE_ENABLE=false default in catalog).

Security invariants

  • Owner/RBAC checks on every export create/list/download (app layer).
  • Stable DEML paths (/api/v1/exports…) authenticate the Firebase session, bind the FORJD tenant, and adapt to FORJD export APIs. Download responses expose a short-lived private object-storage URL from FORJD—not a Railway-hosted DEML download worker and not root credentials in the browser.
  • No frontend access to FORJD or object-store root keys.
  • Prefix / tenant isolation by mapped FORJD tenant (and DEML account_id for audit).
  • Export create/list/download honor DEML RBAC and FORJD tenant binding; quotas and job durability are enforced on the FORJD side.
  • Export generation must not run during account deletion. Deletion stops new FORJD calls and preserves all DEML/Firebase state until FORJD confirms durable tenant erasure.

Implemented surfaces

Piece Location
Owner FORJD export jobs + object store (local DEML ExportJob / ReportArchive retired in 0053)
API DEML GET/POST /api/v1/exports[/id][/download] → FORJD /api/v1/exports… via backend/forjd/
Formats CSV / JSON / Parquet / PDF as exposed by FORJD export APIs
UI Analytics page exports panel (Angular → Django BFF only)

Analytics, status uptime, and report downloads read FORJD projections and export APIs through the BFF. DEML does not materialize AggregatedAnalytics, StatusPageUptimeDaily, or LighthouseScan as live product tables. KPI cards and public status graphs consume FORJD analytics/status responses; missing windows surface as explicit no_data rather than fabricated 100% uptime.

Risk note

FORJD export storage is S3-compatible. Pin image tags, smoke-test presign after upgrades, and keep the client endpoint-abstract so a future Garage/R2 swap is env-only.