go-auditGo Audit
Features

Snapshot & Restore

Reconstruct any entity's exact state at any past moment — or roll it back. Every restore is itself an auditable event.

Because every create, update, and delete is stored as an immutable change record, Go Audit can replay that history to reconstruct an entity's state at any past moment — and write it back if you need to roll forward to a known-good version.

How Snapshots Work

Snapshot fetches every audit row for an entity up to a cutoff time, then replays them in chronological order: a create/restore resets the state, an update applies the changed fields, and a delete/soft_delete clears it. The replay is purely in-memory — it issues one read query and never writes.

Capture a Snapshot

func (Auditor) Snapshot(
    ctx context.Context,
    entityType, entityID string,
    at time.Time,
) (map[string]any, error)
Go
// reconstruct order 42 as it was at the start of 2026
at := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)

state, err := auditor.Snapshot(ctx, "orders", "42", at)
if err != nil {
    return err
}
// state is the column → value map at that moment
Go snippet

Returns nil (without error) if the entity did not exist or had been deleted at that time. Requires a non-empty entityType and entityID, and a non-zero at.

Restore an Entity

Go
func (Auditor) Restore(
    ctx context.Context,
    entityType, entityID string,
    at time.Time,
) (*RestoreResult, error)
Go snippet

Restore reconstructs the target state via Snapshot, writes a restore audit entry (old values = current state, new values = target state), and returns the target values. It does not write to your domain table — you apply the returned values through your ORM. The adapter will not double-record that write, because the restore entry is already persisted.

Go
result, err := auditor.Restore(ctx, "orders", "42", at)
if err != nil {
    return err
}

// apply the returned values via GORM
gormDB.Model(&Order{ID: 42}).Updates(result.Values)
Go snippet
Go
type RestoreResult struct {
    EntityType string         `json:"entity_type"`
    EntityID   string         `json:"entity_id"`
    Values     map[string]any `json:"values,omitempty"`
    WasDeleted bool           `json:"was_deleted"`
}
Go snippet

WasDeleted is true when the target state was empty (the entity did not exist at at).

Restore overwrites live rows when you apply the returned values, so production restores should be approved out-of-band. The previous state is never lost — it remains in the audit log, and the restore itself is recorded.

Purge Old Logs

Snapshots can only replay history that still exists in the audit tables. Use Purge on a schedule to enforce retention.

Go
func (Auditor) Purge(ctx context.Context, before time.Time) (PurgeResult, error)
Go snippet
Go
// run nightly: drop entries older than 180 days
cutoff := time.Now().AddDate(0, 0, -180)

result, err := auditor.Purge(ctx, cutoff)
if err != nil {
    return err
}
log.Printf("purged data=%d api=%d", result.DataLogs, result.APILogs)
Go snippet

Purge deletes rows older than before from audit_logs and audit_api_logs (only the tables that are enabled) and returns a PurgeResult:

Go
type PurgeResult struct {
    DataLogs int64
    APILogs  int64
}
Go snippet

Purge is irreversible — deleted entries cannot be reconstructed, and neither can snapshots that depend on them. Take any time-travel cutoff you need before purging in production.

On this page