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
Authentication
Section titled “Authentication”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
dualauthprofile — it accepts both token kinds below, routed by the token’sissclaim. 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
- role
- Services (machine-to-machine, e.g. GNotify) — a confidential client
(
glog-ingest) using the client-credentials grant. It needs a default client scopeaudit:write(Include in token scope: On) and atenant=demohardcoded mapper, so its token carriesscope: 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).409if the username is taken;400if username < 3 or password < 6 chars.POST /api/auth/login{username, password}→ 200 with a token.401on 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:
| profile | auth | identities |
|---|---|---|
localdev | bypassed (synthetic principal) | everything is tenant demo |
localauth | standalone username/password (HS512) | local app_user table |
staging | Keycloak resource server (RS256) | realm interdictii |
Endpoints
Section titled “Endpoints”Ingest — POST /api/v1/app/audit-events
Section titled “Ingest — POST /api/v1/app/audit-events”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:
| field | type | required | notes |
|---|---|---|---|
tenantId | string | ✅ | must match the authenticated tenant |
eventTimestamp | ISO-8601 instant | ✅ | when the action happened, e.g. 2026-07-23T09:01:05Z |
service | string | ✅ | originating system code (GRegistry InformationSystem.code), e.g. gpay |
objectType | string | ✅ | resource category, e.g. PAYMENT, SESSION, DOCUMENT |
actionType | string | ✅ | operation verb, e.g. CREATE, LOGIN, SIGN |
module | string | sub-module within the service | |
operation | string | business operation name, e.g. PAYMENT_PROCESS | |
actorType | string | USER / SERVICE / ADMIN / SYSTEM | |
resourceType | string | ||
sessionId | string | ||
status | string | SUCCESS / FAILURE (drives dashboards) | |
objectAffected | string | id of the affected object, e.g. pay-5521 | |
idnp | string | national person id, when relevant | |
userDetails | string | actor label, e.g. username | |
ipAddress | string | client IP | |
correlationId | string | shared across services for one flow → correlated search | |
viewLink | string | deep link back into the originating service | |
details | object (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.
Operational (public)
Section titled “Operational (public)”GET /health/live, /health/ready, /metrics (Prometheus), /info.
Minimal example
Section titled “Minimal example”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.