Logging and incident handling¶
PKIFactor writes two kinds of records: technical logs, for operating the platform, and the audit trail, which records who did what and when. Both are written as structured JSON, filtered for secrets before they are written, and the audit trail can be forwarded to a SIEM.
Technical logs¶
Format¶
The backend writes one JSON object per line:
{"timestamp": "2026-10-26T09:14:03.512Z", "level": "WARNING", "logger": "PKIFactor.PKI",
"message": "...", "context": {"trace_id": "5f0c...",
"client_ip": "192.0.2.10", "origin": "network", "component": "api"},
"src": "backend/connectors/...:642 _request"}
| Field | Meaning |
|---|---|
timestamp |
UTC, millisecond precision |
level, logger |
Severity and source module |
context.trace_id |
Identifier of the request, shared by the frontend and the backend |
context.user |
The signed-in user, taken from the verified session (since 1.8.29; never from a value sent by the browser) |
context.client_ip |
Address of the client (see the note below) |
src |
File, line and function, for warnings and errors |
exception |
Stack trace, for errors |
Source addresses
Since version 1.8.29 the address recorded in technical logs, in sign-in audit events and for the sign-in rate limit is the client's: nginx sets it (X-Real-IP, replacing any value sent by the client), the frontend relays it, and the backend trusts it only from an internal peer. Before 1.8.29 these records held the address of the proxy container. Other audit events may still record the internal peer; the nginx access log (access.log on the volume pkifactor_nginx_logs) always has the client address.
PKIFACTOR_LOG_FORMAT=text switches the general logs to plain text; the audit trail stays JSON whatever this setting.
Where they are¶
Logs go to the container output (docker logs pkifactor_backend) and to files in /app/backend/logs, on the persistent volume pkifactor_audit_logs:
| File | Content |
|---|---|
audit.log |
The audit trail (always JSON) |
auth.log, api.log, pki.log, credential.log, db.log, server.log |
Technical logs per area |
error.log, access.log |
Errors, HTTP accesses (text format) |
siem_deadletter.log |
Audit events not yet delivered to the SIEM |
Files rotate at midnight and are kept for PKIFACTOR_LOG_RETENTION_DAYS days (7 by default). Keep the audit trail longer in your SIEM.
To limit noise, a message repeated more than 10 times in a minute is written once with a count (PKIFACTOR_LOG_REPEAT_MAX); errors and the audit trail are never reduced. Health probes are left out of the access log.
Tracing a failure¶
- Every response carries an
X-Trace-IDheader. When an unexpected error occurs, the user sees a short reference (the first eight characters of the trace identifier) instead of internal details; search the logs for it. - Unexpected errors are logged with their probable origin:
product(PKIFactor itself),external(a device or third-party service),network(timeout, DNS, TLS),configuration(refused credentials, missing object) oruser(invalid input). This tells the support team which side to look at first. - Error responses never contain stack traces, SQL fragments or internal addresses.
The audit trail¶
What is recorded¶
Each event records:
| Who | What | When and where |
|---|---|---|
The actor (e-mail address, or system for the worker) |
Event name, action, resource type and identifier, outcome (success or failure), reason, sanitized details |
UTC timestamp, source address, trace identifier |
Main event families:
| Area | Examples |
|---|---|
| Sessions | LOGIN_SUCCESS, LOGIN_FAILED, LOGOUT, PASSWORD_CHANGED, PASSWORD_CHANGE_FAILED, ACCESS_DENIED |
| Certificates | CERTIFICATE_REQUEST_PENDING, CERTIFICATE_ISSUED, CERTIFICATE_APPROVED_ISSUED, CERTIFICATE_REJECTED, CERTIFICATE_REVOKED, CERTIFICATE_TRANSFERRED, CERTIFICATE_ADOPTED, CERT_EARLY_RENEWAL_REQUESTED, RENEW_IDENTITY_CHANGED, CERT_PRIVATE_KEY_EXPORTED |
| Issuance policy | POLICY_BLOCK, POLICY_REQUIRE_APPROVAL, POLICY_AUTO_ALLOWED, POLICY_EVAL_ERROR |
| Approvals | APPROVAL_CLAIMED, APPROVAL_RELEASED, APPROVAL_DEMANDE_ALTEREE (request changed after approval), APPROVAL_OVERRIDE (decision by a non-validator administrator), VALIDATOR_DELEGATION_CREATED, VALIDATOR_DELEGATION_REVOKED |
| Administration | ADMIN_CREATE_USER, ADMIN_UPDATE_ROLE, ADMIN_UPDATE_STATUS, ADMIN_RESET_PASSWORD, ADMIN_UPDATE_AUDIT_SCOPE, OPERATOR_ACCESS_UPDATED, organization, profile, team and validator changes, SETTINGS_UPDATED |
| Access control | ORG_SCOPE_DENIED |
| Keys and backups | KMS_PROVIDER_ACTIVATED, KMS_PROVIDER_MIGRATED, KMS_DEK_ROTATED, KMS_RELOADED, BACKUP_CREATED, BACKUP_FAILED, RESTORE_COMPLETED, RESTORE_FAILED |
| Platform | LICENSE_ACTIVATED, LICENSE_QUOTA_THRESHOLD, LICENSE_EXPIRY_ALERT, SERVICE_TLS_REPLACED, SERVICE_TLS_AUTORENEW_FAILED, CLUSTER_RESTART, LOGS_PURGED |
| Integrations and gateways | SERVICENOW_INBOUND_REQUEST, SERVICENOW_INBOUND_REQUEST_REFUSED, SERVICENOW_INBOUND_AUTH_FAILED, ACME_EAB_CREATED, EST_BASIC_UNVERIFIED, SCEP_UNAUTHENTICATED_BLOCKED |
| Certificate Transparency | CT_ALERT, CT_ALERT_QUALIFIED, CT_RUN |
Exporting a private key (PKCS#12 download) has its own event with the certificate, the actor and the address.
Approval records¶
Each approval request also keeps, in the database, a snapshot of the request as the validator saw it and its SHA-256 fingerprint. The audit screen recomputes the fingerprint on display and shows whether it still matches (column Integrity). The fingerprint detects a snapshot changed after the fact; it is not a signature, so it does not protect against someone who can write to the database and recompute it.
Who can read it¶
| Role | Approval records | Security events (audit.log) |
|---|---|---|
| Administrator, auditor with Audit scope set to Whole estate (the default) | All organizations | Yes |
| Auditor with Audit scope set to Own organisations only | Their organizations only | No |
| Operator with the Read the audit trail permission (granted by default) | Their organizations only | No |
| Validator, user | No | No |
The screens and exports (CSV, JSON) are described in Audit trail.
What is never logged¶
Before an audit event is written, its details are sanitized:
- fields whose name contains
password,secret,token,api_key,private_key,client_secret,passphrase,encryption_key,credential(and a few more), and fields nameddek,kekorwrapped_dek, are replaced by***REDACTED***, at any depth; - any value that looks like a private key (PEM block) is replaced, whatever its field name;
- line breaks are escaped, so a value cannot forge a new log line.
Every log file and the container output also pass through a filter that removes private key blocks, CSRs, encrypted values, session tokens, and password=...-style pairs from any message.
Certificates and public keys are not secrets and can appear in logs.
Forwarding to a SIEM¶
Each audit event is forwarded as it is written, over UDP, TCP or TCP with TLS, in CEF or syslog (RFC 5424) with JSON data. Configuration: SIEM and syslog.
Delivery is not allowed to block an operation, and no event is lost silently:
- an event that cannot be delivered is appended to
siem_deadletter.log, and the failure is logged; - the worker replays that file every 2 minutes once the collector answers, and keeps only the events that still fail;
audit.logstays the local reference, independently of the SIEM.
Incident handling hooks¶
| Signal | Use |
|---|---|
GET /health/ready |
ready, degraded (KMS unavailable, issuance suspended) or HTTP 503 (database unreachable or schema error). The probe needs no authentication and returns only the state. Point your monitoring and load balancer at it |
| SIEM rules | Alert on LOGIN_FAILED bursts, ORG_SCOPE_DENIED, APPROVAL_OVERRIDE, CT_ALERT, CERT_PRIVATE_KEY_EXPORTED, KMS_*, RESTORE_*, ADMIN_UPDATE_ROLE, SETTINGS_UPDATED outside change windows |
| ServiceNow | A failed deployment, a refused key custody or an expiry warning opens an incident; a device that needs a manual installation step opens a change request. A repeated event updates the open ticket instead of opening another (ServiceNow) |
| Expiry and approval notifications (E-mail and notifications) |
First steps after a suspected compromise¶
- Deactivate the accounts concerned (Administration workspace, Governance > Users): their tokens stop working immediately. Resetting a local account's password also ends every session opened before the reset.
- Export the audit trail for the period (Oversight workspace, Compliance > Audit Trail) and keep a copy of
audit.logbefore it rotates. - Search the SIEM by actor and trace identifier, and the nginx access log for client addresses.
- If a platform secret may have leaked, rotate it (Secrets and key management); if a certificate's private key may have leaked, revoke the certificate.
- Take an appliance backup of the state for analysis, then restore a known good state if needed (Backup and restore).