Documentation
¶
Overview ¶
Package horos provides the HOROS type system: typed service contracts, codec-agnostic serialization, structured errors, and a wire envelope format for inter-service communication.
The type system sits above connectivity.Handler (bytes in, bytes out) and provides compile-time safety for what travels inside those bytes.
Index ¶
- Constants
- Variables
- func IsChecksumError(err error) bool
- func Unwrap(data []byte) (formatID uint16, payload []byte, err error)
- func Wrap(formatID uint16, payload []byte) ([]byte, error)
- type Codec
- type Contract
- type Encoder
- type ErrChecksum
- type ErrDecode
- type ErrEncode
- type ErrUnsupportedFormat
- type FormatInfo
- type Registry
- type ServiceError
- func (e ServiceError) Decode(data []byte) (ServiceError, error)
- func (e ServiceError) Encode() ([]byte, error)
- func (e *ServiceError) Error() string
- func (e *ServiceError) Is(target error) bool
- func (e *ServiceError) WithDetails(v any) (*ServiceError, error)
- func (e *ServiceError) WithService(service string) *ServiceError
Constants ¶
const ( // HeaderSize is the fixed overhead of the wire envelope. HeaderSize = 6 // 2 (format_id) + 4 (checksum) // FormatRaw is passthrough — no codec, payload is used as-is. FormatRaw uint16 = 0 // FormatJSON is the JSON codec format ID. // JSON is format 1: the canonical, human-readable wire format. // Use for debugging, external consumers, or when readability matters. FormatJSON uint16 = 1 // FormatMsgp is the MessagePack codec format ID. // Msgpack is format 2: the performance-optimized wire format for // Go-to-Go inter-service communication. Keys are in cleartext in // the encoding, so it remains inspectable unlike protobuf. FormatMsgp uint16 = 2 )
const Schema = `` /* 163-byte string literal not displayed */
Schema is the SQLite DDL for the formats table. This table mirrors the Go-side registry for observability, admin UI, and format negotiation.
Variables ¶
var ( ErrNotFound = &ServiceError{Code: "NOT_FOUND"} ErrBadRequest = &ServiceError{Code: "BAD_REQUEST"} ErrInternal = &ServiceError{Code: "INTERNAL"} ErrRateLimited = &ServiceError{Code: "RATE_LIMITED"} ErrForbidden = &ServiceError{Code: "FORBIDDEN"} ErrConflict = &ServiceError{Code: "CONFLICT"} )
Common error codes as pre-built ServiceError values for use with errors.Is.
Functions ¶
func IsChecksumError ¶
IsChecksumError returns true if the error is a checksum mismatch.
Types ¶
type Codec ¶
Codec defines how a type serializes itself to and from bytes. The F-bounded constraint T Codec[T] ensures that Decode returns the concrete type, not an interface — zero runtime casts, zero panics.
The F-bounded constraint T Codec[T] has been supported since Go 1.18 (generics introduction).
type Contract ¶
type Contract[Req Codec[Req], Resp Codec[Resp]] struct { // Service is the name used for routing in connectivity.Router. Service string // FormatID identifies the wire format (1=JSON, 2=msgpack). // When zero, the registry default is used. FormatID uint16 }
Contract is a typed service contract: it binds a request type and a response type together with a service name. Both Req and Resp must be self-codecs.
A Contract is metadata — it carries no state and exists to give the compiler enough information to type-check service calls at build time.
func NewContract ¶
NewContract creates a contract for a service with the default wire format.
func (Contract[Req, Resp]) Call ¶
func (c Contract[Req, Resp]) Call( ctx context.Context, caller func(ctx context.Context, service string, payload []byte) ([]byte, error), req Req, ) (Resp, error)
Call encodes req, dispatches via the provided caller function, and decodes the response. The caller is typically connectivity.Router.Call or a test stub.
This is the typed entry point: callers get compile-time guarantees on both the request and response types.
func (Contract[Req, Resp]) Handler ¶
func (c Contract[Req, Resp]) Handler( fn func(ctx context.Context, req Req) (Resp, error), ) func(ctx context.Context, payload []byte) ([]byte, error)
Handler returns a connectivity-compatible handler (bytes in, bytes out) from a typed endpoint function. This is the server side of a contract.
func (Contract[Req, Resp]) WithFormat ¶
WithFormat returns a copy of the contract using the specified format ID.
type Encoder ¶
Encoder writes a value to bytes. This is the write-only side of Codec, useful when a function only needs to serialize (e.g. building a request).
type ErrChecksum ¶
ErrChecksum is returned when the wire envelope checksum doesn't match.
func (*ErrChecksum) Error ¶
func (e *ErrChecksum) Error() string
type ErrUnsupportedFormat ¶
type ErrUnsupportedFormat struct {
FormatID uint16
}
ErrUnsupportedFormat is returned when a format ID has no registered codec.
func (*ErrUnsupportedFormat) Error ¶
func (e *ErrUnsupportedFormat) Error() string
type FormatInfo ¶
type FormatInfo struct {
// ID is the format identifier used in the wire envelope header.
ID uint16
// Name is a human-readable label (e.g. "json", "msgpack", "protobuf").
Name string
// MIME is the content type (e.g. "application/json", "application/msgpack").
MIME string
}
FormatInfo describes a registered wire format.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps format IDs to human-readable names and tracks which formats are available. The Go-side registry is the source of truth for codec dispatch; the SQLite table is for observability and admin tooling.
Registry is safe for concurrent use.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry creates a registry with the built-in formats pre-registered.
func (*Registry) All ¶
func (r *Registry) All() []FormatInfo
All returns a snapshot of all registered formats.
func (*Registry) Lookup ¶
func (r *Registry) Lookup(id uint16) (FormatInfo, bool)
Lookup returns the format info for the given ID, or false if not found.
func (*Registry) Register ¶
func (r *Registry) Register(info FormatInfo) error
Register adds a format to the registry. Returns an error if the ID is already registered with a different name (prevents accidental collisions).
type ServiceError ¶
type ServiceError struct {
// Code is a machine-readable error code (e.g. "NOT_FOUND", "RATE_LIMITED").
Code string `json:"code"`
// Message is a human-readable description.
Message string `json:"message"`
// Details is optional structured data for the error (retry-after, field
// validation errors, etc.). It is codec-dependent: JSON for now.
Details json.RawMessage `json:"details,omitempty"`
// Service is the originating service name (set by the handler).
Service string `json:"service,omitempty"`
}
ServiceError is a structured error that travels across the wire. Unlike Go's error interface (a string), ServiceError carries a machine-readable code, a human message, and optional typed details — all serializable.
ServiceError implements Codec[ServiceError] so it can be encoded in the same wire envelope as any other payload.
func DetectError ¶
func DetectError(payload []byte) (*ServiceError, bool)
DetectError checks if a payload contains a ServiceError (by looking for the __error sentinel). Returns the error and true if found.
func NewServiceError ¶
func NewServiceError(code, message string) *ServiceError
NewServiceError creates a ServiceError with the given code and message.
func ToServiceError ¶
func ToServiceError(err error) *ServiceError
ToServiceError converts any error to a ServiceError. If the error is already a *ServiceError, it is returned as-is. Otherwise, a generic INTERNAL error is created.
func (ServiceError) Decode ¶
func (e ServiceError) Decode(data []byte) (ServiceError, error)
Decode deserializes a ServiceError from JSON.
func (ServiceError) Encode ¶
func (e ServiceError) Encode() ([]byte, error)
Encode serializes the ServiceError to JSON with the __error sentinel.
func (*ServiceError) Error ¶
func (e *ServiceError) Error() string
Error implements the error interface.
func (*ServiceError) Is ¶
func (e *ServiceError) Is(target error) bool
Is supports errors.Is matching by code.
func (*ServiceError) WithDetails ¶
func (e *ServiceError) WithDetails(v any) (*ServiceError, error)
WithDetails returns a copy of the error with JSON details attached. Returns an error if v cannot be marshaled to JSON.
func (*ServiceError) WithService ¶
func (e *ServiceError) WithService(service string) *ServiceError
WithService returns a copy of the error with the service name set.