Repository navigation
Sources and Status
Every data source and served surface in DEMI, each in exactly one state. Counts measured 2026-08-26 against the live test API. Prod holds Eagle and Track data only. The demo frontend and every enrichment source are a test-only surface — supported there, not deployed to prod.
States:
- Served — reachable from eagle-public or the demo frontend; must work end to end and have a test.
- Proven, parked — exists to prove a source can be added; runs on demand, writes nothing a served surface reads.
- Gone — nothing references it; code, container and Bicep go together.
| Surface | Environment | Routes consumed |
|---|---|---|
eagle-public (bcgov/eagle-public) |
test |
GET /api/search only, datasets Project, Document, DocumentChunk (src/app/services/api.ts:52), plus GET /api/documents/recent-uploads for the homepage panel — see Recent-Uploads. Item reads, downloads, RecentActivity and ProjectNotification stay on eagle-api. Prod site search reads /demi-search, the same route as test. |
Demo frontend /map, /search, /summary, /intake
|
test only |
/config, /boundaries*, /search, /search/summary, /documents/:id/download
|
| eagle-api mirror push | test |
PUT /eagle/projects/:eagleId, PUT /eagle/documents/:eagleId — see Sync-Architecture
|
| Extractor / intake (GPU box) | test |
POST /documents, /documents/extract, /documents/:id/chunks, PUT /documents/:id, PUT /documents/:id/published
|
| Admin | test |
/db/stats, /admin/index-progress, /admin/api-keys*, POST /admin/sync/wildfires, /admin/audit, /admin/analytics, /admin/cost
|
| No consumer | — |
GET /projects, /projects/:id, /documents, /documents/:id — anonymous, API-first surface; list pages capped at 100 rows without a credential. /api-docs is test-only and 404s in prod |
| Source | State | Depended on by | Evidence |
|---|---|---|---|
projects container + projects index |
Served | both frontends | 393 rows, 348 of them anonymously readable |
documents + index |
Served | both frontends, download | 61,587 rows |
chunks + index |
Served | deep search, DocumentChunk dataset, summariser |
1,128,576 rows, matching the index; the only extracted copy |
epictrack-api GET /api/v1/projects
|
Served | Track-sourced project identity and fields | read nightly by sync-track-teams.js with the demi-track-reader bearer; the mirror is sync-track-projects.js
|
epictrack-api GET /api/v1/works + /works/<id>/phases
|
Served |
phases on project rows |
same run, same bearer — § Track work phases below |
Track static file src/data/track_projects_enriched.json
|
Served (seed) | 382 Track-sourced projects | snapshot 2026-07-29; the fallback when no reader client is configured, and it carries no work phases |
| eagle-api push | Served | project and document mirroring |
PUT /eagle/{projects,documents}/:eagleId — Sync-Architecture
|
Eagle seed src/scripts/seed-nosql.js
|
Served (on demand) | corpus refresh, disaster recovery | reads prod eagle-api's public API; preserves extraction state; --reconcile deletes surplus under a ceiling |
| Eagle-only projects | Served | project list, project search, document search, point reads |
sourceSystem: 'eagle', id prefix eagle-; counted as unlinkedProjects by GET /api/db/stats
|
boundaries |
Served, test only | demo /map
|
281 rows (Municipality 160, Electoral District 93, Regional District 28), simplifiedGeometry only; the prod container is declared and empty |
config |
Served |
/api/config on every page load |
1 row |
apikeys |
Served (infra) | authMiddleware |
— |
sources.wildfire on project rows |
Served, test only | demo /map panel |
activeCountWithin50km, nearestDistanceKm, firesOfNoteNearby, lastCalculatedAt, written by sync-wildfires.js and read under those names by registry.models.ts
|
wildfires container + sync-wildfires.js
|
Proven, parked | nothing reads the container | write-only repository; no schedule; only POST /admin/sync/wildfires
|
| DataBC wildfire WFS | Served (external, browser-side) | demo /map fire layer |
fetched in map-explorer.component.ts:888; no DEMI storage |
| NRPTI | Gone | — | no ingest path; a handful of comment and fixture mentions remain |
phases on a project row is the assessment schedule eagle-public draws its progress rail from.
Track is the only source; Eagle has no equivalent. Eagle's own currentPhaseName and
phaseHistory are a separate record and are untouched by this.
Two epictrack-api endpoints, on the same bearer as the project list:
-
GET /api/v1/works, once. Each work carriesproject_id,work_type_idandis_active. -
GET /api/v1/works/<id>/phases, once per project — for the one work whosework_type_idis 6 (Assessment). Where a project has two assessment works the active one wins, then the highest work id.
GET /api/v1/work-phases cannot replace the pair: its rows carry no project id, and it returns
only completed, legislated phases that ran over or under budget. It is the overage report.
GET /api/v1/projects?with_works=true only filters the project list; its response carries no works.
Fields, per phase, ordered by sortOrder:
| Field | Read from |
|---|---|
name |
work_phase.name, else the phase code's name |
eaActId, eaActName
|
work_phase.phase.ea_act |
workType |
work_phase.phase.work_type.name |
startDate, endDate
|
work_phase.start_date / end_date, ISO 8601 or null
|
numberOfDays |
work_phase.number_of_days |
legislated |
work_phase.legislated |
sortOrder |
work_phase.sort_order |
isCompleted |
work_phase.is_completed |
Nothing else off the Track row leaves DEMI — staff, responsibility notes, milestone detail and day
counts stay upstream. The whole array is public (src/vis/catalog/projects.js, defaultVis: 4).
sync-track-teams.js pulls the phases and sync-track-projects.js writes them alongside the other
Track-owned fields. A run that pulls none leaves the stored ones alone, so a Track outage cannot
blank the rail. trackPhases= in that run's summary line counts the projects it had phases for;
reconcile-eagle.js reports the same count read-only, which is the only phase signal in prod,
where the sync carries no schedule.
GET /api/projects/:id accepts an Eagle id. A 24-character hex :id is resolved through
eagleId. A Track project number or eagle-<ObjectId> is a DEMI id and reads as before. The two
id spaces cannot collide, and both answer under the same ACL and the same field redaction, so a
caller holding an Eagle id needs no translation step to read a project.
sources.* on a project row is the only place non-Eagle/Track data touches a served row, and
publicView (src/repositories/projects.js) allowlists it against the ENRICHMENT_SOURCES app
setting — wildfire in test, empty in prod. A corpus copied from test into prod therefore serves
no enrichment data even if the rows carry it.
Prod Bicep declares projects, documents, chunks, config, apikeys and boundaries.
deployEnrichment: false in azure/main.prod.bicepparam means prod has no wildfires container
at all.
Documented here, not as a code layer — one source exists and its DataBC parsing is most of it.
-
src/scripts/sync-<name>.js, runnable from the CLI andPOST /admin/sync/<name>(authMiddleware+requireWrite). No scheduler unless a consumer needs freshness. - Writes only
sources.<name>onprojects, always withlastCalculatedAt. A scratch container is optional and TTL'd. -
publicViewandENRICHMENT_SOURCESgate what leaves the API. - The consumer renders the stamp and handles absence — absence is what prod looks like.
- One test asserts the written shape is the shape the consumer reads.
- Environment Reality & Gotchas
- Architecture
- Seeding — filling Cosmos from Track and Eagle
- Sources and Status — every source, one state
- Search & Retrieval — overview
- Search Index Reference
- Search Query Construction
- Search Measurements
- Extraction Pipeline
- Extraction Quality — coverage, word-joining, tiling
- Identity Model — which id means what
- Attribute-Level Access — field-visibility levels, catalog
- Sync Architecture — how records get in
- Eagle Mirror Reference — mirrored kinds, refusals, drift classes
- ENGAGE Rows Reference — ENGAGE-owned comment periods, reconcile
- Public-Read Backfill — filling and repairing the mirror containers
-
Short Links —
/s/{code}redirect and/api/links -
Recent Uploads —
/api/documents/recent-uploads, eagle-public homepage panel - Reports — comment export and BCGW CSV reads
-
User Data — saved map areas, preferences,
/meroutes -
Bulk Download —
/api/bulk-downloads, zip jobs - Bulk Download: Operations — enable, poison-queue runbook
- Document Intake — upload link, malware scan, release
- Connecting an Application to DEMI
- Azure Environments
- Document Backup — archive copy of originals, run and check
- Document Backup: Revert and Drill — put documents back from the archive
- PDF Titles — public name written into stored PDFs, run, restore
- Secret Sync — Key Vault, secret-sync Function app, required names
-
Operating DEMI in Azure — deploy, verify
- Operating the DEMI Data Plane — devbox access, probes, audit and cost reads
- Edge ban detector — hourly timer, modes, kill switch, soak and promote
-
CI Workflows — every GitHub Actions workflow, trigger, identity, roles
- CI Deploy Workflows — eagle-demi staging and production deploys
- CI Admin Frontend Workflows — eagle-demi-admin checks and deploys
- Release Process — cutting, tagging, shipping and rolling back a version
- Runbook: Search Outage — index/app schema drift, diagnose, fix, roll back
- BC Gov Landing Zone
- Business Rules
- EPIC.search Business Rules — where DEMI differs from EPIC.search
- 001: Typesense for DFL Search — superseded
- 002: Async Processing (MongoDB Queue) — superseded
- 003: Rule-Based Classification
- 004: Read ACL Authorization Model
- 005: Cosmos Full-Text Search Ruled Out
- 006: AI Summarization over BM25
- 007: Service-to-Service Credentials
- 008: Audit and Usage Analytics Store
- 009: Track Feed — proposed
- 010: Attribute-Level Access Control
- 011: Public Read Surface
- 012: Public Config Surface
- 013: Stored Project Summaries — proposed
- 014: Sync-out to Eagle
- 015: Malware scanning on intake