Skip to content

Gerege Kiosk

Production · Layer 4 — Vertical product · Repo: gerege-kiosk-mn · geregekiosk.mn

A self-service terminal platform — eID-based, AI-enabled. It connects terminals placed in public spaces to the national electronic ID so a citizen can pull a certificate, pay a fee and print a receipt without queueing or meeting a clerk. Available around the clock, independent of office hours.

Ownership

The platform is owned and operated by Gerege Kiosk LLC. Its source lives in a private repository and is built on Gerege Systems' shared open-gerege-core module.

geregekiosk.mn now serves Gerege Nexus

Checked on 2026-08-07: https://geregekiosk.mn/ returns a Gerege Nexus deployment — the page title is "Gerege Nexus" and the description is the Nexus one. The domain has its own valid certificate and sits on 38.180.243.183.

So the gerege-kiosk-mn behaviour documented on this page may no longer match what the public domain serves. Check the version running on a terminal against that deployment.

The core idea — thin app, thick foundation

The Kiosk Go backend is a single file. Authentication, RBAC, the API gateway, the AI pipeline, eID/SSO — every foundational capability lives in github.com/gerege-systems/open-gerege-core. This repository pins a version of it and boots it under its own name:

func main() {
    server.ServiceName = "gerege-kiosk"

    app, err := server.NewApp()
    if err != nil { /* … */ }

    // Application-specific routes go here:
    //   app.Router().Route("/api/xxx", xxx.Routes(app.Pool()))

    if err := app.Run(); err != nil { /* … */ }
}

This is the answer to the fork-sync technical debt described on the Template Platform page: the foundation used to be copied into every repository, and each improvement had to be carried over by hand. Kiosk is now a versioned consumer of open-gerege-core — security patches propagate from one place, and an upgrade is a single go get open-gerege-core@latest.

Layer Where it lives Who owns it
Foundation backend (auth, RBAC, AI, gateway, migrations) open-gerege-core module Gerege Systems
Entry point, branding, configuration gerege-kiosk-mn/backend Gerege Kiosk LLC
Frontend BFF, UI gerege-kiosk-mn/frontend Gerege Kiosk LLC
Deployment, edge vhost gerege-kiosk-mn/deploy Gerege Kiosk LLC

Layout

gerege-kiosk-mn/
├── backend/     # Go · thin consumer of open-gerege-core (cmd/api/main.go)
├── frontend/    # Next.js 15 BFF — Node 20, TanStack Query, mn/en/zh/ru
├── ios/         # SwiftUI reference client (talks only to the BFF)
└── deploy/      # compose, edge nginx vhost, internal DB TLS certificates

The foundation backend is Clean Architecture — handler → usecase → repository → domain, no back-imports, and no ORM (hand-written SQL over pgx).

Authentication — Gerege SSO only

The sign-in screen has exactly one button: Sign in with Gerege SSO. There is no password, no email/OTP registration and no direct eID flow.

graph LR
    C["Citizen / terminal"] --> W["geregekiosk.mn<br/>Next.js BFF"]
    W --> A["Kiosk API"]
    A --> S["sso.gerege.mn<br/>Gerege SSO"]
    S --> E["eID Mongolia"]
    S -.->|"eID proxy"| A
  • OIDC RP flow/api/auth/sso/startsso.gerege.mn/sso/callback. A separate PKCE public-client flow serves mobile.
  • Session — JWT access + refresh, with rotating refresh tokens; logout revokes the refresh token and deny-lists the access token.
  • Tokens never reach the browser — they stay in httpOnly cookies and are attached server-side by the BFF.

Why isn't it an eID relying party itself?

Under boundary rule 2, Layer 3–4 applications never call eID directly. Kiosk holds no eID RP credentials — all eID traffic is relayed through Gerege SSO. That keeps credentials in one place and the audit trail unbroken.

eID PKI profile — through the proxy

The signed-in citizen's PKI dashboard is fetched through SSO's eID proxy. Kiosk calls it with the user's SSO access token, and SSO retrieves the data using its own eID credentials.

What Where it appears
Consolidated summary /me/eid/id
Certificates and their status /me/eid/certificates
Bound devices /me/eid/devices
Authentication / signing history /me/eid/logs
Linked organisations and authorised signers /me/organizations

A disabled proxy (the eid-proxy service switched off at SSO) or an expired token is handled explicitly by the UI rather than surfacing as a 5xx.

An OIDC provider in its own right

Besides being a relying party of SSO, Kiosk can act as an identity provider. When OAUTH_ISSUER and a state key are configured, its own Go OAuth2/OIDC provider activates — no Ory Hydra involved:

  • login · consent · logout screens under /oauth,
  • RP registration and secret rotation in the oauth_clients table,
  • consent skipped for first-party clients, and remembered afterwards,
  • discovery, userinfo and id_token — with the signing key stored encrypted.

This lets satellite applications around Kiosk offer "Sign in with Gerege Kiosk".

The citizen-facing surface

Area What it does
/me/dashboard Personal dashboard
/me/services · /me/applications Service catalogue, applications and their progress
/me/references Certificates and references
/me/notifications Notifications
/me/payments Payments
/me/appointments Appointment booking
/me/organizations Organisations, membership, permissions
/me/eid/sign Sign a document electronically
/me/integrations Third-party connections and Gerege Space
/me/ai · /me/translate AI assistant, live translation

Creating or searching for an organisation queries the state registry through Gerege Verify. Organisation data is isolated per user by Postgres RLS.

Unified service registry

The R1 component of Ring System — service passports and evidence management:

  • service catalogue, versions, publish/archive,
  • life events — grouping services by the citizen's situation,
  • evidences and the once-only dashboard, which measures how well the principle of never asking twice for a document already held is being applied.

Two permission levels apply: registry.view (read) and registry.manage (write).

API gateway

An admin-managed service catalogue: services · routes · consumers · API keys · policies, plus request telemetry (overview and logs).

Current scope

The gateway is today a management and telemetry layer. Turning the configured routes and policies into actual reverse-proxy enforcement (per-consumer rate limits and quotas) is on the roadmap.

Electronic signatures and the sign relay

  • PAdES — server-side PDF signing through eID Mongolia's /v3 interface, using a long-lived Document-Signer certificate (fail-closed in production).
  • Sign relay — a gateway that lets third-party relying parties sign through the platform's eID credentials. This route is served straight from the edge to the API over a loopback port rather than through the web BFF; results are delivered by webhook.

For the distinction between a citizen's PIN2 signature and a system Document-Signer signature, see G-Sign.

AI assistant (Gemini)

A pipeline built on an SDK-free REST client:

Capability Notes
Chat Text and voice messages, function calling
STT Speech → text
TTS Text → speech (PCM→WAV)
Translation Live streaming translation

A three-layer system prompt: hard-coded guardrails plus a scope and instructions configurable by admins in the database. The guardrail layer is never configurable.

The search_knowledge tool grounds answers in the knowledge base. Search is semantic — Gemini embeddings with pgvector cosine similarity, falling back to ILIKE.

A transient Gemini failure does not become a 5xx: chat returns a localised fallback reply (degraded: true). The /ai/* routes are limited to roughly 20 requests per minute per IP.

Integrations and storage

  • Third-party OAuth connections — Google Drive · Google Meet · Dropbox. Tokens are stored encrypted with AES-256-GCM; without credentials configured the card stays inert with a "coming soon" state.
  • Gerege Space — the platform's own SFTP storage with a per-user quota. The SFTP host key is verified (mandatory in production, otherwise fail-closed).

Roles, administration and audit

  • RBAC — dynamic roles and a permission catalogue, in a four-tier model (superadmin → admin → manager → user).
  • Super admin — a separate account with an MFA onboarding flow (invite allow-list → Google → eID → email OTP → TOTP with recovery codes). It lives in its own table, so one person can be both an eID admin and a super admin.
  • Audit log — hash-chained and append-only, readable by admins with an integrity-verification endpoint.
  • Security events — ingest plus a monitoring view.
  • Site appearance — admin-configured accent / font / density / theme, with a per-user override.

Security

Control Implementation
Data isolation Postgres RLS (ENABLE + FORCE); the api connects as a non-superuser role, and a boot guard verifies that RLS actually applies
Session httpOnly cookies; tokens never reach client JS
CSRF Double protection — a custom header plus an origin check on every mutating BFF route
Headers CSP · HSTS · COOP/COEP/CORP, CORS allow-list
Rate limits Auth ~5 req/min (with a 4 KiB body cap), app 50 r/s, /ai/* ~20/min
Database connection sslmode=verify-full in production, against an internal CA
Observability endpoints /metrics and /swagger are bearer-token gated in production
Proxy trust With TRUSTED_PROXIES unset, X-Forwarded-For is not trusted — preventing rate-limit and audit spoofing

For the ecosystem-wide baseline see Security.

Observability

OpenTelemetry traces, Prometheus metrics and structured Zap logs. The service appears in telemetry as gerege-kiosk.

Deployment

A Docker Compose stack: db (Postgres 16 with pgvector) · redis · migrate (one-off) · api · web. The browser only ever reaches web; api, db and redis stay on the internal network with no public ports.

Three deployment decisions are worth noting:

  1. Migrations are a separate step, not part of up -d. Previously every migrate re-run recreated api and web, producing a second of 502s even on commits that touched no code.
  2. If nothing changed, nothing moves. Docker builds are not reproducible — the same source yields a new image ID — so when HEAD is unchanged the deploy is skipped entirely.
  3. Stateful services are not rebuilt on every deploy. Recreating db would drop every live database connection.

The edge nginx vhost is owned by this repository: each deploy installs it into conf.d, runs nginx -t and reloads; if the test fails the previous config is restored. This is the same pattern used by docs.gerege.mn, the Developer Portal and the Template Platform.

Languages

The UI and documentation are available in Mongolian · English · 中文 · Русский. A test enforces dictionary completeness in the frontend — a key missing from any one language fails CI.

Current status

Production Running at geregekiosk.mn. The full platform foundation is inherited; terminal-specific flows continue to be added.

Next up: real gateway enforcement, streaming (SSE) chat responses, nonce-based CSP, automated database backups with restore tests, and a staging environment.