Tag Reference
Transforms are configured through boundary-specific struct tags.
Tag Format
boundary.operation:"algorithm"
- boundary - When the transform applies (
receive,load,store,send) - operation - What transform to apply (
hash,decrypt,encrypt,mask,redact) - algorithm - Which registered handler to use
Boundaries and Operations
| Boundary | Direction | Available Operations |
|---|---|---|
receive | External → App | hash |
load | Storage → App | decrypt |
store | App → Storage | encrypt |
send | App → External | mask, redact |
receive.hash
Hashes the field when receiving external input. One-way, not reversible.
type User struct {
Password string `receive.hash:"sha256"`
Token string `receive.hash:"argon2"`
}
Tag values:
| Value | Constant | Handler |
|---|---|---|
sha256 | HashSHA256 | Built-in |
sha512 | HashSHA512 | Built-in |
argon2 | HashArgon2 | Built-in |
bcrypt | HashBcrypt | Built-in |
Behavior:
- Hash field value
- Store result (hex or PHC format)
- Original value lost
store.encrypt
Encrypts the field when storing to database. Reversible with load.decrypt.
type User struct {
Email string `store.encrypt:"aes"`
SSN string `store.encrypt:"rsa"`
}
Tag values:
| Value | Constant | Handler |
|---|---|---|
aes | EncryptAES | Requires SetEncryptor |
rsa | EncryptRSA | Requires SetEncryptor |
envelope | EncryptEnvelope | Requires SetEncryptor |
Behavior:
- Encrypt field value
- Base64 encode for string fields
- Store ciphertext
Registration required:
enc, _ := cereal.AES(key)
proc.SetEncryptor(cereal.EncryptAES, enc)
load.decrypt
Decrypts the field when loading from storage. Reverses store.encrypt.
type User struct {
Email string `load.decrypt:"aes"`
}
Tag values: Same as store.encrypt.
Behavior:
- Base64 decode for string fields
- Decrypt ciphertext
- Restore plaintext
Common pattern: Pair with store.encrypt:
Email string `store.encrypt:"aes" load.decrypt:"aes"`
send.mask
Partially masks the field when sending to external destinations.
type User struct {
Email string `send.mask:"email"`
SSN string `send.mask:"ssn"`
Phone string `send.mask:"phone"`
}
Tag values:
| Value | Constant | Example Output |
|---|---|---|
ssn | MaskSSN | ***-**-6789 |
email | MaskEmail | a***@example.com |
phone | MaskPhone | (***) ***-4567 |
card | MaskCard | ************1111 |
ip | MaskIP | 192.168.xxx.xxx |
uuid | MaskUUID | 550e8400-****-****-... |
iban | MaskIBAN | GB82**************5432 |
name | MaskName | J*** S**** |
Behavior:
- Apply content-aware partial masking
- Original value preserved (not reversible)
- Built-in maskers require no registration
send.redact
Replaces the entire field value when sending to external destinations.
type User struct {
Password string `send.redact:"***"`
Token string `send.redact:"[REDACTED]"`
Secret string `send.redact:""`
}
Tag value: The replacement string (can be empty).
Behavior:
- Replace entire value with tag value
- Original value lost
- No registration required
Multiple Tags
Combine tags for different boundaries:
type User struct {
// Full lifecycle: encrypt for storage, decrypt on load, mask for API
Email string `store.encrypt:"aes" load.decrypt:"aes" send.mask:"email"`
// Hash on receive, redact on send
Password string `receive.hash:"argon2" send.redact:"***"`
}
Each tag applies at its specific boundary. They don't conflict.
Field Type Support
| Tag | string | []byte |
|---|---|---|
receive.hash | Yes | Yes |
store.encrypt | Yes | Yes |
load.decrypt | Yes | Yes |
send.mask | Yes | No |
send.redact | Yes | No |
For string fields, encrypted values are base64 encoded.
Nested Structs
Tags apply to nested struct fields:
type Address struct {
Street string `json:"street"`
City string `json:"city" send.redact:"[HIDDEN]"`
}
type User struct {
Name string `json:"name" send.mask:"name"`
Address Address `json:"address"` // City will be redacted
}
Both direct fields and nested fields are processed.
Pointer Fields
Tags work on pointer fields:
type User struct {
Email *string `send.mask:"email"`
}
Nil pointers are skipped.
Slice and Map Fields
Tags apply to elements within slices and maps:
type User struct {
Emails []string `send.mask:"email"`
Tags map[string]string `send.redact:"***"`
}
Each element is transformed individually.
Validation
Call Validate() to check all tags have registered handlers:
type User struct {
Email string `store.encrypt:"custom"` // Unknown algorithm
}
proc, _ := cereal.NewProcessor[User](json.New())
err := proc.Validate()
// err: missing encryptor for algorithm "custom" (field Email)
Validation checks:
store.encryptandload.decryptvalues have registered encryptorsreceive.hashvalues have registered hashers (sha256/sha512 built-in)send.maskvalues are valid built-in typessend.redactcan be any string (no validation)
Override Interface Bypass
When override interfaces are implemented, corresponding tags are ignored:
type User struct {
Email string `store.encrypt:"aes"` // Ignored if Encryptable implemented
}
func (u *User) Encrypt(encryptors map[cereal.EncryptAlgo]cereal.Encryptor) error {
// Your implementation
}
The interface takes full responsibility for that transform type.
Invalid Tag Handling
Unknown boundaries or operations are ignored:
type User struct {
Email string `unknown.operation:"value"` // Ignored
Name string `send.unknown:"value"` // Ignored
}
Only valid boundary.operation combinations are processed.