sealruntime

package
v0.72.0 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package sealruntime is the link-time seam between the messaging package and the payload-sealing codec (ADR-097). It mirrors internal/streamruntime (ADR-091): the codec registers itself from messaging/sealed's init, the app configures the runtime facts once at bootstrap, and messaging reads both without importing jose or the keystore — so a process that never imports messaging/sealed carries no sealing code. The app already links go-jose through HTTP jose; the import gate keeps `messaging` itself jose-free. It lives in the module's root internal tree because app and messaging both import it, so only go-bricks code can call Register and Configure; two indirect re-aim paths stay open (#2010, ADR-139).

Index

Constants

View Source
const (
	MeterName               = "github.com/gaborage/go-bricks/messaging/sealed"
	MetricOperationDuration = "seal.operation.duration"
	MetricOpenFailures      = "seal.open.failures.total"
	AttrOperation           = "seal.operation"
	AttrCode                = "seal.error.code"
	OpSeal                  = "seal"
	OpOpen                  = "open"
)

Metric names and attribute keys shared by the seal and open paths.

Variables

View Source
var ErrKeyStoreMissing = errors.New("messaging: a seal-tagged event type needs key material; register keystore.NewModule() before the declaring module")

ErrKeyStoreMissing fires when a seal-tagged type is declared and no keystore module is registered: key material is the sole trust act, so there is nothing to seal with.

View Source
var ErrNotConfigured = errors.New("messaging: sealing runtime not configured before declarations were collected")

ErrNotConfigured fires when a seal-tagged type is declared before the app configured the sealing runtime — a wiring order mistake, never a per-message condition.

View Source
var ErrNotLinked = errors.New(
	`a seal-tagged event type is declared but the sealing codec is not linked; import _ "github.com/gaborage/go-bricks/messaging/sealed"`)

ErrNotLinked fires at startup when a seal-tagged type is declared but the codec was never linked into the build.

Functions

func Configure

func Configure(rt *Runtime)

Configure records the runtime facts. The app is the single writer and calls it before collecting declarations; a later call replaces the facts (the app rebuilds them per process start, tests per case), and the seal instruments are rebuilt from the meter. The Activation selector is copied: a caller's later write to its own map can neither re-aim a generation after bootstrap nor race a declaration being collected.

func Register

func Register(c Codec)

Register installs the codec. messaging/sealed calls it from init; a second call panics.

func Reset

func Reset()

Reset clears codec, runtime and metrics. Tests only.

Types

type Codec

type Codec interface {
	ScanType(t reflect.Type) (Spec, error)
	NewSealer(spec Spec, eventType string, rt *Runtime) (Sealer, error)
}

Codec is what messaging/sealed registers. ScanType returns (nil, nil) for a type that carries no seal tags; NewSealer is the producer's startup fail-fast (Activation, roles) and returns a Sealer bound to the declaration's EventType.

func Registered

func Registered() Codec

Registered returns the installed codec, or nil when messaging/sealed is not linked.

type Envelope

type Envelope struct {
	JTI        string
	IssuedAt   time.Time
	EventType  string
	TenantID   string
	SignKid    string
	SignFamily string
	EncKid     string
}

Envelope is the verified signed envelope of one opened message — field-for-field the shape messaging exposes on Metadata, so the mapping is a struct conversion. Produced by messaging/sealed in #1359.

type KeyStore

type KeyStore interface {
	PublicKey(name string) (*rsa.PublicKey, error)
	PrivateKey(name string) (*rsa.PrivateKey, error)
}

KeyStore is the app.KeyStore subset sealing needs: stdlib RSA types only, so this seam names nothing from jose or keystore. The codec type-asserts the value for the family enumeration the keystore also implements.

type Metrics

type Metrics struct {
	// contains filtered or unexported fields
}

Metrics holds the seal instruments; nil-safe so an unconfigured process records nothing.

func Instruments

func Instruments() *Metrics

Instruments returns the configured instruments, or a no-op set before Configure ran.

func (*Metrics) RecordOpenFailure

func (m *Metrics) RecordOpenFailure(ctx context.Context, code string)

RecordOpenFailure counts one refused open by code (codes only — never a wire value).

func (*Metrics) RecordOperation

func (m *Metrics) RecordOperation(ctx context.Context, op string, d time.Duration)

RecordOperation records one seal or open duration.

type OpenRefusedError

type OpenRefusedError struct {
	Code        string
	Details     map[string]string
	Recoverable bool
	Cause       error
}

OpenRefusedError is a sealed delivery the opener refused: Code is the rule that fired, Details carries presence/length facts only (never a wire value), Recoverable marks the provisioning-recoverable unknown-generation case, and Cause keeps the codec's own error for errors.As downstream. Returned by messaging/sealed in #1359.

func (*OpenRefusedError) Error

func (e *OpenRefusedError) Error() string

Error renders the code and details only.

func (*OpenRefusedError) Unwrap

func (e *OpenRefusedError) Unwrap() error

Unwrap exposes the codec's error for errors.As / errors.Is.

type Opener

type Opener interface {
	Open(ctx context.Context, body []byte, want TenantRule, out any) (Envelope, error)
}

Opener turns one sealed delivery body back into the event (into out) and its verified Envelope, judging tid by want. Implemented by messaging/sealed in #1359.

type OpenerProvider

type OpenerProvider interface {
	NewOpener(spec Spec, eventType string, rt *Runtime) (Opener, error)
}

OpenerProvider is the OPTIONAL consume side of a registered Codec: the consumer door type-asserts Registered() to it. NewOpener is the consumer's startup fail-fast (families provisioned in the inherited roles) bound to the declaration's EventType. Implemented by messaging/sealed in #1359.

type Runtime

type Runtime struct {
	// KeyStore is the registered keystore, nil when no keystore module is registered.
	KeyStore KeyStore
	// Active is the messaging.seal.active selector: Logical kid -> "v<N>".
	Active map[string]string
	// Tenancy is the deployment's messaging tenancy as the opener must judge tid.
	Tenancy Tenancy
	// Meter feeds the seal metrics; nil means no-op instruments.
	Meter metric.MeterProvider
}

Runtime is what the app knows and the codec needs, fixed once at bootstrap.

func Configured

func Configured() *Runtime

Configured returns an independent snapshot of the runtime facts, or nil before Configure ran. The stored facts cannot be reached through the result.

type Sealer

type Sealer interface {
	Seal(ctx context.Context, evt any) (data []byte, jti string, err error)
}

Sealer turns one event of a declared type into its sealed wire bytes and the jti it signed.

type Spec

type Spec interface {
	SignLogical() string
	EncryptLogical() string
}

Spec is the codec's scanned declaration of one seal-tagged type, opaque here.

type Tenancy

type Tenancy uint8

Tenancy is the consume-side tenancy fact the opener's tid rule depends on (#1309 G2/G10).

const (
	// TenancyDisabled is multitenant.enabled: false — no tid rule, value surfaced only.
	TenancyDisabled Tenancy = iota
	// TenancyShared is multitenant.enabled + messaging.tenancy: shared — tid required
	// unless the consumer declares TenantOptional, and equality-checked against the carrier.
	TenancyShared
	// TenancyPerTenant is multitenant.enabled + per-tenant messaging — present-and-different
	// from the context tenant is poison, absent is accepted.
	TenancyPerTenant
)

func (Tenancy) String

func (t Tenancy) String() string

String names the tenancy for errors and logs.

type TenantRule

type TenantRule struct {
	Required bool
	Expected string
}

TenantRule is the tid expectation for one delivery, derived by the consumer door from Tenancy and the carrier: Required makes an absent signed tid poison; a non-empty Expected must equal a present tid. Implemented (derived) by messaging/sealed in #1359.

type Verifier

type Verifier interface {
	Verify(ctx context.Context, body []byte) (Envelope, error)
}

Verifier checks one stored sealed body against the producer's own declaration without decrypting it; a refusal is an *OpenRefusedError, and the tid is surfaced, never judged.

type VerifierProvider

type VerifierProvider interface {
	NewVerifier(spec Spec, eventType string, rt *Runtime) (Verifier, error)
}

VerifierProvider is the OPTIONAL producer-side verification of a registered Codec: the typed publish door type-asserts Registered() to it at declaration, and a codec without it fails only PublishSealed, never startup.

Jump to

Keyboard shortcuts

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