Audit Log & Permissions

ClientCove logs every meaningful change to locations and assets — field updates, vault access, file uploads, assignment changes — with old value, new value, user, and timestamp. The log is the source of truth for "who changed what when".

What Gets Logged

For both assets and locations, the activity log captures:

Field changes

  • Status / Condition
  • Next Service Date / Last Service Date / Warranty Expiry
  • IP Address / MAC Address
  • Assigned To / Secondary
  • Primary Contact (any of name, email, phone, user)
  • Service Notes
  • URLs (added, removed, edited)
  • Title (rename)
  • Category / Asset Type / Location Type

Vault events

  • PIN requested
  • PIN verified (vault unlocked)
  • PIN failed
  • Credentials viewed (after unlock)
  • Credential set added / edited / removed
  • Vault manually locked

File events

  • File uploaded
  • File renamed
  • File deleted

Lifecycle

  • Asset/location created
  • Asset/location archived
  • Asset/location restored from archive
  • Asset/location permanently deleted

Every event captures: who (user ID), when (timestamp), what (action + field), before/after (old/new values, except for vault credentials which log only the action).

The Activity Log Tables

ClientCove uses dedicated database tables, not WordPress posts, for activity logs:

Asset log: wp_asset_activity_log

ColumnTypePurpose
idbigintPrimary key
asset_idbigintThe asset this entry belongs to
user_idbigintWho performed the action
actionvarchar(50)Event type (e.g. field_change, vault_access, file_uploaded)
field_namevarchar(255)Which field was changed (e.g. status, next_service_date)
old_valuelongtextPrevious value (NULL for creation events)
new_valuelongtextNew value (NULL for deletion events)
file_namevarchar(255)For file events, the affected filename
timestampdatetimeWhen the event happened

Indexes on asset_id, user_id, and timestamp for fast lookups.

Location log

A parallel table with the same schema, scoped to locations.

Append-only

Both tables are append-only by design — no UPDATE, no DELETE except via the cleanup hook (next section). The log is meant to be the source of truth, not editable.

Where to View the Log

In the asset/location workspace

The Activity Log card appears in the left column of the asset workspace (or in the Activity tab on mobile). It shows the most recent N events as a vertical timeline:

  • User avatar + name
  • Action description (e.g. "Changed Status from Active to Maintenance")
  • Time ago (relative) with absolute timestamp on hover
  • Pagination or "Load More" for older events

The log is read-only in the UI — no editing, no deleting from the workspace.

Filtering

The log can be filtered by:

  • Event type (field changes only, vault events only, file events only)
  • User (show only events by a specific tech)
  • Date range

Filters are accessible via the Activity Log card's controls (admin/editor only).

Direct database access

For deeper analysis or compliance reporting, query the table directly:

SELECT *
FROM wp_asset_activity_log
WHERE asset_id = 123
  AND timestamp >= '2026-01-01'
ORDER BY timestamp DESC;

Or join against the assets table for context:

SELECT
    l.timestamp,
    u.display_name AS user,
    l.action,
    l.field_name,
    l.old_value,
    l.new_value,
    p.post_title AS asset
FROM wp_asset_activity_log l
JOIN wp_users u ON u.ID = l.user_id
JOIN wp_posts p ON p.ID = l.asset_id
WHERE l.timestamp >= NOW() - INTERVAL 30 DAY
ORDER BY l.timestamp DESC;

Log Entry Format

A typical timeline entry looks like:

Jane Doe changed Status from Active to Maintenance 2 hours ago — Apr 30, 2026 at 14:22

For vault events:

Bob Smith unlocked the Vault (PIN verified) 5 minutes ago — May 2, 2026 at 09:15

For file events:

Jane Doe uploaded printer-manual-v2.pdf Yesterday — May 1, 2026 at 16:08

For relationship changes (Assigned To):

Admin User changed Assigned To from Bob Smith to Jane Doe 3 days ago — Apr 29, 2026 at 11:00

The format is generated by track_asset_activity() (and the parallel function for locations) — it human-readable-ifies the field names and looks up user display names from IDs.

Vault Events vs Field Events

A specific design decision: vault credential changes are logged but the values are not.

For a regular field change like Status, the log captures both old and new values:

field_nameold_valuenew_value
statusActiveMaintenance

For vault credential changes, the log captures the action and the credential set label, but not the password/key values:

actionfield_nameold_valuenew_value
vault_credential_addedCloudflare AdminNULLNULL
vault_credential_editedCloudflare AdminNULLNULL
vault_credential_removedOld API KeyNULLNULL

This is intentional — even an audit log shouldn't expose secrets in plaintext. The action and the label are enough to identify what changed; the actual values are encrypted in the vault and accessed only via PIN-verified decrypt.

The implication: the audit log can tell you "Jane edited the Cloudflare credential at 3:14pm", but to see what the new password actually is, someone has to unlock the vault separately (and that unlock will itself be logged).

Retention & Cleanup

By default, log entries are kept indefinitely — they're append-only and there's no automatic pruning.

When entries are deleted

The only automatic deletion is when the parent asset/location is permanently deleted (not archived). The delete_asset_log_on_asset_deletion hook fires on before_delete_post and removes all log entries for that asset.

Manual cleanup

For ongoing log table size management:

  • Old logs can be archived to a separate table via custom SQL or scheduled cleanup script
  • Or kept indefinitely — even a busy portal will have manageable log sizes (rows are small, indexes keep queries fast)

Compliance retention

For regulated industries (HIPAA, SOX, PCI), confirm your retention requirements separately and configure cleanup accordingly. The default keep-everything model is conservative — easier to satisfy retention requirements when no cleanup happens.

Capability Matrix

ActionWho
View Locations / Assets directoriesAdministrator, Editor
View any single asset / locationAdministrator, Editor, Representative
View own client's assets / locationsClient users assigned to the client (limited fields visible)
Create assets / locationsAdministrator, Editor
Edit assets / locations (any)Administrator, Editor
Edit assets they're assigned toTechnicians via assigned_to
Inline-edit status / datesAdministrator, Editor (and assigned techs depending on portal config)
Access vault (after PIN)Anyone with view access to the asset/location
Edit vault credentialsAdministrator, Editor
View activity logAdministrator, Editor — full visibility
View own actions in activity logAssigned technicians
ArchiveAdministrator, Editor
Restore from archiveAdministrator, Editor
Permanent deleteAdministrator only
Manage categories / statusesAdministrator, Editor
Bulk importAdministrator, Editor

Visibility Rules

What a non-admin user actually sees depends on their role and assignments:

Administrators / Editors

Full access to all assets and locations across all clients. No filtering.

Representatives

Can view the assets and locations for clients they're assigned to (via assign_to_client). Cannot edit unless explicitly granted.

Technicians (assigned)

Can view assets they're listed as Assigned To or Secondary on. Can edit field values that don't require admin privilege (status, condition, service dates, service notes). Cannot delete or archive.

Client users

Limited view of their client's assets and locations:

  • Visible: title, type, status, address (for locations), assigned tech name
  • Hidden: vault, IP/MAC, assigned-to user details, audit log
  • Editable: nothing — clients are read-only

The hidden fields are filtered at the data layer (format_asset_for_api and similar functions), so even API requests from client users won't return sensitive data.

Anonymous users

No access. Asset/location URLs redirect to login.

Archive vs Delete

Two separate actions for ending an asset/location's lifecycle:

Archive (soft action)

  • Sets _archived = '1' post meta
  • Hidden from the default directory view
  • Restorable via the View Archived toggle
  • Activity log preserved
  • Vault preserved
  • Linked tickets/projects continue to reference the archived item

Use archive when:

  • The asset is decommissioned but the history is still relevant
  • The location is closed but past tickets still reference it
  • You want it out of the way without losing the data

Permanent delete

  • Removes the post entirely
  • Activity log entries are deleted (via the before_delete_post hook)
  • Vault data is deleted
  • Linked tickets / projects retain a broken reference (the post ID no longer resolves)

Use permanent delete only for:

  • Test data created by mistake
  • Items created in error before any real work was done
  • Cleanup of imported but invalid records

For everything else, archive is the safer choice — you keep the audit trail, and you can restore later.

Compliance & Audit Use

The activity log is designed to support audit and compliance use cases:

What the log can prove

  • Change attribution — every field change has a user and timestamp
  • Vault access — every credential view is recorded with user, IP, and time
  • Maintenance compliance — Last Service Date changes show service was performed and when
  • Assignment history — who was responsible for an asset over time

What the log doesn't capture

  • Read events outside the vault — viewing an asset doesn't create a log entry (would create too much noise)
  • Page views — no page-load logging
  • Failed login attempts — handled by WordPress core or your security plugin, not the asset log
  • Vault credential values — by design (only the action is logged, not the secret)

Exporting for audit

For external audit, query the log table directly and export to CSV. A simple example:

SELECT
    p.post_title AS asset,
    l.timestamp,
    u.display_name AS user,
    l.action,
    l.field_name,
    l.old_value,
    l.new_value
FROM wp_asset_activity_log l
LEFT JOIN wp_posts p ON p.ID = l.asset_id
LEFT JOIN wp_users u ON u.ID = l.user_id
WHERE l.timestamp BETWEEN '2026-01-01' AND '2026-12-31'
ORDER BY l.timestamp;

Output the result as CSV via your DB tool of choice and hand it to the auditor.

Was this page helpful?