A deterministic event recording and replay framework for Go. This library provides a powerful way to record ordered events (inputs, state transitions) and replay them deterministically. It supports branching timelines and checksums for validation, making it particularly useful for debugging distributed systems and simulations.
- Event Recording: Capture events with type, payload, timestamp, and automatic checksum generation
- Deterministic Replay: Replay recorded events in exact order with validation
- Branching Timelines: Create alternative timelines from any point in the event history
- Checksum Validation: Automatic integrity checking for events and timelines
- File-based Storage: Simple JSON-based persistence for easy inspection and portability
- CLI Tool: Command-line interface for recording and replaying events
- Thread-safe: Concurrent access support with proper synchronization
go get github.com/BaseMax/go-event-replayTo install the CLI tool:
go install github.com/BaseMax/go-event-replay/cmd/eventreplay@latestOr build from source:
git clone https://github.com/BaseMax/go-event-replay
cd go-event-replay
go build -o bin/eventreplay ./cmd/eventreplaypackage main
import (
"fmt"
"github.com/BaseMax/go-event-replay"
)
func main() {
// Create a new recorder
recorder := eventreplay.NewRecorder("my-simulation")
// Record events
recorder.Record("user.login", map[string]interface{}{
"username": "alice",
"ip": "192.168.1.1",
})
recorder.Record("order.created", map[string]interface{}{
"order_id": 123,
"total": 99.99,
})
// Save timeline to disk
storage, _ := eventreplay.NewStorage(".eventreplay")
storage.SaveTimeline(recorder.GetTimeline())
fmt.Printf("Recorded %d events\n", recorder.EventCount())
}package main
import (
"fmt"
"github.com/BaseMax/go-event-replay"
)
func main() {
storage, _ := eventreplay.NewStorage(".eventreplay")
timeline, _ := storage.LoadTimeline("timeline-id")
replayer := eventreplay.NewReplayer(timeline)
// Register handlers for specific event types
replayer.RegisterHandler("user.login", func(event *eventreplay.Event) error {
fmt.Printf("User logged in: %v\n", event.Payload["username"])
return nil
})
// Replay all events
count, err := replayer.ReplayAll()
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Replayed %d events\n", count)
}
}package main
import (
"github.com/BaseMax/go-event-replay"
)
func main() {
storage, _ := eventreplay.NewStorage(".eventreplay")
timeline, _ := storage.LoadTimeline("timeline-id")
// Create a branch from event index 5
branch, _ := timeline.Branch("alternate-scenario", 5)
// Add different events to the branch
branch.AddEvent(eventreplay.NewEvent("order.cancelled", map[string]interface{}{
"order_id": 123,
"reason": "customer_request",
}))
// Save the branch
storage.SaveTimeline(branch)
}package main
import (
"fmt"
"github.com/BaseMax/go-event-replay"
)
func main() {
storage, _ := eventreplay.NewStorage(".eventreplay")
timeline, _ := storage.LoadTimeline("timeline-id")
valid, errors := timeline.Validate()
if valid {
fmt.Println("Timeline is valid")
} else {
fmt.Println("Validation errors:")
for _, err := range errors {
fmt.Printf(" - %v\n", err)
}
}
}# Record a new event (creates timeline if it doesn't exist)
eventreplay record my-timeline user.login '{"username":"alice"}'
# Record additional events to the same timeline
eventreplay record my-timeline order.created '{"order_id":123,"total":99.99}'
eventreplay record my-timeline payment.processed '{"order_id":123,"amount":99.99}'eventreplay listOutput:
Available Timelines
===================
Name ID Events Created
---- -- ------ -------
my-timeline 1766270220370310... 3 2025-12-20 22:37
eventreplay info <timeline-id>Output:
Timeline Information
====================
ID: 1766270220370310633-fq8wayn0
Name: my-timeline
Created: 2025-12-20T22:37:00Z
Total Events: 3
Checksum: bd0279991db21cb7ad629ffb6645e5d3580046a758909bd4ba4dab16f03f2ace
Events:
Index Type Timestamp ID
----- ---- --------- --
0 user.login 22:37:00 1766270220370381...
1 order.created 22:37:07 1766270227845326...
2 payment.processed 22:37:07 1766270227847210...
eventreplay replay <timeline-id>Output:
Replaying timeline: my-timeline
Total events: 3
---
[Event 0]
ID: 1766270220370381385-4caymk8w
Type: user.login
Timestamp: 2025-12-20T22:37:00Z
Payload: {
"username": "alice"
}
[Event 1]
ID: 1766270227845326752-0o2q4shv
Type: order.created
Timestamp: 2025-12-20T22:37:07Z
Payload: {
"order_id": 123,
"total": 99.99
}
...
# Create a branch from event index 2
eventreplay branch <timeline-id> 2 alternate-flow
# Add events to the branch
eventreplay record alternate-flow order.cancelled '{"order_id":123,"reason":"test"}'eventreplay validate <timeline-id>Output:
Timeline: my-timeline
Timeline ID: 1766270220370310633-fq8wayn0
Total Events: 3
✓ Timeline is valid
✓ All events have valid checksums
✓ Timeline checksum is valid
Record all events (messages, state changes) in a distributed system and replay them to reproduce bugs:
// Record every message
recorder.Record("message.sent", map[string]interface{}{
"from": "service-a",
"to": "service-b",
"payload": messageData,
})
// Later, replay to debug
replayer.RegisterHandler("message.sent", func(event *Event) error {
// Re-execute the message handling logic
return processMessage(event.Payload)
})Create branches to test different scenarios without re-running the entire simulation:
// Run simulation to checkpoint
// Branch from checkpoint
branch, _ := timeline.Branch("scenario-2", checkpointIndex)
// Test alternative decisions
branch.AddEvent(NewEvent("decision.alternate", decisionData))Maintain tamper-evident audit logs with checksum validation:
// Record actions
recorder.Record("user.action", actionData)
// Validate integrity
valid, errors := timeline.Validate()
if !valid {
// Handle tampering
}The library consists of several key components:
- Event: Individual events with ID, timestamp, type, payload, and checksum
- Timeline: Ordered sequence of events with branching support
- Recorder: Captures and stores events
- Replayer: Plays back events with handlers
- Storage: Persists timelines to disk in JSON format
Run the test suite:
go test -v ./...MIT License - see LICENSE file for details
Contributions are welcome! Please feel free to submit a Pull Request.
BaseMax