Quickstart
Requirements
Go 1.24 or later.
Installation
go get github.com/zoobzio/cereal
Basic Codec Usage
For simple marshaling without transforms, use a codec directly:
package main
import (
"fmt"
"github.com/zoobzio/cereal/json"
)
type Message struct {
ID string `json:"id"`
Text string `json:"text"`
}
func main() {
codec := json.New()
msg := Message{ID: "1", Text: "Hello"}
// Marshal
data, err := codec.Marshal(msg)
if err != nil {
panic(err)
}
fmt.Println(string(data))
// {"id":"1","text":"Hello"}
// Unmarshal
var result Message
err = codec.Unmarshal(data, &result)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", result)
// {ID:1 Text:Hello}
}
Processor with Boundary Transforms
For boundary-aware serialization with automatic field transforms:
package main
import (
"context"
"fmt"
"github.com/zoobzio/cereal"
"github.com/zoobzio/cereal/json"
)
// 1. Define your type with boundary tags
type User struct {
ID string `json:"id"`
Email string `json:"email" store.encrypt:"aes" load.decrypt:"aes" send.mask:"email"`
Password string `json:"password" receive.hash:"sha256"`
Token string `json:"token" send.redact:"[REDACTED]"`
}
// 2. Implement Cloner[T]
func (u User) Clone() User { return u }
func main() {
ctx := context.Background()
// 3. Create processor
proc, err := cereal.NewProcessor[User]()
if err != nil {
panic(err)
}
// 4. Configure encryption
key := []byte("32-byte-key-for-aes-256-encrypt!")
enc, err := cereal.AES(key)
if err != nil {
panic(err)
}
proc.SetEncryptor(cereal.EncryptAES, enc)
// 5. Validate configuration (optional - runs automatically on first operation)
if err := proc.Validate(); err != nil {
panic(err)
}
// 6. Primary API: T -> T transforms
// Receive: transform incoming data (hashes password)
incoming := User{ID: "123", Email: "alice@example.com", Password: "secret", Token: "abc123"}
user, err := proc.Receive(ctx, incoming)
if err != nil {
panic(err)
}
fmt.Printf("Received - Password hashed: %s...\n", user.Password[:16])
// Store: transform for storage (encrypts email)
stored, err := proc.Store(ctx, user)
if err != nil {
panic(err)
}
fmt.Printf("Stored - Email encrypted: %s\n", stored.Email)
// Load: transform from storage (decrypts email)
loaded, err := proc.Load(ctx, stored)
if err != nil {
panic(err)
}
fmt.Printf("Loaded - Email decrypted: %s\n", loaded.Email)
// Send: transform for external output (masks email, redacts token)
sanitized, err := proc.Send(ctx, loaded)
if err != nil {
panic(err)
}
fmt.Printf("Sent - Email: %s, Token: %s\n", sanitized.Email, sanitized.Token)
// 7. Secondary API: codec-aware byte serialization (optional)
proc.SetCodec(json.New())
// Decode: bytes -> *T with Load transforms
data := []byte(`{"id":"123","email":"encrypted-value","password":"hashed","token":"abc123"}`)
decoded, err := proc.Read(ctx, data)
if err != nil {
panic(err)
}
fmt.Printf("Decoded - Email: %s\n", decoded.Email)
// Encode: *T -> bytes with Send transforms
response, err := proc.Encode(ctx, &loaded)
if err != nil {
panic(err)
}
fmt.Printf("Encoded: %s\n", response)
}
What's Happening
receive.hash:"sha256"- Hashes password when receiving external inputstore.encrypt:"aes"- Encrypts email before storing to databaseload.decrypt:"aes"- Decrypts email when loading from databasesend.mask:"email"- Masks email (a***@example.com) in API responsessend.redact:"[REDACTED]"- Replaces token entirely in API responsesClone()- Returns a copy so the original is never modifiedcontext.Context- Flows through for observability integration
Performance Note
The processor uses reflection to apply transforms. For most use cases this is negligible (microseconds per operation). For performance-critical paths, implement override interfaces (Encryptable, Hashable, Maskable, Redactable) to bypass reflection entirely. See Escape Hatches.
Next Steps
- Concepts - Understand the boundary model
- Encryption Guide - Configure encryptors
- Masking Guide - Available mask types
- Tag Reference - Complete tag documentation