Analytics Exports & FORJD exports Object Store
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
Deploy from
docs/FORJD_INTEGRATION.md(pinned FORJD exports image).Attach a volume at
/data.Set strong
FORJD_EXPORT_ACCESS_KEY/FORJD_EXPORT_SECRET_KEY.Point backend + workers:
FORJD_API_URL=https://backend.forjd.co FORJD_EXPORT_BUCKET=deml-exports FORJD_EXPORT_USE_SSL=falseKeep console disabled publicly (
FORJD_EXPORT_CONSOLE_ENABLE=falsedefault 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_idfor 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.