Repository navigation
ADR 012 Public Config Surface
Accepted.
GET /config serves DEMI's own frontend: it reads the config container's one document, overlays
it on app settings, and degrades to app settings alone when the document is missing or Cosmos is
unreachable. Its OVERRIDABLE_KEYS (ENVIRONMENT, KEYCLOAK_*, BANNER_COLOUR, USE_MOCK_DATA)
are DEMI's own settings, not eagle-public's.
eagle-public boots from eagle-api's GET /api/config, backed by eagle-api's own MongoDB Config
document. eagle-api has no Config write controller — that document is edited in Mongo by hand — so
there is no write to hang a push handler off the way projects and documents push on their own
controllers.
Seed a second item, id: 'public', into the same config container. GET /config/public serves
it, unauthenticated like /config. PUT /eagle/config/public writes it, on the authenticated write
chain like every other eagle-api push. eagle-api keeps it current: GET /api/config compares the
payload it is about to serve against the last one it sent, and calls PUT /eagle/config/public when
the two differ (including on the first read after the pod starts). The push is fire-and-forget and retried on a later read if DEMI
rejects it. The read path is the only hook available, since Config has no write controller of its
own. A devbox script, src/scripts/seed-public-config.js, reads eagle-api's live /api/config and
writes the allowlisted keys directly — this is the bootstrap, run before the first push exists to
create the document, and the manual override, run to force the copy back to eagle-api's live
payload. It is not how the copy stays current day to day.
eagle-api's Mongo Config document stays the source of truth and the kill switch for both
ACCESS_GATE and the search backend. This copy follows it automatically; see Seeding for the
bootstrap command.
/config/public fails closed, the opposite of /config. A missing or unreadable document
answers 503 with Cache-Control: no-store rather than defaulting, because a defaulted payload
would serve ACCESS_GATE: false and open eagle-public's access curtain — the wrong direction to
fail in. /config degrades to app settings because there both was already a document-free answer,
answered a private admin app, and the app settings are DEMI's own real values. Neither is true here:
there is no meaningful default ACCESS_GATE, and the caller is the public internet.
/config/public sits outside the ACL and the entity catalog, for the reason /config and
apikeys already do: both endpoints are unauthenticated by design, so there is no visibility
predicate to compose and no read[] to check. What gates a public, unauthenticated payload is the
controller's key allowlist (PUBLIC_KEYS) rather than a permission field — a key reaches an
anonymous caller only once it is added to that list, not merely once it is written into the
document. An allowlisted key can also be dropped by a shape check (EXTENDED_PROJECT_PAGES
today), on the push, the seeder and the GET alike.
PUBLIC_KEYS is its own list, not a slice of OVERRIDABLE_KEYS. The two documents answer
different frontends with different key sets; sharing a list would let a key meant for the DEMI
admin console become public because the lists were merged.
- eagle-public gains a second config source (
CONFIG_PATH, see eagle-public'sConfiguration Managementwiki page) that it tries before falling back to/api/config, never in place of it. - The push rides
GET /api/config, so this copy only follows changes a request actually surfaces. A pod that never receives a read after a Mongo edit holds a stale hash until one arrives; in practice every environment gets frequent reads from the frontends themselves. - The
configcontainer holds two items with different failure behaviour on the same read path; a third audience needs its own item and its own allowlist, not a branch on either existing one. - No bicep change: the container already partitions by
/id, and a second item under an existing partition key needs no schema change.
-
Seeding — the
db:seed-public-configbootstrap command -
Connecting-an-Application-to-DEMI — reading
/config/publicwith no credential -
ADR-004: Read ACL Authorization Model — why this route
composes neither
visibilityFor()norsystemAccess() - ADR-011: Public Read Surface — the other reads eagle-public takes from DEMI, and why configuration itself was set aside there
- 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