Audit logs
The append-only, hash-chained record of sign-ins, changes, deployments and secret access, and how to search, export and check it.
On this page
OpsNexus records every sign-in, every change and every secret access in an append-only audit log. Each entry carries the hash of the entry before it, so editing or deleting a row breaks the chain.
Open it from Audit logs in the sidebar. You need Read only on the Audit logs module. Among the built-in roles, Organization Admin, DevOps Engineer and Security Engineer have it.
What an entry contains
| Field | Meaning |
|---|---|
| Time | When it happened. |
| Actor | Who did it: the user's email, system for the first-run setup, or the identifier typed on a failed sign-in. |
| Action | A dotted name such as secret.reveal. |
| Category | A group for filtering: Sign-in, Access, Secrets, Deploy, Integrations, Agents, System and others. |
| Resource | The thing acted on, with its type and ID. |
| Result | Usually success or failed. The data model also allows denied. |
| Source IP | The client's IP address. |
| Details | Extra context as JSON, such as which fields changed or the old and new value of a setting. |
| Hash | The SHA-256 hash of this entry. |
The audit log also records the project an action happened in.
What is never logged
Secret values never go into the audit log. Before an entry is saved, any detail whose key contains password, token, secret, api_token, private_key, client_secret or value is replaced with ***. Credential changes log the field names only.
Recorded actions
The table lists the action names found in OpsNexus, grouped by area.
| Area | Actions |
|---|---|
| Sign-in and your account | auth.login (success and failure), auth.password.change, auth.mfa.enable, auth.mfa.disable, auth.api_key.create, auth.api_key.revoke, profile.update, profile.avatar.set, profile.avatar.remove, profile.session.revoke |
| Users, roles and teams | user.create, user.update, user.delete, user.project_access, role.create, role.permission.update, role.delete, team.create, team.update, team.delete |
| Projects | project.create, project.update, project.delete, project.member.add, project.member.role, project.member.remove, projects.sharing |
| Organization and settings | system.bootstrap, organization.update, settings.password_policy.update, settings.theme.update |
| Secrets and variables | secret.create, secret.update, secret.rotate, secret.reveal, secret.delete, variable.upsert, variable.delete |
| Pipelines and builds | pipeline.trigger, build.cancel, ci.job.create, ci.job.update, ci.job.enable, ci.job.disable, ci.job.delete, ci.job.webhook_rotate, ci.run.approve, ci.run.reject, ci.artifact.delete |
| Pipeline plugins | ci.plugin.install, ci.plugin.create, ci.plugin.publish, ci.plugin.enable, ci.plugin.disable, ci.plugin.delete |
| Build agents | agent.token.create, agent.register, agent.unregister, agent.build_config, and agent.<command> for commands sent to an agent |
| Integrations | integration.create, integration.update, integration.delete |
| Artifacts and marketplace | registry.create, registry.update, registry.delete, artifact.tag.delete, retention.apply, deploy_target.create, marketplace.deploy, marketplace.uninstall |
| Cloud | cloud.account.add, cloud.account.remove, cloud.budget.create |
| Infra designer | infra.stack.create, infra.stack.update, infra.stack.delete, infra.run.approve, infra.run.reject, infra.run.cancel, infra.state.download, infra.state.force_unlock, infra.keypair.generate, infra.keypair.download, infra.windows_password.read |
| Kubernetes | k8s.cluster.create, k8s.cluster.update, k8s.cluster.delete, k8s.agent.token |
| Servers and remote access | remote.host.create, remote.host.update, remote.host.delete, remote.host.test, remote.host.reset_host_key, terminal.session.start, terminal.session.end, terminal.session.terminate |
| Monitoring | alert.acknowledge, alert.resolve, alert_rule.create, alert_rule.update, alert_rule.delete, alert_rule.recommended |
| Notifications | notification.channel.create, notification.channel.update, notification.channel.delete, notification.rule.create, notification.rule.update, notification.rule.delete, notification.digest.create, notification.digest.update, notification.digest.delete |
| Domains | domain.create, domain.update, domain.delete, domain.traffic_source |
| Security | security.scan.upload, security.finding.triage, security.sonarqube.connect |
| Dashboards and reports | dashboard.create, dashboard.delete, report_layout.create, report_layout.update, report_layout.delete |
| Backups | backup.config.update, backup.create, backup.download, backup.restore |
Failed sign-ins are recorded as auth.login with result failed and a reason, for example the attempt count or "account locked".
Search and filter
- Type in Search user, action, resource or IP to match the actor, action name, resource or IP address. The search runs after a short pause in typing.
- Select a category: All, Sign-in, Access, Secrets, Deploy, Integrations, Agents or System.
- Results are newest first, 50 per page.
- Select a row to open its details: category, resource, resource type, source IP, the details JSON and the entry's hash.
The API supports more filters than the page. GET /api/audit-logs accepts category, q, result (success, failed or denied), since (an ISO date-time), page and page_size (up to 200).
curl -H "X-API-Key: <access-key>.<secret-key>" \
"https://opsnexus.example.com/api/audit-logs?result=failed&since=2026-11-01T00:00:00Z"Export
Select Export CSV to download audit-logs.csv. The export uses the current category and search filter. It contains up to 50,000 entries, newest first, with these columns:
time,actor,action,category,resource,result,ip,details,hashThe export endpoint is GET /api/audit-logs/export.csv and accepts category, q and since.
You can also generate the Audit log report as PDF, Excel or CSV for a date range, or email it on a schedule. See Dashboards & reports.
To keep a copy outside OpsNexus, ship the CSV export to your SIEM on a schedule, or collect the API container's output with a log shipper.
The hash chain
Each organization has its own chain. When OpsNexus writes an entry, it:
- Reads the hash of the organization's most recent entry.
- Stores it in the new entry as
prev_hash. - Computes the new entry's
hashas the SHA-256 of a JSON array holding the organization ID, actor ID, action, resource type, resource ID, result, the scrubbed details andprev_hash, serialized with sorted keys.
If someone changes a stored entry, its hash no longer matches its content. If someone deletes an entry, the next entry's prev_hash no longer points at an existing hash.
Check the chain
OpsNexus has no built-in verify button. To check the chain yourself, query the audit_logs table and confirm that each entry's prev_hash equals the hash of the entry before it in the same organization:
docker compose exec -T db psql -U opsnexus -d opsnexus -c "
SELECT id, created_at, action
FROM (
SELECT id, created_at, action, prev_hash,
LAG(hash) OVER (PARTITION BY organization_id ORDER BY created_at) AS expected
FROM audit_logs
) t
WHERE expected IS NOT NULL AND prev_hash IS DISTINCT FROM expected;"An empty result means the links are intact. Replace the user and database names with the POSTGRES_USER and POSTGRES_DB values from your .env.
This query checks the links only. To also detect edited content, recompute each hash from the fields listed above. The project ID is not part of the hash.
Retention
OpsNexus doesn't delete audit entries. There is no retention setting for the audit log, so it grows until you archive it. Plan database size with that in mind, and export older entries to long-term storage if your policy needs it.
Audit entries are included in platform backups. See Backup & restore.