System Architecture - Michael J Wright Digital Archive
Audience: Technical (primary) · Library integrators (secondary)
Use this doc when you need to: understand how the domains, Workers, Pages, and Fedora repo fit together (and which URL is intended for which use).
Related:
- Integrations: Library Access Guide
- Security posture: Security Architecture Overview
DNS & Routing Configuration
Production URLs
| URL | Service | Purpose |
|---|---|---|
https://michaeljwright.com.au |
Cloudflare Pages (mjw-project-prod) |
Main project website |
https://data.michaeljwright.com.au |
Cloudflare Pages (fedora-frontend) |
Public archive frontend/catalog |
https://api.michaeljwright.com.au |
Cloudflare Worker (mjw-archive-api) |
Authenticated API to Fedora |
https://dev.michaeljwright.com.au |
Cloudflare Pages (mjwright-dev) |
Development/staging environment |
URL Patterns for Public Use
For Browsing (Public):
- Frontend:
https://data.michaeljwright.com.au/ - Browse collections, view metadata, see low-res previews
For API Access (Programmatic):
- API Base:
https://api.michaeljwright.com.au/ - Direct resource access:
https://api.michaeljwright.com.au/fcrepo/rest/{path} - Example:
https://api.michaeljwright.com.au/fcrepo/rest/paintings/coastal-studies/MJW-P-1987-042
For Curator/Admin (Authenticated):
- Local development:
http://localhost:8080/fcrepo/rest/ - Credentials:
curator1/ password from.env - Use for: Creating containers, uploading binaries, editing metadata
Architecture Diagram
┌─────────────────────────────────────────────────────────────────┐
│ Public Internet │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┬──────────────┬──────────────┐
│ Main Site │ Frontend │ API │
│ (Pages) │ (Pages) │ (Worker) │
│ michaeljw... │ data.mich... │ api.data... │
└──────────────┴──────────────┴──────────────┘
│
│ Proxies with
│ auto-auth
▼
┌──────────────┐
│ Tunnel │
│ fcrepo.mich..│
└──────────────┘
│
│ HTTPS
▼
┌───────────────────────────────────────────────────────────────────┐
│ Local Docker Environment │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ mjw-fedora │◄─────│ mjw-db │ │ mjw-grafana │ │
│ │ (Fedora 6) │ │ (PostgreSQL) │ │ (Monitoring) │ │
│ │ :8080 │ │ :5432 │ │ :3000 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ▲ ▲ │
│ │ │ │
│ │ ┌──────────────┐ │
│ └──────────────────────────────│ mjw-prometheus │
│ Scrapes metrics │ :9090 │ │
│ └──────────────┘ │
│ │
│ Volumes: │
│ • fcrepo_data (repository storage) │
│ • postgres_data (database) │
└───────────────────────────────────────────────────────────────────┘
Component Details
1. Cloudflare Worker (mjw-archive-api)
Location: src/index.js
Deployed to: api.michaeljwright.com.au
Configuration: wrangler.toml
Responsibilities:
- Proxies all requests to Fedora backend via Tunnel
- Automatically injects
FEDORA_BASIC_AUTHcredentials from Cloudflare Secrets - Adds CORS headers for cross-origin requests
- Provides security headers (HSTS, CSP, etc.)
- Routes:
api.michaeljwright.com.au/*→fcrepo.michaeljwright.com.au/*
Secrets (set via wrangler secret put):
FEDORA_BASIC_AUTH: Base64-encoded{serviceUser}:{password}for automatic authentication (use a dedicated non-admin/service account)
Example API Call:
# Get repository root
curl https://api.michaeljwright.com.au/fcrepo/rest/ \
-H "Accept: application/ld+json"
# Get specific resource
curl https://api.michaeljwright.com.au/fcrepo/rest/paintings/coastal-studies/MJW-P-1987-042 \
-H "Accept: application/ld+json"
2. Cloudflare Pages (fedora-frontend)
Deployed to: data.michaeljwright.com.au
Source: pages/ directory
Build: Static HTML/CSS/JS
Responsibilities:
- Public-facing catalog interface
- Browse collections by type/series
- Display metadata and web preview images
- Link to licensing portal for full-resolution access
- Calls Worker API for data:
api.michaeljwright.com.au/fcrepo/rest/
Deployment:
# From repository root
wrangler pages deploy pages --project-name fedora-frontend
3. Cloudflare Tunnel
Tunnel ID: 912ded76-65b7-402a-8ea8-76592a651160
Public Hostname: fcrepo.michaeljwright.com.au
Target: localhost:8080 (mjw-fedora container)
Responsibilities:
- Securely exposes local Fedora repository to internet
- No inbound firewall rules required
- Worker uses this endpoint as backend
Configuration: Managed via Cloudflare Zero Trust dashboard
4. Docker Containers
mjw-fedora (Fedora 6 Repository)
- Image:
fcrepo/fcrepo:6-tomcat9 - Port:
8080 - Data:
/datavolume (fcrepo_data) - Config:
fcrepo.properties,scripts/init-tomcat-users.sh - Database: Connects to
mjw-db:5432 - Users: AdminRob (fedoraAdmin), curator1 (fedoraUser)
mjw-db (PostgreSQL)
- Image:
postgres:12.3 - Port:
5432 - Data:
postgres_datavolume - Database:
fcrepo - User:
fcrepo-user/fcrepo-pw
mjw-prometheus (Metrics Collection)
- Image:
prom/prometheus - Port:
9090 - Config:
prometheus.yml - Scrapes: Fedora JMX metrics (if exposed)
mjw-grafana (Monitoring Dashboards)
- Image:
grafana/grafana - Port:
3000 - Dashboards: Repository health, storage, performance
Deployment Workflow
Initial Setup (Done)
- ✅ Configure DNS records in Cloudflare
- ✅ Set up Cloudflare Tunnel to local environment
- ✅ Deploy Cloudflare Worker for API proxy
- ✅ Deploy Cloudflare Pages for frontend
- ✅ Start Docker containers locally
Ongoing Curator Workflow
Local Development:
- Work at
http://localhost:8080/fcrepo/rest/ - Create containers, upload binaries, edit metadata
- Test changes before they go live
- Work at
Automatic Sync:
- Cloudflare Tunnel keeps
fcrepo.michaeljwright.com.auin sync with localhost - Changes appear immediately at public API endpoint
- Frontend fetches updated data from API
- Cloudflare Tunnel keeps
Frontend Updates:
- Edit files in
pages/directory - Deploy:
wrangler pages deploy pages --project-name fedora-frontend - Live at
data.michaeljwright.com.auin ~30 seconds
- Edit files in
Worker Updates
# Deploy updated Worker code
wrangler deploy
# Update secrets
wrangler secret put FEDORA_BASIC_AUTH
# Enter: Base64 of "AdminRob:your-strong-password"
Security Model
Authentication Layers
Public Users (read-only):
- Access via:
data.michaeljwright.com.au(frontend) - No authentication required
- See: Low-res previews, metadata
- Cannot: Create, edit, delete resources
API Consumers (programmatic):
- Access via:
api.michaeljwright.com.au - Authentication: Automatic via Worker's
FEDORA_BASIC_AUTHsecret - Can: Read all public resources
- Cannot: Modify without curator credentials
Curators (read/write):
- Access via:
http://localhost:8080/fcrepo/rest/ - Authentication: HTTP Basic Auth
curator1:password - Can: Create containers, upload files, edit metadata
- Role:
fedoraUserin Tomcat
Administrators (full control):
- Access via:
http://localhost:8080/fcrepo/rest/ - Authentication: HTTP Basic Auth
AdminRob:strong-password - Can: All curator actions + system configuration
- Role:
fedoraAdminin Tomcat
Network Security
- Tunnel: Encrypted connection; no exposed ports to internet
- Worker: HTTPS only; HSTS enforced
- CORS: Controlled via Worker headers
- Secrets: Stored in Cloudflare (not in code)
- Database: Accessible only within Docker network
DNS Configuration Summary
Current (Optimal) Setup
# Main site
michaeljwright.com.au CNAME mjw-project-prod.pages.dev
# Archive frontend
data.michaeljwright.com.au CNAME fedora-frontend.pages.dev
# API endpoint (Worker)
api.michaeljwright.com.au CNAME 912ded76-65b7-402a-8ea8-76592a651160.cfargotunnel.com
# Direct Fedora access (Tunnel)
fcrepo.michaeljwright.com.au CNAME 912ded76-65b7-402a-8ea8-76592a651160.cfargotunnel.com
# Development
dev.michaeljwright.com.au CNAME mjwright-dev.pages.dev
Notes:
- All records set to "Proxied" (orange cloud) in Cloudflare
- Tunnel CNAMEs point to same tunnel ID
- Pages CNAMEs point to respective Pages projects
- Auto TTL managed by Cloudflare
Container Management
Start/Stop Services
# Start all containers
docker-compose up -d
# Stop all containers
docker-compose down
# Restart specific service
docker restart mjw-fedora
# View logs
docker logs mjw-fedora --tail 100 -f
Verify Health
# Check containers
docker ps
# Test Fedora locally
curl http://localhost:8080/fcrepo/rest/ \
-u curator1:password \
-H "Accept: application/ld+json"
# Test via Tunnel
curl https://fcrepo.michaeljwright.com.au/fcrepo/rest/ \
-u AdminRob:password \
-H "Accept: application/ld+json"
# Test via Worker API
curl https://api.michaeljwright.com.au/fcrepo/rest/ \
-H "Accept: application/ld+json"
Troubleshooting
Issue: Fedora returns 404
Cause: Database wasn't ready when Fedora started
Solution: docker restart mjw-fedora
Issue: Worker authentication fails
Cause: FEDORA_BASIC_AUTH secret not set
Solution:
# Generate Base64 auth header
echo -n "AdminRob:your-password" | base64
# Set secret
wrangler secret put FEDORA_BASIC_AUTH
# Paste the Base64 value
Issue: Tunnel not routing
Cause: Tunnel service not running or DNS not updated
Solution: Check Cloudflare Zero Trust dashboard; verify tunnel status
Issue: CORS errors on frontend
Cause: Worker not adding CORS headers
Solution: Verify Worker deployed; check src/index.js CORS configuration
Monitoring
Public Health Dashboard
URL: https://api.michaeljwright.com.au/dashboard
A public, no-authentication-required dashboard providing real-time system status:
| Panel | Metrics |
|---|---|
| Containers | Status, uptime, CPU %, memory % for each Docker container |
| Service Response Times | HTTP latency to Fedora REST, Prometheus, Grafana (color-coded) |
| Disk Usage | Percentage used, free space, total capacity |
| Memory Available | System RAM availability |
| Issues | Active warnings/critical alerts |
| System Logs | Health check, backup, cleanup, security logs |
The dashboard auto-refreshes every 30 seconds.
API Endpoints (Public)
| Endpoint | Description |
|---|---|
GET /api/v1/health |
JSON health status with container stats, response times, resource usage |
GET /api/v1/logs/{type} |
System logs (health-check, backup, cleanup, security) |
Internal Dashboards
- Prometheus:
http://localhost:9090(metrics database) - Grafana:
http://localhost:3000(visual dashboards, requires SSH tunnel) - Cloudflare Analytics: Cloudflare dashboard → Analytics
- Worker Logs: Cloudflare dashboard → Workers →
mjw-archive-api→ Logs
Key Metrics
- Fedora response times
- Database connection pool
- Storage volume usage
- Worker request counts
- API error rates
Backup & Recovery
Automated Backups (Recommended)
Database:
# Backup PostgreSQL
docker exec mjw-db pg_dump -U fcrepo-user fcrepo > backup-$(date +%Y%m%d).sql
# Restore
docker exec -i mjw-db psql -U fcrepo-user fcrepo < backup-20251103.sql
Fedora Data:
# Backup volume
docker run --rm -v fcrepo_data:/data -v $(pwd):/backup alpine \
tar czf /backup/fcrepo-data-$(date +%Y%m%d).tar.gz /data
# Restore
docker run --rm -v fcrepo_data:/data -v $(pwd):/backup alpine \
tar xzf /backup/fcrepo-data-20251103.tar.gz -C /
Configuration:
- Commit
.env(without passwords) to git - Store passwords in password manager
- Keep
wrangler.tomlin git - Document secrets separately
Future Enhancements
Planned
- IIIF Image API server for pan/zoom
- OAI-PMH endpoint for harvesting
- Full-text search via Elasticsearch
- Automated nightly backups to S3/Azure
Under Consideration
- Cloudflare Zero Trust for curator authentication
- Multi-user curator accounts with audit logs
- Automated metadata validation pipeline
- Bulk import tools with CSV upload