NOTE: This architecture document is a conceptual schema and is subject to change based on new requirements, technical constraints, or development needs. The actual implementation may differ from this initial design.
This document provides the architecture diagram and description of the RLUbiodiversity.com Visual Data Platform, a custom-built system that integrates with Amaturalist API to display biodiversity observation data using Three.js for 3D visualization and story maps.
The RLUbiodiversity.com Visual Data Platform is a specialized web application designed to:
graph TB
subgraph USER_LAYER["USER LAYER"]
WEB_USER[Web Browser
RLUbiodiversity.com Visual Data Platform
Three.js + React]
ADMIN_USER[Admin Panel
Content Management
Media Upload]
end
subgraph RLU_FRONTEND["RLU FRONTEND"]
REACT[React Application
Three.js Integration
Story Maps Component]
REDIS_CLIENT[Redis Client
Real-time Subscriptions]
end
subgraph CDN["CDN LAYER"]
CF[Cloudflare CDN
Static Assets
SSL/TLS Termination]
end
subgraph RLU_BACKEND["RLU BACKEND"]
API[Laravel API Routes
Middleware Layer]
REDIS[(Redis Cache
Real-time Data
Admin Session Storage)]
CONTENT_DB[(Content Database
Storymaps
Media Metadata)]
end
subgraph AMATURALIST_API["AMATURALIST API EXTERNAL"]
AUTH[JWT Authentication
API Token Management]
GEN_OBS[General Observations API
/api/general-observations]
end
subgraph AMATURALIST_BACKEND["AMATURALIST BACKEND (Existing)"]
AMATURALIST_DB[(Amaturalist Database
Observations Data)]
S3[(S3 Storage
Media Files)]
end
USER_LAYER -->|HTTPS| CDN
CDN --> REACT
REACT --> API
API -->|API Token| AUTH
AUTH --> GEN_OBS
GEN_OBS --> AMATURALIST_DB
AMATURALIST_DB --> S3
API --> REDIS
REDIS --> REACT
ADMIN_USER --> API
API --> CONTENT_DB
style USER_LAYER fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style RLU_FRONTEND fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style RLU_BACKEND fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style AMATURALIST_API fill:#ca8a04,stroke:#ea580c,stroke-width:2px,color:#ffffff
style AMATURALIST_BACKEND fill:#ca8a04,stroke:#ea580c,stroke-width:2px,color:#ffffff
sequenceDiagram
participant User
participant React
participant Redis
participant API
participant AmaturalistAPI
participant ContentDB
User->>React: Request Observation Data
React->>Redis: Check Cache (observation:{id})
alt Cache Hit
Redis-->>React: Return Cached Data
React-->>User: Display Data
else Cache Miss
React->>API: GET /api/observations
API->>AmaturalistAPI: Request with API Token
AmaturalistAPI-->>API: Return Observation Data
API->>Redis: Cache Data (TTL: 5min)
API-->>React: Return Data
React-->>User: Display Data
end
User->>React: Request Story Map Content
React->>API: GET /api/storymaps/{id}
API->>ContentDB: Query Storymap
ContentDB-->>API: Return Storymap Content
API-->>React: Return Content
React-->>User: Render Story Map
graph LR
subgraph AMATURALIST["AMATURALIST"]
WEBHOOK[Webhook Events
New Observation]
end
subgraph RLU_BACKEND["RLU BACKEND"]
WEBHOOK_HANDLER[Webhook Handler]
REDIS_PUB[Redis Publisher]
end
subgraph REDIS["REDIS"]
CHANNEL[Pub/Sub Channel
observations:updates]
end
subgraph RLU_FRONTEND["RLU FRONTEND"]
SOCKET[Socket.io Client]
REACT_STATE[React State Update]
end
WEBHOOK --> WEBHOOK_HANDLER
WEBHOOK_HANDLER --> REDIS_PUB
REDIS_PUB --> CHANNEL
CHANNEL --> SOCKET
SOCKET --> REACT_STATE
style AMATURALIST fill:#ca8a04,stroke:#ea580c,stroke-width:2px,color:#ffffff
style RLU_BACKEND fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style RLU_FRONTEND fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
/api/general-observations
/api/taxa/{id}
Observation Data Response:
{
"id": 12345,
"scientific_name": "Milvus migrans",
"common_name": "Black Kite",
"latitude": -6.2088,
"longitude": 106.8456,
"observation_date": "2026-05-01T10:00:00Z",
"images": [
{
"url": "https://s3.amazonaws.com/...",
"type": "image/jpeg"
}
],
"audio": {
"url": "https://s3.amazonaws.com/...",
"spectrogram": "https://s3.amazonaws.com/..."
},
"quality": {
"grade": "Research Grade",
"identifications_count": 5
},
"location": "Jakarta, Indonesia"
}
graph TB
subgraph EXTERNAL["EXTERNAL THREATS"]
ATTACKER[Attacker]
end
subgraph SECURITY_LAYERS["SECURITY LAYERS"]
L1[Layer 1: Cloudflare WAF
DDoS Protection
Rate Limiting]
L2[Layer 2: CDN SSL/TLS
TLS 1.3 Only
HSTS Enabled]
L3[Layer 3: API Gateway
Request Validation
Rate Limiting per IP]
L4[Layer 4: Authentication
JWT Token Validation
Role-based Access]
L5[Layer 5: Application Security
Input Validation
SQL Injection Prevention]
L6[Layer 6: Data Security
Encryption at Rest
Redis AUTH]
end
subgraph INTERNAL["INTERNAL ASSETS"]
APP[RLUbiodiversity.com Application]
DB[(Databases)]
REDIS[(Redis)]
end
ATTACKER --> L1
L1 --> L2
L2 --> L3
L3 --> L4
L4 --> L5
L5 --> L6
L6 --> APP
APP --> DB
APP --> REDIS
style SECURITY_LAYERS fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style INTERNAL fill:#059669,stroke:#16a34a,stroke-width:2px,color:#ffffff
Operating System:
VPS Configuration (Exabytes):
Security Tools:
Cloudflare Configuration:
graph LR
ATTACKER[Attacker]
WAF[WAF]
DDOS[DDoS]
DNS[DNS]
FIREWALL[Firewall]
SSH[SSH Port]
HTTP[HTTP]
HTTPS[HTTPS]
FAIL2BAN[Fail2ban]
SYSTEM[System]
LOGS[Audit Logs]
ATTACKER --> WAF
ATTACKER --> DDOS
ATTACKER --> DNS
DNS --> FIREWALL
FIREWALL --> SSH
FIREWALL --> HTTP
FIREWALL --> HTTPS
SSH --> FAIL2BAN
HTTP --> FAIL2BAN
FAIL2BAN --> SYSTEM
SYSTEM --> LOGS
style ATTACKER fill:#dc2626,stroke:#b91c1c,stroke-width:2px,color:#ffffff
style WAF fill:#059669,stroke:#16a34a,stroke-width:2px,color:#ffffff
style DDOS fill:#059669,stroke:#16a34a,stroke-width:2px,color:#ffffff
style DNS fill:#059669,stroke:#16a34a,stroke-width:2px,color:#ffffff
style FIREWALL fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style FAIL2BAN fill:#ca8a04,stroke:#ea580c,stroke-width:2px,color:#ffffff
style SYSTEM fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style LOGS fill:#7c3aed,stroke:#8b5cf6,stroke-width:2px,color:#ffffff
Authentication Log Example:
2026-05-02 14:30:45 api.amaturalist.com sshd[1234]: Accepted publickey for admin from 192.168.1.100 port 54321 ssh2
2026-05-02 14:31:22 api.amaturalist.com sshd[1235]: Invalid user test from 203.0.113.50 port 22
2026-05-02 14:31:23 api.amaturalist.com sshd[1235]: Failed password for invalid user test from 203.0.113.50 port 22 ssh2
2026-05-02 14:31:24 api.amaturalist.com fail2ban.actions[5678]: NOTICE [sshd] Ban 203.0.113.50
System Log Example:
2026-05-02 14:30:00 api.amaturalist.com kernel: [UFW BLOCK] IN=eth0 SRC=203.0.113.50 DST=10.0.0.5 PROTO=TCP DPT=22
2026-05-02 14:30:01 api.amaturalist.com systemd[1]: Started Session 1234 of user admin
2026-05-02 14:30:02 api.amaturalist.com systemd[1]: Starting Daily apt download activities...
2026-05-02 14:30:10 api.amaturalist.com apt[9012]: Unattended upgrade: linux-image-generic installed
Application Log Example:
[2026-05-02 14:30:15] production.INFO: User admin logged in via access token
[2026-05-02 14:30:20] production.INFO: API request /api/general-observations from 192.168.1.100
[2026-05-02 14:30:25] production.WARNING: Rate limit exceeded for IP 203.0.113.50
[2026-05-02 14:30:30] production.ERROR: Database connection failed - retrying
Attack Prevention Flow:
Final Outcome:
Failed Attack Notifications:
When an attack is blocked at any security layer, the following notifications are sent to responsible personnel:
Notification Channels:
Successful Breach Response (ASAP):
If an attacker successfully bypasses all security layers and gains unauthorized access:
Immediate Actions (within 5 minutes):
Containment (within 15 minutes):
Investigation (within 1 hour):
Recovery (within 24 hours):
Communication:
graph TB
subgraph CACHE_TYPES["CACHE TYPES"]
OBS_CACHE["Observations Cache
Key: obs:id
TTL: 5 minutes"]
TAXA_CACHE["Taxa Cache
Key: taxa:id
TTL: 10 minutes"]
TILE_CACHE["Map Tiles Cache
Key: tile:z:x:y
TTL: 1 hour"]
STORY_CACHE["Story Maps Cache
Key: story:id
TTL: 15 minutes"]
SESSION_CACHE["Session Cache
Key: session:token
TTL: 24 hours"]
end
subgraph INVALIDATION["CACHE INVALIDATION"]
OBS_INVALID["Observation Updates
Invalidate obs:id"]
TAXA_INVALID["Taxa Updates
Invalidate taxa:id"]
MANUAL_INVALID["Manual Flush
Admin Triggered"]
end
OBS_CACHE --> OBS_INVALID
TAXA_CACHE --> TAXA_INVALID
STORY_CACHE --> MANUAL_INVALID
TILE_CACHE --> MANUAL_INVALID
SESSION_CACHE --> MANUAL_INVALID
style CACHE_TYPES fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style INVALIDATION fill:#ca8a04,stroke:#ea580c,stroke-width:2px,color:#ffffff
obs:{id}
obs:list:{page}:{filters_hash}
taxa:{id}
taxa:search:{query}
tile:{z}:{x}:{y}
story:{id}
story:list
session:{token}
graph TB
subgraph ADMIN_PANEL["ADMIN PANEL"]
DASHBOARD[Dashboard
Statistics Overview]
CONTENT_MGMT[Content Management
Story Maps
Media Upload]
USER_MGMT[User Management
Admin List
Permissions]
CACHE_MGMT[Cache Management
Manual Flush
Monitoring]
SETTINGS[Settings
API Configuration
System Config]
end
subgraph ADMIN_FEATURES["ADMIN FEATURES"]
STORY_EDITOR[Story Map Editor
Rich Text Editor
Media Embed]
MEDIA_UPLOAD[Media Upload
Image/Audio Upload
S3 Integration]
ADMIN_LOGS[Admin Activity Logs
Audit Trail]
MONITORING[System Monitoring
Performance Metrics]
end
ADMIN_PANEL --> ADMIN_FEATURES
style ADMIN_PANEL fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style ADMIN_FEATURES fill:#059669,stroke:#16a34a,stroke-width:2px,color:#ffffff
Super Admin
Content Admin
Admin panel uses token-based authentication:
graph LR
subgraph DATA_SOURCE["DATA SOURCE"]
API_DATA[API Response
Observations]
GEO_DATA[Geospatial Data
Coordinates]
MEDIA_DATA[Media Files
Images/Audio]
end
subgraph PREPROCESSING["PREPROCESSING"]
DATA_TRANSFORM[Data Transformation
Coordinate Conversion]
LOD_GENERATION[LOD Generation
Level of Detail]
TEXTURE_COMP[Texture Compression
Optimization]
end
subgraph THREE_JS["THREE.JS RENDERING"]
SCENE[Scene Setup
Camera/Lights]
GEOMETRY[Geometry Creation
Points/Meshes]
MATERIALS[Materials
Textures/Shaders]
ANIMATION[Animation
Transitions]
end
subgraph RENDER["RENDER"]
WEBGL_RENDERER[WebGL Renderer
Post-processing]
CANVAS[Canvas Output
Display]
end
DATA_SOURCE --> PREPROCESSING
PREPROCESSING --> THREE_JS
THREE_JS --> RENDER
style DATA_SOURCE fill:#ca8a04,stroke:#ea580c,stroke-width:2px,color:#ffffff
style PREPROCESSING fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style THREE_JS fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style RENDER fill:#059669,stroke:#16a34a,stroke-width:2px,color:#ffffff
graph TB
subgraph PRODUCTION["PRODUCTION ENVIRONMENT"]
subgraph FRONTEND["FRONTEND"]
EXABYTES_WEB[Exabytes Web Hosting
Static Files
React Build]
end
subgraph BACKEND["BACKEND"]
LARAVEL[Laravel Application
API Endpoints
Business Logic]
MYSQL[(MySQL/PostgreSQL
Database)]
REDIS[(Redis
Cache)]
end
subgraph STORAGE["STORAGE"]
LOCAL[(Local Storage
Media Files)]
CLOUDFRONT[Cloudflare
CDN]
end
end
subgraph MONITORING["MONITORING & LOGGING"]
UPTIME[Uptime Robot
Uptime Monitoring
Alerts]
LOGS[Application Logs
Laravel Log Viewer]
METRICS[Performance Metrics
Custom Dashboard]
end
FRONTEND --> BACKEND
BACKEND --> MYSQL
BACKEND --> REDIS
BACKEND --> LOCAL
LOCAL --> CLOUDFRONT
FRONTEND --> CLOUDFRONT
BACKEND --> MONITORING
FRONTEND --> MONITORING
style PRODUCTION fill:#0066cc,stroke:#0088ff,stroke-width:2px,color:#ffffff
style MONITORING fill:#ca8a04,stroke:#ea580c,stroke-width:2px,color:#ffffff
Development
Staging
Production
This architecture is designed to support TISAX (Trusted Information Security Assessment Exchange) requirements: