Skip to content

Gerege Nexus

Production · Layer 3 — Platform foundation · Repo: open-gerege-nexus · nexus.gerege.mn

An integrated platform for services, operations and systems. A modular platform that brings the services, operations, systems and data of public and private organisations onto one foundation. Open source, Apache 2.0.

Nexus means the point of connection — where organisations, services, workflows, systems, users and data meet. The platform itself targets no single sector: the modules running on top of it are what define a given organisation's needs.

The ecosystem model is changing

Gerege Nexus is the successor foundation to the Template Platform. The previous model was "one template → a fork per product"; the new one is "one upstream (Nexus) → a fork per brand, refreshed by merging from upstream". The transition is in progress — the existing platforms remain in production. See Layered architecture.

The essential difference: an app is a module

In the previous model a new product meant a new repository, a new deployment and a new database. On Nexus a new product is usually a new module — an app compiled into the same binary that each tenant can switch on or off.

Template model (previous) Nexus model (new)
New product Fork the template Write a module, add it to the catalogue
Distribution One deployment per repo Per tenant, through the app store
Code sharing open-gerege-core + @gerege/ui-core packages One upstream; downstream forks merge from it
Calls between modules HTTP (when repos are separate) In-process Go calls
Enable / disable Requires a deploy An admin decides in app_installations

Modular monolith

Business modules implement a Go Module contract and compile into one binary. Which apps are active for a given tenant is decided dynamically by the app_installations table in PostgreSQL.

  • No extra network hops — modules call each other in-process, so neither microservice latency nor orchestration complexity arises.
  • DAG dependency resolution — module dependencies are resolved recursively over a directed acyclic graph, with cycle detection and semver validation.
  • Catalogue synccatalog/apps.json is the single source of truth; the apps table is refreshed from it on every boot. Adding an app requires no hand-written SQL.
  • App gating — a route belonging to an uninstalled app answers 403 Forbidden.

Why not microservices?

Module boundaries are enforced by Go interfaces, not by the network. The boundary guarantee is kept while network latency, distributed transactions and the operational cost of many deployments are all avoided.

The modules that ship with it

Module ID Path Purpose
Contacts io.example.contacts /contacts Contact directory with XYP auto-fill
Products io.example.products /products Products, pricing, tenant-scoped SKUs
Inventory io.example.inventory /inventory Warehouses, stock levels, append-only movement log
Billing & e-Barimt io.example.billing /billing Invoices, 10% VAT, e-Barimt receipts
Digital Documents io.example.documents /documents Digital records and approval flows
Developer Portal io.example.developer_portal /developer/apps OAuth2 client app registration
PDF E-Sign io.example.esign /esign Legally valid signatures via eID Mongolia (PIN2)
Government services io.example.gov_services /gov Configurable service workflow, hierarchy, SLA

Configurable government service workflow

The gov_services module turns one codebase into a service-delivery capability that every tenant — and every service inside a tenant — configures for itself. No code changes are involved in choosing between three modes:

Mode Meaning
LOCAL The receiving unit fulfils the request itself
DELEGATE Forwarded to a lower unit while the upper unit monitors and verifies
HYBRID A routing rule decides per request which of the two applies

The governing principle — code decides what is possible, configuration decides what is offered. The canonical transition table lives in code; a published version may narrow it but can never widen it. A misconfigured tenant therefore cannot reach an impossible state.

Other guarantees:

  • The status is computed server-side — a client sends an action, never a status.
  • A lower unit finishing work never closes the request — when a step requires verification, completion lands in AWAITING_VERIFICATION.
  • Overdue is derived (due_at < now()), never written over the business status.
  • Tenant and unit isolation live in the schema — every foreign key is composite, including tenant_id, so a row cannot point across tenants even if application code has a bug.
  • Idempotent ingestion — an inbound request is identified by (tenant_id, source_system, external_request_id); an identical retry returns "created": false, and a replay with a materially different payload is refused with 409.
  • Status delivery goes through an outbox — a remote endpoint can never roll back or stall a workflow transition.

Electronic signature — eID Mongolia (PIN2)

The esign module connects to eID Mongolia's qualified remote signature as a relying party:

  1. the PDF is hashed → eID pushes that digest to the citizen's phone,
  2. the citizen approves with PIN2,
  3. eID's own doc-signer embeds the PKCS#7 together with OCSP and CRL data and assembles a PAdES-signed PDF.

The signing private key never reaches the platform. The certificate level defaults to QUALIFIED — accepting ADVANCED would silently downgrade every document the platform produces.

Signing on behalf of an organisation

Representation rights are read live from the national registry, not from a certificate — because a director who resigned yesterday still holds yesterday's certificate.

Also included: a signature log (filters, pagination, CSV export), batch signing, stamp placement with an A4 preview, HSM connection and signing policy. A tenant can require qualified eID signatures and disable the HSM rail outright, including for callers hitting the API directly.

Authentication and state integration

  • Its own OAuth2 / OIDC provider/.well-known/openid-configuration, /oauth2/token, /oauth2/introspect, /oauth2/revoke, supporting authorization_code, client_credentials and refresh_token.
  • eID and DAN — the four official channels: PKI digital signature, Mobile OTP, bank SSO and biometric face verification.
  • XYP — civil registration (WS100101) and legal-entity verification (WS100201).
  • Session tokens are opaque, 256 bits, stored only as a SHA-256 digest. Logout revokes them for real.

Mock mode does not run in production

E-ID / DAN / XYP mock modes exist only for development. Under ENVIRONMENT=production they switch off automatically, so no one can sign in with fabricated citizen data.

AI and resilience

AI — a Gemini assistant grounded in the tenant's actual database state (/api/v1/ai/chat, /stt, /tts, /translate), admin-managed prompts and knowledge base, plus an inventory demand forecaster.

Cloud-native resilience (inspired by go-zero):

Component Role
Adaptive circuit breaker Google SRE style sliding-window failure ratio
Adaptive load shedding 503 + Retry-After when concurrency is exceeded
Singleflight coalescing Collapses duplicate queries, preventing cache stampedes
Exponential backoff retry Retries transient failures with backoff

Language policy

Mongolian plus the six official UN languages = seven in total. Mongolian is the source. Documentation exists in all seven, but the software ships in Mongolian and English, with the remaining five enabled under Settings → Appearance. This is the same principle as this site's i18n policy.

Brands forked from it

Nexus is the upstream; each brand forks from it and is refreshed by merging from upstream.

Brand Repo Domain What differs
Gerege Nexus open-gerege-nexus nexus.gerege.mn Upstream, reference deployment
Gerege SSO sso-gerege-nexus A fork centred on the sign-in, rights and access layer
Eduge.mn eduge-mn-nexus eduge.mn Education-sector brand; carries a host-build overlay for rollouts that cannot pull from GHCR

Two things called Gerege SSO

sso-gerege-nexus is the new fork built on Nexus; sso.gerege.mn in production still runs the older sso-gerege-mn code. Do not conflate them — until the transition completes, the behaviour documented on the Gerege SSO page is the one in force.

Deployment

A push to main runs GitHub Actions: build and push the backend and frontend images to GHCR → copy docker-compose.prod.yml to the server → pull the images → swap the API and frontend over only once migrations have finished → check /health and /ready. Deployment starts only after CI has actually passed.

The server needs nothing but Docker — no source, no Go, no Node.

PUBLIC_ORIGIN defines three things at once

CORS, the OIDC issuer and the eID callback all derive from one variable. Changing it moves DNS, the TLS certificate and every issuer-dependent client together. Use the checklist on Authentication & authorization when changing a domain.

Stack

Layer Choice
Backend Go 1.25 · chi router · pgx (no ORM, hand-written SQL)
Frontend Next.js 15 App Router
Database PostgreSQL 16 — shared schema, isolated by tenant_id
Migrations goose (backend/db/migrations/); runtime DDL is forbidden
Observability Prometheus (/metrics) · OpenTelemetry
Containers Docker Compose · GHCR

See Technology stack for the ecosystem-wide picture.

Detailed documentation

Implementation-level documentation lives in the repository, in seven languages:

Document Contents
README.md Platform overview (7 languages)
docs/ARCHITECTURE_SPECIFICATION.md Layers and architectural decisions (MN/EN)
docs/MODULE_AUTHORING_GUIDE.md How to write a new app module
docs/GOV_SERVICES_WORKFLOW.md The full government service workflow model
docs/DOCUMENTS_SIGNING.md The signing ceremony and its contract
docs/TRANSLATION_GUIDE.md The seven-language translation guide
CHANGELOG.md Changes per release