trix

package
v0.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 22, 2026 License: EUPL-1.2 Imports: 9 Imported by: 0

Documentation

Overview

Package trix implements the TRIX binary container format (RFC-0002).

The .trix format is a generic, protocol-agnostic container for storing arbitrary binary payloads alongside structured JSON metadata. It consists of:

[Magic Number (4)] [Version (1)] [Header Length (4)] [JSON Header] [Payload]

Key features:

  • Custom 4-byte magic number for application-specific identification
  • Extensible JSON header for metadata (content type, checksums, timestamps)
  • Optional integrity verification via configurable checksum algorithms
  • Integration with the Sigil transformation framework for encoding/compression

Example usage:

container := &trix.Trix{
    Header:  map[string]interface{}{"content_type": "text/plain"},
    Payload: []byte("Hello, World!"),
}
encoded, _ := trix.Encode(container, "MYAP", nil)

Index

Examples

Constants

View Source
const (
	// HeaderKeyEncrypted indicates whether the payload is encrypted.
	HeaderKeyEncrypted = "encrypted"
	// HeaderKeyAlgorithm stores the encryption algorithm used.
	HeaderKeyAlgorithm = "encryption_algorithm"
	// HeaderKeyEncryptedAt stores when the payload was encrypted.
	HeaderKeyEncryptedAt = "encrypted_at"
	// HeaderKeyObfuscator stores the obfuscator type used.
	HeaderKeyObfuscator = "obfuscator"

	// AlgorithmChaCha20Poly1305 is the identifier for ChaCha20-Poly1305.
	AlgorithmChaCha20Poly1305 = "xchacha20-poly1305"
	// ObfuscatorXOR identifies the XOR obfuscator.
	ObfuscatorXOR = "xor"
	// ObfuscatorShuffleMask identifies the shuffle-mask obfuscator.
	ObfuscatorShuffleMask = "shuffle-mask"
)
View Source
const (
	// Version is the current version of the .trix file format.
	// See RFC-0002 for version history and compatibility notes.
	Version = 2
	// MaxHeaderSize is the maximum allowed size for the header (16 MB).
	// This limit prevents denial-of-service attacks via large header allocations.
	MaxHeaderSize = 16 * 1024 * 1024 // 16 MB
)

Variables

View Source
var (
	// ErrNoEncryptionKey is returned when encryption is requested without a key.
	ErrNoEncryptionKey = errors.New("trix: encryption key not configured")
	// ErrAlreadyEncrypted is returned when trying to encrypt already encrypted data.
	ErrAlreadyEncrypted = errors.New("trix: payload is already encrypted")
	// ErrNotEncrypted is returned when trying to decrypt non-encrypted data.
	ErrNotEncrypted = errors.New("trix: payload is not encrypted")
)
View Source
var (
	// ErrInvalidMagicNumber is returned when the magic number is incorrect.
	ErrInvalidMagicNumber = errors.New("trix: invalid magic number")
	// ErrInvalidVersion is returned when the version is incorrect.
	ErrInvalidVersion = errors.New("trix: invalid version")
	// ErrMagicNumberLength is returned when the magic number is not 4 bytes long.
	ErrMagicNumberLength = errors.New("trix: magic number must be 4 bytes long")
	// ErrNilSigil is returned when a sigil is nil.
	ErrNilSigil = errors.New("trix: sigil cannot be nil")
	// ErrChecksumMismatch is returned when the checksum does not match.
	ErrChecksumMismatch = errors.New("trix: checksum mismatch")
	// ErrHeaderTooLarge is returned when the header size exceeds the maximum allowed.
	ErrHeaderTooLarge = errors.New("trix: header size exceeds maximum allowed")
)

Functions

func Encode

func Encode(trix *Trix, magicNumber string, w io.Writer) ([]byte, error)

Encode serializes a Trix struct into the .trix binary format. It returns the encoded data as a byte slice.

Example
package main

import (
	"fmt"
	"log"

	"forge.lthn.ai/Snider/Enchantrix/pkg/trix"
)

func main() {
	t := &trix.Trix{
		Header:  map[string]interface{}{"author": "Jules"},
		Payload: []byte("Hello, Trix!"),
	}
	encoded, err := trix.Encode(t, "TRIX", nil)
	if err != nil {
		log.Fatalf("Encode failed: %v", err)
	}
	fmt.Printf("Encoded data is not empty: %v\n", len(encoded) > 0)
}
Output:

Encoded data is not empty: true

Types

type CryptoConfig

type CryptoConfig struct {
	// Key is the 32-byte encryption key.
	Key []byte
	// Obfuscator type: "xor" (default) or "shuffle-mask"
	Obfuscator string
}

CryptoConfig holds encryption configuration for a Trix container.

type Trix

type Trix struct {
	// Header contains JSON-serializable metadata about the payload.
	Header map[string]interface{}
	// Payload is the binary data stored in the container.
	Payload []byte
	// InSigils lists sigil names to apply during Pack (forward transformation).
	InSigils []string `json:"-"`
	// OutSigils lists sigil names to apply during Unpack (reverse transformation).
	// If empty, InSigils is used in reverse order.
	OutSigils []string `json:"-"`
	// ChecksumAlgo specifies the hash algorithm for integrity verification.
	// If set, a checksum is computed and stored in the header during Encode.
	ChecksumAlgo crypt.HashType `json:"-"`
}

Trix represents a .trix container with header metadata and binary payload.

The Header field holds arbitrary JSON-serializable metadata. Common fields include:

  • content_type: MIME type of the original payload
  • created_at: ISO 8601 timestamp
  • encryption_algorithm: Algorithm used for encryption (if applicable)
  • checksum: Hex-encoded integrity checksum (auto-populated if ChecksumAlgo is set)

The InSigils and OutSigils fields specify transformation pipelines:

  • InSigils: Applied during Pack() in order (e.g., ["gzip", "base64"])
  • OutSigils: Applied during Unpack() in reverse order (defaults to InSigils)

func Decode

func Decode(data []byte, magicNumber string, r io.Reader) (*Trix, error)

Decode deserializes the .trix binary format into a Trix struct. It returns the decoded Trix struct. Note: Sigils are not stored in the format and must be re-attached by the caller.

Example
package main

import (
	"fmt"
	"log"

	"forge.lthn.ai/Snider/Enchantrix/pkg/trix"
)

func main() {
	t := &trix.Trix{
		Header:  map[string]interface{}{"author": "Jules"},
		Payload: []byte("Hello, Trix!"),
	}
	encoded, err := trix.Encode(t, "TRIX", nil)
	if err != nil {
		log.Fatalf("Encode failed: %v", err)
	}
	decoded, err := trix.Decode(encoded, "TRIX", nil)
	if err != nil {
		log.Fatalf("Decode failed: %v", err)
	}
	fmt.Printf("Decoded payload: %s\n", decoded.Payload)
	fmt.Printf("Decoded header: %v\n", decoded.Header)
}
Output:

Decoded payload: Hello, Trix!
Decoded header: map[author:Jules]

func NewEncryptedTrix

func NewEncryptedTrix(payload []byte, key []byte, header map[string]interface{}) (*Trix, error)

NewEncryptedTrix creates a new Trix container with an encrypted payload. This is a convenience function for creating encrypted containers in one step.

func (*Trix) DecryptPayload

func (t *Trix) DecryptPayload(config *CryptoConfig) error

DecryptPayload decrypts the Trix payload using the provided key.

The nonce is extracted from the ciphertext itself - no need to read it from the header separately.

func (*Trix) EncryptPayload

func (t *Trix) EncryptPayload(config *CryptoConfig) error

EncryptPayload encrypts the Trix payload using ChaCha20-Poly1305 with pre-obfuscation.

The nonce is embedded in the ciphertext itself and is NOT stored separately in the header. This is the production-ready approach (not demo-style).

Header metadata is updated to indicate encryption status without exposing cryptographic parameters that are already embedded in the ciphertext.

func (*Trix) GetEncryptionAlgorithm

func (t *Trix) GetEncryptionAlgorithm() string

GetEncryptionAlgorithm returns the encryption algorithm used, if any.

func (*Trix) IsEncrypted

func (t *Trix) IsEncrypted() bool

IsEncrypted returns true if the payload is currently encrypted.

func (*Trix) Pack

func (t *Trix) Pack() error

Pack applies the In method of all attached sigils to the payload. It modifies the Trix struct in place.

Example
package main

import (
	"fmt"
	"log"

	"forge.lthn.ai/Snider/Enchantrix/pkg/trix"
)

func main() {
	t := &trix.Trix{
		Payload:  []byte("secret message"),
		InSigils: []string{"base64", "reverse"},
	}
	err := t.Pack()
	if err != nil {
		log.Fatalf("Pack failed: %v", err)
	}
	fmt.Printf("Packed payload: %s\n", t.Payload)
}
Output:

Packed payload: =U2ZhN3cl1GI0VmcjV2c
Example (Checksum)
package main

import (
	"fmt"
	"log"

	"forge.lthn.ai/Snider/Enchantrix/pkg/crypt"
	"forge.lthn.ai/Snider/Enchantrix/pkg/trix"
)

func main() {
	t := &trix.Trix{
		Header:       map[string]interface{}{},
		Payload:      []byte("secret message"),
		InSigils:     []string{"base64", "reverse"},
		ChecksumAlgo: crypt.SHA256,
	}
	encoded, err := trix.Encode(t, "TRIX", nil)
	if err != nil {
		log.Fatalf("Encode failed: %v", err)
	}
	decoded, err := trix.Decode(encoded, "TRIX", nil)
	if err != nil {
		log.Fatalf("Decode failed: %v", err)
	}
	fmt.Printf("Decoded payload: %s\n", decoded.Payload)
	fmt.Printf("Checksum verified: %v\n", decoded.Header["checksum"] != nil)
}
Output:

Decoded payload: secret message
Checksum verified: true

func (*Trix) Unpack

func (t *Trix) Unpack() error

Unpack applies the Out method of all sigils in reverse order. It modifies the Trix struct in place.

Example
package main

import (
	"fmt"
	"log"

	"forge.lthn.ai/Snider/Enchantrix/pkg/trix"
)

func main() {
	t := &trix.Trix{
		Payload:   []byte("=U2ZhN3cl1GI0VmcjV2c"),
		OutSigils: []string{"base64", "reverse"},
	}
	err := t.Unpack()
	if err != nil {
		log.Fatalf("Unpack failed: %v", err)
	}
	fmt.Printf("Unpacked payload: %s\n", t.Payload)
}
Output:

Unpacked payload: secret message

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL