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
| Column | Type | Purpose |
|---|---|---|
id | bigint | Primary key |
asset_id | bigint | The asset this entry belongs to |
user_id | bigint | Who performed the action |
action | varchar(50) | Event type (e.g. field_change, vault_access, file_uploaded) |
field_name | varchar(255) | Which field was changed (e.g. status, next_service_date) |
old_value | longtext | Previous value (NULL for creation events) |
new_value | longtext | New value (NULL for deletion events) |
file_name | varchar(255) | For file events, the affected filename |
timestamp | datetime | When 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_name | old_value | new_value |
|---|---|---|
status | Active | Maintenance |
For vault credential changes, the log captures the action and the credential set label, but not the password/key values:
| action | field_name | old_value | new_value |
|---|---|---|---|
vault_credential_added | Cloudflare Admin | NULL | NULL |
vault_credential_edited | Cloudflare Admin | NULL | NULL |
vault_credential_removed | Old API Key | NULL | NULL |
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
| Action | Who |
|---|---|
| View Locations / Assets directories | Administrator, Editor |
| View any single asset / location | Administrator, Editor, Representative |
| View own client's assets / locations | Client users assigned to the client (limited fields visible) |
| Create assets / locations | Administrator, Editor |
| Edit assets / locations (any) | Administrator, Editor |
| Edit assets they're assigned to | Technicians via assigned_to |
| Inline-edit status / dates | Administrator, Editor (and assigned techs depending on portal config) |
| Access vault (after PIN) | Anyone with view access to the asset/location |
| Edit vault credentials | Administrator, Editor |
| View activity log | Administrator, Editor — full visibility |
| View own actions in activity log | Assigned technicians |
| Archive | Administrator, Editor |
| Restore from archive | Administrator, Editor |
| Permanent delete | Administrator only |
| Manage categories / statuses | Administrator, Editor |
| Bulk import | Administrator, 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_posthook) - 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.
For HIPAA, SOX, PCI, or other regulated environments, the built-in audit log is a starting point — confirm specific requirements with your compliance officer. You may need additional infrastructure (centralized log aggregation, immutable storage, longer retention) layered on top. The ClientCove log gives you the data; compliance tooling is what proves it hasn't been tampered with.