stream

package
v1.10.0 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package stream defines the hash chain stream entity and store interface.

What is a Stream?

A Stream is the head pointer of an independent SHA-256 hash chain. Every app+tenant combination has exactly one stream. All audit events recorded within that scope are appended to the same chain in monotonically increasing sequence order.

Stream fields:

  • ID — TypeID with "stream_" prefix, globally unique
  • AppID — the application this stream belongs to
  • TenantID — the tenant within the application (empty for single-tenant apps)
  • HeadHash — SHA-256 hash of the most recently appended event
  • HeadSeq — sequence number of the most recently appended event

Hash Chain Mechanics

When an event is appended, Chronicle:

  1. Reads HeadHash and HeadSeq from the stream.
  2. Sets the event's PrevHash = HeadHash and Sequence = HeadSeq + 1.
  3. Computes the event's Hash over: prevHash|timestamp|action|resource| category|resourceID|outcome|severity|metadata_json (sorted keys).
  4. Persists the event.
  5. Updates the stream: HeadHash = event.Hash, HeadSeq = event.Sequence.

For the very first event in a stream, PrevHash is an empty string. Any modification of a past event changes its hash, breaking every subsequent hash in the chain — making tampering cryptographically detectable.

Multi-Tenant Isolation

Each app+tenant pair has its own independent chain. This means:

  • Cross-tenant queries are structurally impossible (no shared sequence space).
  • Compliance reports, verification, and retention are always scoped.
  • The Store.GetStreamByScope method retrieves or creates the stream for a given (appID, tenantID) pair.

Import Cycle Note

The root chronicle package cannot import stream (that would create a cycle). Instead, chronicle defines its own chronicle.StreamInfo type mirroring the fields that the record pipeline needs (ID, AppID, TenantID, HeadHash, HeadSeq). [store.NewAdapter] translates between Stream and chronicle.StreamInfo so both sides remain decoupled.

Store

Store manages stream lifecycle:

Package stream defines the hash chain stream entity and store interface.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ListOpts

type ListOpts struct {
	Limit  int
	Offset int
}

ListOpts defines pagination options for listing streams.

type Store

type Store interface {
	// CreateStream initializes a new hash chain stream.
	CreateStream(ctx context.Context, s *Stream) error

	// GetStream returns a stream by ID.
	GetStream(ctx context.Context, streamID id.ID) (*Stream, error)

	// GetStreamByScope returns the stream for a given app+tenant scope.
	GetStreamByScope(ctx context.Context, appID, tenantID string) (*Stream, error)

	// ListStreams returns all streams.
	ListStreams(ctx context.Context, opts ListOpts) ([]*Stream, error)

	// UpdateStreamHead updates the stream's head hash and sequence after append.
	UpdateStreamHead(ctx context.Context, streamID id.ID, hash string, seq uint64) error

	// UpdateStreamScheme moves the stream's digest pin to scheme, applying from
	// sequence since.
	//
	// It exists so turning a stronger digest on actually takes effect on streams
	// that already exist. Without it the pin written at creation is the only pin
	// the stream ever has, and a chain that is now being written under a keyed
	// scheme keeps advertising the weaker one, which is exactly the claim a
	// downgrade check needs to be true. Callers must only ever move the pin
	// upward; see chronicle.Chronicle's resolveStream.
	UpdateStreamScheme(ctx context.Context, streamID id.ID, scheme string, since uint64) error
}

Store manages hash chain streams.

type Stream

type Stream struct {
	chronicle.Entity
	ID       id.ID  `json:"id"`
	AppID    string `json:"app_id"`
	TenantID string `json:"tenant_id"`
	HeadHash string `json:"head_hash"`
	HeadSeq  uint64 `json:"head_seq"`

	// Scheme is the digest scheme this stream is pinned to, and SchemeSince the
	// first sequence it applies from. Events below SchemeSince predate the pin.
	Scheme      string `json:"scheme"`
	SchemeSince uint64 `json:"scheme_since"`
}

Stream represents a hash chain stream, scoped to an app+tenant combination. Each stream maintains its own independent chain.

Jump to

Keyboard shortcuts

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