Integrating Fabrica-Util with Roma, Janus, Lares, and Senate: A Complete Guide

Fabrica-Util provides the foundational utility library that unifies runtime behavior across the go-pantheon ecosystem, requiring careful attention to initialization order, package modularity, and version alignment when integrating with Roma, Janus, Lares, and Senate.

Fabrica-Util serves as the core utility library for the go-pantheon microservices architecture, providing shared primitives for time handling, concurrency, cryptography, and distributed ID generation. When integrating this library with Roma (game-logic), Janus (gateway), Lares (account), and Senate (admin) services, developers must follow specific architectural contracts to ensure consistent behavior across the cluster. This guide examines the source code implementation to provide practical integration patterns for each component.

Package Modularity and Import Patterns

Fabrica-Util organizes functionality into discrete, well-typed packages that each service imports selectively. This modular design prevents bloat while maintaining unified runtime behavior across the go-pantheon ecosystem.

Time Handling with xtime

The xtime package provides timezone-aware timestamp operations and period calculations. Roma relies on this for deterministic daily and weekly game resets, while Janus uses it for request timestamp normalization. Lares leverages xtime for time-based token expiration, and Senate schedules maintenance windows using these utilities.

Import path: github.com/go-pantheon/fabrica-util/xtime

Concurrency Primitives with xsync

The xsync package contains delayers, futures, and goroutine lifecycle helpers. Janus manages per-connection timeouts using NewDelayer, Roma spawns background workers with Go helpers, Senate runs periodic jobs, and Lares implements cancellable async flows. The Future type supports context-based cancellation via GetWithContext as implemented in xsync/future.go【L58-L70】.

Import path: github.com/go-pantheon/fabrica-util/xsync

Distributed Identity with xid

The xid package generates zone-aware identifiers using HashID encoding. All services must reference the same player identifier format, with zone bits guaranteeing global uniqueness across data centers. The BuildUID function in xid/id.go【L58-L79】 constructs these identifiers, while EncodeID and DecodeID handle obfuscation.

Import path: github.com/go-pantheon/fabrica-util/xid

Cryptographic Utilities

The security hierarchy provides AES-GCM, RSA, ECDH, and X.509 handling. Lares encrypts authentication tokens, Roma encrypts persisted game state snapshots, Janus secures payloads, and Senate protects configuration files. The security/aes package validates key sizes during NewAESCipher initialization to ensure cross-service compatibility【L20-L27】.

Import path: github.com/go-pantheon/fabrica-util/security/aes

Data Structures for Distributed Systems

Specialized packages support distributed architectures:

  • consistenthash: Provides the hash ring implementation used by Roma for player sharding
  • bloom: Supplies probabilistic filters for Janus anti-spam detection
  • bitmap: Offers compact bit-set storage for Senate flag management

Initialization Order and Configuration

Most Fabrica-Util packages require explicit initialization before use. Skipping these steps causes fallback to safe defaults (such as UTC for time), which may produce inconsistent results across the cluster.

Critical Initialization Sequence

  1. xtime.Init: Must execute first in any binary handling timestamps. The function in xtime/time.go【L22-L53】 parses configuration, loads the location atomically, and falls back to default locale settings. Roma, Janus, and Lares must all call this during startup.

  2. xid Configuration: While the package initializes automatically via init() blocks, services must agree on identical zoneBit and zoneSlotBit layouts to ensure binary-compatible ID generation.

  3. Security Setup: Cryptographic packages like security/aes require explicit cipher creation through NewAESCipher, which validates key sizes for cross-service compatibility.

Error Handling and Context Propagation

All utilities integrate with the project-wide errors wrapper, preserving stack traces and supporting service-wide error policies such as gRPC status conversion. Concurrency primitives expose cancellation through Go's context package.

The Future type provides GetWithContext for deadline propagation, allowing services to cancel asynchronous operations without additional boilerplate【L58-L70】. Similarly, the Delayer supports explicit cancellation methods alongside its timer-based Wait() channel.

Thread Safety and Performance Characteristics

Fabrica-Util implements lock-free and low-contention patterns suitable for high-frequency operations:

  • Atomic location storage in xtime guarantees lock-free reads after initialization using atomic value storage【L12-L15】
  • Delayer uses a single time.Timer protected by an RW-mutex, with buffered channels to prevent caller blocking【L45-L50】【L71-L78】
  • Future and Then are fully generic, avoiding unnecessary allocations suitable for Roma's game loops

Version Compatibility and Dependency Management

Fabrica-Util targets Go 1.24+ across all packages. Services must align go.mod dependencies to the same vX.Y.Z tag; mismatched versions can break binary-compatible hashid layouts used by xid.

The repository enforces a strict license policy permitting only MIT, Apache-2.0, BSD, ISC, and MPL-2.0 dependencies, preventing legal complications when shipping services【L13-L22】.

Practical Integration Examples

Initializing Time Utilities in Roma and Janus

import (
	"log"

	"github.com/go-pantheon/fabrica-util/xtime"
)

func initTime() {
	if err := xtime.Init(xtime.Config{
		Language: "en",
		Timezone: "Asia/Shanghai",
	}); err != nil {
		log.Fatalf("time init failed: %v", err)
	}
}

This initialization pattern appears in xtime/time.go【L22-L53】, loading the timezone location atomically for subsequent operations.

Generating Zone-Based IDs for Lares and Roma

import (
	"fmt"

	"github.com/go-pantheon/fabrica-util/xid"
)

func playerIDExample() {
	uid, _ := xid.BuildUID(12345, 3)          // zone 3
	enc, _ := xid.EncodeID(uid)               // hashid string
	fmt.Println("Encoded:", enc)

	dec, _ := xid.DecodeID(enc)
	gameID, zone := xid.SplitUID(dec)
	fmt.Printf("Decoded gameID=%d zone=%d\n", gameID, zone)
}

The xid/id.go implementation【L58-L79】 provides zone-aware construction and HashID encoding.

Managing Connection Timeouts in Janus

import (
	"fmt"
	"time"

	"github.com/go-pantheon/fabrica-util/xsync"
)

func connTimeoutDemo() {
	d := xsync.NewDelayer()
	expireAt := time.Now().Add(5 * time.Second)
	d.SetExpiryTime(expireAt)

	fmt.Println("Waiting for expiry…")
	<-d.Wait() // blocks until timer fires
	fmt.Println("Connection timed out")
}

The Delayer implementation in xsync/delayer.go uses buffered channels and RW-mutex protection【L45-L50】【L71-L78】.

Chaining Asynchronous Operations in Roma

import (
	"context"
	"fmt"
	"time"

	"github.com/go-pantheon/fabrica-util/xsync"
)

func asyncChain() {
	f := xsync.NewFuture[int]()
	go func() {
		// simulate work
		f.Complete(42, nil)
	}()

	// Transform the result
	f2 := xsync.Then[int, string](f, func(v int) (string, error) {
		return fmt.Sprintf("value=%d", v), nil
	})

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	s, err := f2.GetWithContext(ctx)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println("final:", s) // -> "final: value=42"
}

The Then function and context-aware retrieval appear in xsync/future.go【L58-L70】【L93-L112】.

Encrypting Sensitive Data Across Services

import (
	"fmt"

	"github.com/go-pantheon/fabrica-util/security/aes"
)

func encryptDemo() {
	key := []byte("0123456789abcdef0123456789abcdef") // 32-byte key
	c, _ := aes.NewAESCipher(key)

	plain := []byte("super-secret")
	ciphertext, _ := c.Encrypt(plain)

	fmt.Printf("cipher: %x\n", ciphertext)

	dec, _ := c.Decrypt(ciphertext)
	fmt.Println("decrypted:", string(dec))
}

The AES-GCM implementation in security/aes/aes.go validates key sizes during initialization【L20-L38】.

Key Source Files and Architecture

File Package Role
xtime/time.go xtime Timezone handling and atomic location storage
xsync/delayer.go xsync Timer-based expiry with RW-mutex protection
xsync/future.go xsync Generic async results with context cancellation
xid/id.go xid Zone-aware ID construction and HashID encoding
security/aes/aes.go security/aes AES-GCM encryption with PKCS7 padding
consistenthash/* consistenthash Distributed hash ring for Roma sharding
bloom/* bloom Probabilistic filters for Janus spam detection
bitmap/* bitmap Compact bit-sets for Senate flag storage

Summary

  • Initialize xtime first: Call xtime.Init before any timestamp operations to ensure timezone consistency across Roma, Janus, and Lares
  • Align zone configurations: Ensure identical zoneBit layouts in xid across all services to maintain ID compatibility
  • Use context propagation: Leverage GetWithContext in xsync primitives to respect request deadlines without boilerplate
  • Verify Go versions: Target Go 1.24+ and align Fabrica-Util versions across all services to prevent HashID incompatibilities
  • Respect thread-safety: Rely on atomic operations in xtime and buffered channels in Delayer for high-concurrency scenarios

Frequently Asked Questions

What happens if xtime.Init is not called before using time functions?

Functions will fall back to safe defaults such as UTC, but this produces inconsistent results across the cluster. According to the implementation in xtime/time.go, the Init function loads the timezone location atomically; without it, services may calculate different daily reset times or token expirations.

How do I ensure ID compatibility between different go-pantheon services?

All services must use the same zoneBit and zoneSlotBit configuration when calling xid functions. The BuildUID and DecodeID functions in xid/id.go rely on these bit layouts; mismatched configurations between Roma and Lares will cause player ID collisions or decoding failures.

Is Fabrica-Util thread-safe for high-concurrency scenarios?

Yes. The library uses atomic value storage for timezone locations in xtime, RW-mutexes for the Delayer timer state, and lock-free patterns in Future implementations. These designs support high-frequency operations in Roma's game loops and Janus's connection handling without contention.

What Go version is required to use Fabrica-Util?

Fabrica-Util targets Go 1.24+ across all packages. The repository enforces this requirement to utilize modern language features and maintain compatibility. Services must update their go.mod files accordingly, as older versions may not support the generic implementations in xsync or the atomic patterns in xtime.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →