DreamPro Citizen Science Platform — Deep Feature Reference
The DreamPro Citizen Science Platform is the public-facing community layer built on top of the unified DreamPro/Open Energy templates model. It is the active development frontier of HiveJournal — replication science as a worldwide participatory effort. This doc is the technical reference for everything that lives under
/dreampro/*(excluding the private/dashboard/dreampro/*task surfaces). For the original Open Energy 10-phase pathway, see open-energy.md.
What it is
A worldwide community of citizen scientists, family teams, K-12 classrooms, and meetup groups replicating Open Energy experiments together. The platform is a directory and coordination layer — HiveJournal connects parties (sponsors ↔ scientists, teachers ↔ classroom-safe templates, organizers ↔ attendees, builders ↔ video viewers) but never processes payments and never stores personally identifying minor data.
Eight phases have shipped. Phase 7 (cron) and Phase 8b (channel landing) remain.
Phase map
| Phase | Surface | Status |
|---|---|---|
| 1 | Citizen Scientist World Map (/dreampro/map) + opt-in | ✅ |
| 2 | Open Energy Competition leaderboard (/dreampro/competition/[slug]) | ✅ |
| 3a | Sponsors directory (/dreampro/sponsors, /sponsorship, /sponsors/apply) | ✅ |
| 3b | Opportunities (/dreampro/opportunities[/slug]) | ✅ |
| 3c | Sponsorable participants (/dreampro/sponsorable) | ✅ |
| 4 | Local meetups + events (/dreampro/meetups[/id], /meetup-organizer) | ✅ |
| 5 | K-12 classrooms (/dreampro/classroom, /templates/classroom, /classrooms/[id]) | ✅ |
| 6 | Family teams (/dreampro/family) | ✅ |
| 7 | Manual scoring trigger (cron deferred) | ✅ trigger / ⏳ cron |
| 8a | Dream videos (URL embed model) | ✅ |
| 8b | Dedicated /dreampro/videos channel landing + Creator Kit | ⏳ pending YouTube channel |
URL map
| Route | Purpose | File |
|---|---|---|
/dreampro | Public landing with 8 entry-point cards | page.tsx |
/dreampro/templates | Browse all research-grade templates | templates/page.tsx |
/dreampro/templates/[id] | Template detail (clone, replicate, build, video submit) | templates/[id]/page.tsx |
/dreampro/templates/classroom | Classroom-safe template browse with grade-band filter | templates/classroom/page.tsx |
/dreampro/map | World map of opted-in participants | map/page.tsx |
/dreampro/citizen-scientist | Individual opt-in form | citizen-scientist/page.tsx |
/dreampro/competition/[slug] | Annual competition leaderboard | competition/[slug]/page.tsx |
/dreampro/sponsors | Sponsor wall | sponsors/page.tsx |
/dreampro/sponsorship | Program landing with tier benefits | sponsorship/page.tsx |
/dreampro/sponsors/apply | Sponsor application form | sponsors/apply/page.tsx |
/dreampro/opportunities | Browse opportunities by kind | opportunities/page.tsx |
/dreampro/opportunities/[slug] | Opportunity detail with apply CTA | opportunities/[slug]/page.tsx |
/dreampro/sponsorable | Directory of citizen scientists seeking sponsorship | sponsorable/page.tsx |
/dreampro/meetups | Browse meetups + global upcoming events feed | meetups/page.tsx |
/dreampro/meetups/[id] | Meetup detail with events list and RSVP | meetups/[id]/page.tsx |
/dreampro/meetup-organizer | Meetup group registration | meetup-organizer/page.tsx |
/dreampro/classroom | Teacher opt-in form | classroom/page.tsx |
/dreampro/classrooms/[id] | Public classroom page with progress reports | classrooms/[id]/page.tsx |
/dreampro/family | Family team registration | family/page.tsx |
Backend
All endpoints live in apps/backend/src/routes/dreampro.ts (a single large router file). Services and helpers:
- Scoring:
services/dreampro/competition-scorer.ts—recomputeCompetition(),enrollParticipantInActiveCompetitions() - Privacy floor:
utils/geoFuzz.ts—fuzzCoords(),snapToGrid() - YouTube parsing:
utils/youtube.ts—extractYouTubeVideoId(),fetchYouTubeOEmbed() - Legacy bridge (Phase 0 of unification):
services/dreampro/legacy-bridge.ts - Mosaic generator:
services/dreampro/mosaic-generator.ts
Express route ordering trap
Several specific routes MUST be defined before parameterized /:id routes or Express will incorrectly match the parameter pattern. Routes that need this care:
GET /templates/community-summarybeforeGET /templates/:idGET /templates/classroombeforeGET /templates/:idGET /meetup-eventsbeforeGET /meetups/:idGET /participants/sponsorablebeforeGET /participants/:idGET /participants/mebeforeGET /participants/:id
Migrations
| # | File | Adds |
|---|---|---|
| 050 | 050_dreampro_unification.sql | dreams template/clone columns, dream_replications, dream_builds, dream_cheers, version-bump triggers, can_mark_research_grade() |
| 051 | 051_legacy_experiment_clone_bridge.sql | legacy_experiment_clone_map for dual-write |
| 052 | 052_dreams_is_seed.sql | is_seed flag |
| 053 | 053_dream_mosaics.sql | dream_mosaics cache + participation_version counter |
| 054 | 054_participants_and_competitions.sql | participants, participant_pattern_focus, competitions, competition_entries; seeds open-energy-2026 |
| 055 | 055_sponsors.sql | sponsors + sponsorships (M2M) |
| 056 | 056_opportunities.sql | opportunities (internship/scholarship/fellowship/mentorship) |
| 057 | 057_sponsorable_participants.sql | seeking_sponsorship + sponsor_outreach |
| 058 | 058_classrooms.sql | dreams.safety_level, dreams.min_grade_band, classroom_progress_reports |
| 059 | 059_meetups.sql | meetup_events + meetup_event_rsvps + count view |
| 060 | 060_dream_videos.sql | dream_videos + dream_video_upvotes |
Privacy & safety invariants (do not violate)
These are the load-bearing design rules. Most have a "we'd get sued or banned" failure mode. Treat them as immutable unless explicitly relaxed by legal review.
Geographic privacy (all participant kinds)
- All coordinates fuzzed server-side via
fuzzCoords()to a ~5km grid + per-user deterministic jitter - The raw lat/lng a user submits is never persisted
- Enforced in
POST /api/dreampro/participants/opt-in— there is no path that bypasses the fuzz
K-12 classrooms (Phase 5)
- NO STUDENT DATA EVER. The participant is the school. The teacher is the contact. There are no rows representing individual students anywhere in the database.
- APPROXIMATE STUDENT COUNTS ONLY — bucketed at the form layer (10/20/30/40), never exact.
- TEMPLATES DEFAULT TO
adult_only. Admins must explicitly call/admin/templates/:id/promote-to-classroomto make any template visible in the classroom catalog. There is no auto-promotion. - PROGRESS REPORTS ARE AGGREGATE — class-as-a-unit, never per-student.
- TEACHER MUST OWN THE CLASSROOM —
POST /classrooms/:id/progress-reportschecksclassroom.linked_user_id === auth.uid(). - NO PHOTOS, NO STUDENT FILES, NO DM. The platform never accepts uploads tied to a classroom.
Sponsors directory (Phase 3a)
contact_emailandcontact_nameare admin-only and never returned by public endpoints — only the columns inSPONSOR_PUBLIC_COLUMNS- Six tiers: platinum / gold / silver / bronze / in_kind / community
- Directory model only — HiveJournal does NOT process payments. Sponsors apply, admin approves, the wall renders publicly. Money flows happen off-platform.
Sponsor outreach (Phase 3c)
- The recipient's email is never exposed to the sender. The platform forwards via Resend with the sender's reply-to so threading still works
- Rate-limited 5/24h per (sender, recipient) to prevent abuse
Dream videos (Phase 8a)
- Classroom-kind participants CANNOT submit videos (minor safety, enforced server-side at
POST /videos) - Rate limit: 3 submissions per user per 24 hours
- URL must extract a valid 11-char YouTube ID
- oEmbed must succeed (rejects private/deleted/non-existent videos)
- One pending or approved video per (template, submitter) — schema UNIQUE constraint
- Embeds use
youtube-nocookie.comto minimize tracking - Directory only — admin approval before public display, never re-host video files
Consent capture
- Opt-in submission IS the consent record. A
participantsrow existing implies the user submitted the form - We capture
consent_version('v1') andconsent_atso we can prove what was on the page at the time - If consent text changes, bump the version
Scoring model (Phase 2 + 7)
The scoring service walks dreams (clones), dream_replications, and dream_builds for events whose created_at falls inside [competition.starts_at, competition.ends_at], groups by user_id, multiplies by per-event weights, and materializes the totals into competition_entries.current_score + rank.
Default weights:
| Event | Points |
|---|---|
| Clone a template | 1 |
| Complete a clone | 5 |
| Confirm a replication | 25 |
| Refute a replication | 5 |
| Publish a build | 10 |
Weights are stored per-competition in competitions.scoring_rules and can be overridden. Recompute is idempotent — same input state always produces same output.
Manual trigger (Phase 7)
The competition leaderboard page detects super_admin and shows an amber row with a "Recompute scores" button. The button POSTs to /api/dreampro/competitions/:slug/recompute, which is gated server-side via isSuperAdmin(). On success the page refetches the leaderboard so new scores/ranks render in place.
Auto-enrollment
Opting into the map auto-enrolls the new participant in all active, entry-open competitions. Implemented in both:
- The API endpoint
POST /participants/opt-in(callsenrollParticipantInActiveCompetitions()) - Seed scripts (which bypass the API) — see
seed-citizen-scientists.tsfor the manual mirror
Future cron (Phase 7 deferred)
When added, the cron should iterate competitions WHERE is_active AND entry_open and call recomputeCompetition(comp.id) for each. The trigger code is the same one-liner as the manual button.
Map architecture (Phase 1)
Leaflet + OpenStreetMap tiles. Free, no API key, indefinitely sustainable.
The inner map component must be dynamic-imported with ssr: false because Leaflet references window at module load:
// apps/frontend/src/app/dreampro/map/page.tsx
const WorldMapInner = dynamic(() => import('@/components/dreampro/map/WorldMapInner'), {
ssr: false,
})
Seed data
Run order matters — sponsors must exist before opportunities, participants before meetups.
cd apps/backend
npx tsx scripts/seed-citizen-scientists.ts --apply # 12 globally distributed individuals
npx tsx scripts/seed-sponsors.ts --apply # 6 placeholder sponsors
npx tsx scripts/seed-opportunities.ts --apply # 4 sample opportunities
npx tsx scripts/seed-meetups.ts --apply # 3 meetups + 6 events
All seed rows carry is_seed=true so they can be filtered out via the Show demo toggles on each page.
Computational companion — calculators, optimizer, structured measurements
A full software layer that lets users predict before they build, reducing wasted physical iterations and enabling software-only contributions toward the 50-confirmation goal.
6 experiment calculators
Pure TypeScript, no external deps. Public, no auth required — accessible to anonymous visitors for funnel. Each returns structured outputs with confidence levels (exact / approximate / order-of-magnitude) and optional Chart.js graph data.
| Calculator | Covers | Objective for optimizer |
|---|---|---|
| Resonance | LC tank f₀, Q factor, bandwidth, tank voltage/current | Max Q factor |
| Coil | Toroid + solenoid inductance, SRF, distributed capacitance, DC wire resistance. 8 cores (FT37/50/82/114 in -43/-61/-77 materials) | Max SRF |
| Impedance | Full Z(f) sweep with magnitude + phase (log frequency axis) — "what your NanoVNA will show" | Max Q |
| Electrolysis | Faraday-law gas production, voltage + overall energy efficiency, pulsed-mode correction | Max energy efficiency |
| Pulsed DC | Back-EMF peak, inductor energy, recovery capacitor voltage + efficiency, time-domain waveform | Max recovery efficiency |
| Plasma | Paschen curve for 5 gases, breakdown voltage, operating-point analysis | Min breakdown voltage |
- Engine:
apps/backend/src/services/experiment-calculators/ - Endpoints:
GET /api/dreampro/calculators,POST /api/dreampro/calculate - Frontend:
ExperimentCalculator.tsx— mounted on research-grade template detail pages
Structured measurements (migration 078)
dream_replications now carries calculator_inputs, calculator_outputs, measured_values, and delta_values JSONB. The delta (predicted vs measured %) is computed server-side via PATCH /api/dreampro/replications/:id/measurements. The "Record your measurements" form on the template page shows real-time delta badges as the user types (green <5%, amber <20%, red >20%).
Parameter optimizer
optimizer.ts sweeps up to 50K parameter combinations per calculator, ranking by the default objective function (see table above). Admins set the top result as the golden spec on the template (predicted_values JSONB on dreams). The public-facing "Confirm this prediction" card on the template page shows: recommended build parameters, expected outcomes, convergence bar toward 50 confirmations within ±5%, and a "Clone + confirm this experiment" CTA.
- Endpoints:
POST /api/dreampro/optimize,GET /api/dreampro/optimize/meta/:calculator,POST /api/dreampro/templates/:id/golden-spec(super-admin),GET /api/dreampro/templates/:id/confirm-invitation(public) - Frontend:
ExperimentOptimizer.tsx
Aggregate results + anomaly detection
GET /api/dreampro/templates/:id/measurement-stats aggregates measured_values across all replications. Per-parameter: mean, stddev, min/max, predicted reference, within-5% count, anomaly detection (>2σ). Frontend ExperimentResults.tsx renders Chart.js histograms with the predicted-value bin highlighted green, convergence bars, and anomaly flags in red.
The end-to-end flow
- Admin runs optimizer → reviews ranked configurations → sets #1 as golden spec
- Public "Confirm this prediction" card appears on the template page
- Visitor runs the calculator (predict before you build) → sees expected values + graph
- Buys parts, builds, records measurements → system computes delta
- Community aggregates converge (or diverge) → convergence bar tracks toward 50
- Anomalies (>2σ) surface for investigation — the gap between calculation and reality is where the 768-patent meta-pattern claims live
Future work
- Phase 7 cron — wire the recompute trigger into Vercel Cron (or equivalent). The function call is already factored out
- Phase 8b —
/dreampro/videoschannel landing page with downloadable Creator Kit assets. Blocked on the actual YouTube channel existing (so we can hard-link to it) - Federation considerations — the participant model is single-instance. If/when we want to federate citizen scientist data with other platforms, the
participantstable will need a stable external identifier and consent-flow extensions
See also
INDEX.md § DreamPro Citizen Science Platform— feature index entries- open-energy.md — original 10-phase Open Energy initiative (legacy
/open-energy/*routes still exist) - ../DREAMPRO_PLAN.md — earlier DreamPro design notes