Skip to content

GLog API — integrator reference

GLog is the centralized audit & logging microservice of the gStack ecosystem. Any service (GPay, GPass, GNotify, GRegistry, …) posts its audit events to GLog over HTTP; GLog stores them immutably (SHA-256 hash chain per (tenant, service)) and exposes query + integrity endpoints.

  • Base URL (demo): https://glog.gstack.esempla.systems
  • OpenAPI: GET /api/v1/openapi.json (Swagger UI at /swagger-ui.html)
  • Content type: application/json
  • How to connect (auth, code samples in curl/Java/PHP/Python/Node, best practices, SDK roadmap): integration-guide.md

GLog is an OAuth2 resource server. Authorization is on audit:read / audit:write / audit:admin, plus a tenant claim that must equal the tenantId in the request (tenant isolation). Send Authorization: Bearer <token>.

The live demo runs the dualauth profile — it accepts both token kinds below, routed by the token’s iss claim. Full end-to-end setup (what to create in Keycloak and why): auth-and-integrations.md.

Two kinds of caller:

  • Humans (the web UI) — log in via Keycloak (public client glog-web, PKCE). Their permissions come from realm roles, mapped in the backend:
    • role ROLE_USER → audit:read (view logs)
    • role ROLE_ADMIN → audit:read + audit:write + audit:admin
  • Services (machine-to-machine, e.g. GNotify) — a confidential client (glog-ingest) using the client-credentials grant. It needs a default client scope audit:write (Include in token scope: On) and a tenant=demo hardcoded mapper, so its token carries scope: audit:write + tenant: demo. This is what the Postman Auth ▸ Get service token request does, and how GNotify authenticates (verified end-to-end).

Standalone auth (no Keycloak) — the localauth profile

Section titled “Standalone auth (no Keycloak) — the localauth profile”

GLog can also run without any Keycloak dependency, using its own username/password accounts. This is the localauth Spring profile (currently the live demo at glog.gstack.esempla.systems). It exposes three open endpoints under /api/auth:

  • POST /api/auth/register {username, password, tenant?} → 201 with a session token. Self-registration always creates a read-only USER (audit:read). 409 if the username is taken; 400 if username < 3 or password < 6 chars.
  • POST /api/auth/login {username, password} → 200 with a token. 401 on bad credentials or a disabled account.
  • GET /api/auth/me (Bearer) → the presented token’s {username, tenant, authorities}.

The token is a self-issued HS512 JWT (signed with GLOG_AUTH_JWT_SECRET), shaped exactly like a Keycloak access token (realm_access.roles + tenant claim), so the same role→scope mapping applies: ROLE_ADMIN → audit:read+write+admin, ROLE_USER → audit:read. A bootstrap admin (GLOG_ADMIN_USER/GLOG_ADMIN_PASSWORD, default admin/admin) is seeded on first start so there is always a way in — change it in any real deployment.

Response body (login/register):

{ "accessToken": "eyJhbGciOiJIUzUxMiJ9…", "tokenType": "Bearer",
"expiresAt": 1753617600, "username": "admin", "role": "ADMIN", "tenant": "demo" }

The three auth modes are mutually exclusive per deployment, selected by Spring profile:

profileauthidentities
localdevbypassed (synthetic principal)everything is tenant demo
localauthstandalone username/password (HS512)local app_user table
stagingKeycloak resource server (RS256)realm interdictii

Scope audit:write. Optional header Idempotency-Key: <key> — a repeated key returns the first-stored event instead of creating a duplicate. → 201 Created, Location: …/{id}, body = the stored event.

Request body:

fieldtyperequirednotes
tenantIdstring✅must match the authenticated tenant
eventTimestampISO-8601 instant✅when the action happened, e.g. 2026-07-23T09:01:05Z
servicestring✅originating system code (GRegistry InformationSystem.code), e.g. gpay
objectTypestring✅resource category, e.g. PAYMENT, SESSION, DOCUMENT
actionTypestring✅operation verb, e.g. CREATE, LOGIN, SIGN
modulestringsub-module within the service
operationstringbusiness operation name, e.g. PAYMENT_PROCESS
actorTypestringUSER / SERVICE / ADMIN / SYSTEM
resourceTypestring
sessionIdstring
statusstringSUCCESS / FAILURE (drives dashboards)
objectAffectedstringid of the affected object, e.g. pay-5521
idnpstringnational person id, when relevant
userDetailsstringactor label, e.g. username
ipAddressstringclient IP
correlationIdstringshared across services for one flow → correlated search
viewLinkstringdeep link back into the originating service
detailsobject (JSON)free-form metadata (stored as JSON)

The response adds server-computed fields: id (UUID), receivedAt, prevHash, entryHash.

Get one — GET /api/v1/app/audit-events/{id}

Section titled “Get one — GET /api/v1/app/audit-events/{id}”

Scope audit:read. → 200 with the event, or 404.

List / search — GET /api/v1/app/audit-events

Section titled “List / search — GET /api/v1/app/audit-events”

Scope audit:read. Query params: tenantId (required), from + to (required, ISO instants), objectType (optional), page (default 0), size (default 20). → 200 { items, page, size, total }.

Integrity — GET /api/v1/admin/audit-events/integrity

Section titled “Integrity — GET /api/v1/admin/audit-events/integrity”

Scope audit:admin. Params tenantId, from, to, service (optional). Recomputes the hash chain → { entriesChecked, chainBreaks[], lastVerifiedAt } (empty chainBreaks = untampered).

Forward replay — POST /api/v1/admin/audit-events/forward/replay

Section titled “Forward replay — POST /api/v1/admin/audit-events/forward/replay”

Scope audit:admin. Re-dispatches failed outbox entries to the external sinks → { replayed }.

Forward log — GET /api/v1/admin/audit-events/forward

Section titled “Forward log — GET /api/v1/admin/audit-events/forward”

Scope audit:admin. The record of what GLog actually transferred to each external sink — including the exact payload and the sink’s response (for MLog, the registration UID). Optional params: sink (e.g. MLOG), eventId (a single event’s forwards), limit (default 50, max 500). Without eventId, scoped to the caller’s tenant, newest first. → 200 array of { id, auditEventId, sink, status, attempts, payload, response, lastError, createdAt, updatedAt }. Rows written before this feature have a null payload. See mlog-forwarding.md.

Service catalog — GET /api/v1/app/services

Section titled “Service catalog — GET /api/v1/app/services”

Authenticated principal. The full service catalog for the caller’s tenant (range-independent): the union of configured well-known services, every service ever seen in the store, and every config row. → 200 array of { service, mlogEnabled }. Backs the Servicii page.

Service MLog toggle — PUT /api/v1/admin/services/{service}/mlog?enabled=<bool>

Section titled “Service MLog toggle — PUT /api/v1/admin/services/{service}/mlog?enabled=<bool>”

Scope audit:admin. Sets (upserts) whether a service’s events are forwarded to the government MLog register — the “Trimite la MLog” switch. → 200 { service, mlogEnabled }. See mlog-forwarding.md.

GET /health/live, /health/ready, /metrics (Prometheus), /info.

Terminal window
curl -X POST https://glog.gstack.esempla.systems/api/v1/app/audit-events \
-H 'Content-Type: application/json' \
-d '{
"tenantId": "demo",
"eventTimestamp": "2026-07-23T09:01:05Z",
"service": "gpay",
"objectType": "PAYMENT",
"actionType": "CREATE",
"operation": "PAYMENT_PROCESS",
"status": "SUCCESS",
"objectAffected": "pay-5521",
"correlationId": "corr-1001",
"details": { "amount": 1250, "currency": "MDL" }
}'

A ready-to-run Postman collection is at docs/glog.postman_collection.json.