Skip to content

Personal data tools ​

AuditStash provides commands to redact selected personal fields, delete audit rows attributed to a user, and export those rows as JSON. These operations apply to audit data, not every record in your application.

The default redaction list cannot identify all personal information in arbitrary payloads. Configure additional fields and review the result for your schema. Hashing a user identifier does not guarantee that the person cannot be identified. These tools alone do not establish GDPR compliance.

Command Line Interface ​

The audit_stash gdpr command provides all GDPR operations:

bash
# View statistics for a user's audit logs
bin/cake audit_stash gdpr stats --user-id=123

# Redact configured fields in a user's audit logs
bin/cake audit_stash gdpr anonymize --user-id=123

# Preview anonymization without making changes
bin/cake audit_stash gdpr anonymize --user-id=123 --dry-run

# Delete user's audit logs completely
bin/cake audit_stash gdpr delete --user-id=123 --force

# Export user's audit logs
bin/cake audit_stash gdpr export --user-id=123
bin/cake audit_stash gdpr export --user-id=123 --output=/path/to/export.json

Operations ​

Stats ​

View statistics about a user's audit log footprint:

bash
bin/cake audit_stash gdpr stats --user-id=123

Output:

GDPR Statistics
---------------
User ID: 123
Total records: 47

By table:
  - Articles: 23
  - Comments: 15
  - Users: 9

By event type:
  - update: 30
  - create: 12
  - delete: 5

Anonymize ​

This operation replaces configured identifying fields while retaining the audit rows. It does not automatically detect personal data in every field.

bash
bin/cake audit_stash gdpr anonymize --user-id=123

What gets anonymized:

FieldBeforeAfter
user_id123anon_a1b2c3d4
user_displayJohn DoeANONYMIZED
meta.userjohn@example.comANONYMIZED
meta.usernamejohndoeANONYMIZED
meta.emailjohn@example.comdeleted@anonymized.local
meta.ip192.168.1.1000.0.0.0
meta.user_agentMozilla/5.0 ...ANONYMIZED
original.emailold@example.com[REDACTED]
changed.nameJohn Doe[REDACTED]

Use --dry-run to preview changes:

bash
bin/cake audit_stash gdpr anonymize --user-id=123 --dry-run

Delete ​

Complete deletion removes all audit logs for a user. Use this only when required by specific regulations, as it destroys the audit trail.

bash
# Requires --force flag
bin/cake audit_stash gdpr delete --user-id=123 --force

# Preview deletion
bin/cake audit_stash gdpr delete --user-id=123 --dry-run

Export ​

Export all audit logs for a user in JSON format for data portability requests:

bash
# Output to console
bin/cake audit_stash gdpr export --user-id=123

# Save to file
bin/cake audit_stash gdpr export --user-id=123 --output=/path/to/export.json

Export format:

json
{
  "export_date": "2024-01-15T10:30:00+00:00",
  "user_id": "123",
  "record_count": 47,
  "audit_logs": [
    {
      "id": 1234,
      "transaction_key": "abc-123-def",
      "type": "update",
      "source": "Articles",
      "primary_key": "42",
      "display_value": "My Article Title",
      "original": {"title": "Old Title"},
      "changed": {"title": "New Title"},
      "meta": {"ip": "192.168.1.1"},
      "created": "2024-01-15T10:30:00+00:00"
    }
  ]
}

Configuration ​

Configure GDPR behavior in config/app.php:

php
'AuditStash' => [
    'gdpr' => [
        // Strategy for anonymizing user_id: 'hash', 'null', or 'placeholder'
        'anonymizeUserId' => 'hash',

        // Custom anonymization values for meta fields
        'anonymizeFields' => [
            'user' => 'ANONYMIZED',
            'email' => 'deleted@anonymized.local',
            'ip' => '0.0.0.0',
            'username' => 'ANONYMIZED',
            'user_agent' => 'ANONYMIZED',
        ],

        // PII fields to redact in original/changed data
        'piiFields' => [
            'email',
            'name',
            'first_name',
            'last_name',
            'phone',
            'address',
            'ip_address',
            'username',
        ],
    ],
],

User ID Anonymization Strategies ​

StrategyResultUse Case
hash (default)anon_a1b2c3d4Allows grouping anonymized records
nullnullComplete removal of user association
placeholderDELETED_USERClear indication of deletion

Programmatic Usage ​

Use GdprService directly in your application:

php
use AuditStash\Service\GdprService;

$service = new GdprService();

// Get statistics
$stats = $service->getStats($userId);

// Anonymize logs
$count = $service->anonymize($userId);

// With custom options
$count = $service->anonymize($userId, [
    'anonymizeUserId' => 'null',
    'piiFields' => ['email', 'ssn', 'credit_card'],
]);

// Delete logs
$count = $service->delete($userId);

// Export logs
$json = $service->export($userId, 'json');
$array = $service->export($userId, 'array');

// Find logs for a user
$query = $service->findByUser($userId);

Before Running Maintenance ​

Use --dry-run to inspect the planned operation. Review configured redaction fields against your actual payloads and confirm whether records should be redacted, deleted, or retained. The plugin does not make that policy decision.

When a hash chain is enabled, maintenance records a seal as described below. Retention cleanup still applies to redacted rows by their source and creation time; it does not select a separate policy based on anonymization status.

Integration with Retention Policies ​

Combine GDPR operations with retention policies for a complete data lifecycle:

php
// config/app.php
'AuditStash' => [
    'retention' => [
        'default' => 90,        // Keep logs for 90 days
        'tables' => [
            'Users' => 365,     // Keep user logs for 1 year
            'Orders' => 2555,   // Example retention period
        ],
    ],
    'gdpr' => [
        'anonymizeUserId' => 'hash',
    ],
],

Workflow:

  1. User requests data deletion
  2. Run audit_stash gdpr anonymize --user-id=X
  3. Redacted logs remain until the configured retention period expires
  4. Cleanup command removes old anonymized logs based on retention policy

Hash chain ​

With hashChain enabled, anonymize and delete append a chain_seal row so that verify_chain keeps passing. The seal records the operation and the number of rows, not the user id. See Retention cleanup and GDPR erasure.

Released under the MIT License.