Help us shape the future of Go! Take the 2026 Go Developer Survey

b5session

package
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package b5session implements the feature-off B5 S3 coordination contracts: a strongly-consistent session directory, continuation capabilities, shared connection and plan admission, durable dial identities, quarantine, and the owner-crash reaper.

The package is deliberately not imported by a production MCP/HTTP handler. It owns metadata only and never receives a business SQL executor.

Index

Constants

View Source
const MaxTombstoneTerminalCodeBytes = 96
View Source
const PostgreSQLSchema = `` /* 4324-byte string literal not displayed */

PostgreSQLSchema is intentionally package-owned and feature-off. It is not registered in the application's production migration stream. S10 can move the reviewed DDL into that stream without changing this ledger contract.

Variables

View Source
var (
	ErrInvalidDirectoryConfig = errors.New("b5session: invalid directory configuration")
	ErrInvalidContinuation    = errors.New("b5session: invalid continuation request")
)
View Source
var (
	ErrAdmissionDenied      = errors.New("b5session: shared admission denied")
	ErrCASConflict          = errors.New("b5session: generation compare-and-swap conflict")
	ErrInvalidTransition    = errors.New("b5session: invalid lease transition")
	ErrUnknownProofSchema   = errors.New("b5session: unknown proof schema")
	ErrBackendStillPresent  = errors.New("b5session: backend still present")
	ErrInventoryUnavailable = errors.New("b5session: backend inventory unavailable")
)
View Source
var ErrInvalidDialIdentity = errors.New("b5session: invalid dial permit identity")

Functions

func DigestSessionID

func DigestSessionID(sessionID string) [32]byte

func InstallPostgresSchema

func InstallPostgresSchema(ctx context.Context, db *sql.DB) error

func SignContinuation

func SignContinuation(secret string, sessionID, method, requestID string, ownerEpoch uint64, expectedSeq *uint64, bodyDigest [32]byte) (string, error)

Types

type AESGCMSealer

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

func NewAESGCMSealer

func NewAESGCMSealer(keyID string, key []byte) (*AESGCMSealer, error)

func (*AESGCMSealer) KeyID

func (s *AESGCMSealer) KeyID() string

func (*AESGCMSealer) Open

func (s *AESGCMSealer) Open(encoded string, aad []byte) ([]byte, error)

func (*AESGCMSealer) Seal

func (s *AESGCMSealer) Seal(plaintext, aad []byte) (string, error)

type AbsenceProof

type AbsenceProof struct {
	SchemaID       string
	SchemaVersion  uint16
	Digest         [32]byte
	InventoryEpoch uint64
	ObservedAt     time.Time
}

func NewAbsenceProof

func NewAbsenceProof(epoch uint64, observed time.Time, material []byte) AbsenceProof

type BackendIdentity

type BackendIdentity struct {
	ServerID       string
	Database       string
	PID            int32
	BackendStarted time.Time
	DialToken      string
}

func (BackendIdentity) Complete

func (identity BackendIdentity) Complete() bool

func (BackendIdentity) Equal

func (identity BackendIdentity) Equal(other BackendIdentity) bool

type BackendInventory

type BackendInventory interface {
	ObservePermit(context.Context, string) (InventoryObservation, error)
	Terminate(context.Context, BackendIdentity) error
}

type Budget

type Budget struct {
	DatasourceID          string
	HardLimit             int
	AdminEmergencyReserve int
	ConnectionUnitsUsed   int
	PlanBytesLimit        int64
	PlanBytesUsed         int64
	Revision              int64
}

type Claim

type Claim struct {
	ID, DatasourceID                  string
	Kind                              ClaimKind
	OwnerInstanceID, OwnerIncarnation string
	Generation                        uint64
	ClaimedCapacity, MaxOpenConns     int
	State                             ClaimState
	Revision                          int64
}

type ClaimKind

type ClaimKind string
const (
	ClaimPoolEnvelope ClaimKind = "POOL_ENVELOPE"
	ClaimDirectPinned ClaimKind = "DIRECT_PINNED"
	ClaimMigration    ClaimKind = "MIGRATION"
	ClaimHealth       ClaimKind = "HEALTH"
	ClaimAdmin        ClaimKind = "ADMIN"
)

type ClaimRequest

type ClaimRequest struct {
	ClaimID, DatasourceID             string
	Kind                              ClaimKind
	OwnerInstanceID, OwnerIncarnation string
	Capacity, MaxOpenConns            int
}

type ClaimState

type ClaimState string
const (
	ClaimActive   ClaimState = "ACTIVE"
	ClaimFenced   ClaimState = "FENCED"
	ClaimReleased ClaimState = "RELEASED"
)

type ContinuationInput

type ContinuationInput struct {
	AgentSQLSessionID string
	McpSessionID      string
	PrincipalID       string
	InstanceID        string
	Method            string
	RequestID         string
	OwnerEpoch        uint64
	ExpectedSeq       *uint64
	BodyDigest        [32]byte
	Proof             string
}

ContinuationInput intentionally has separate MCP and AgentSQL identifiers. McpSessionID is transport metadata only and is never a fallback lookup key.

type CreateSession

type CreateSession struct {
	AgentID, TenantID, PrincipalID string
	OwnerInstanceID, StickyRoute   string
}

type CreatedSession

type CreatedSession struct {
	Session store.B5Session
	// ContinuationSecret is returned exactly once and is never persisted.
	ContinuationSecret string
}

type DialIdentity

type DialIdentity struct {
	PermitID        string
	ApplicationName string
	OwnerHash       [8]byte
}

type DialIdentityCodec

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

DialIdentityCodec produces a PostgreSQL application_name below the 63-byte NAMEDATALEN limit. The field carries a random durable permit id, a digest of the owner incarnation, and a truncated HMAC. Inventory accepts it only after verifying the HMAC and expected incarnation.

func NewDialIdentityCodec

func NewDialIdentityCodec(key []byte) (*DialIdentityCodec, error)

func (*DialIdentityCodec) New

func (c *DialIdentityCodec) New(ownerIncarnation string) (DialIdentity, error)

func (*DialIdentityCodec) Verify

func (c *DialIdentityCodec) Verify(applicationName, ownerIncarnation string) (DialIdentity, error)

type DialPermitIssuer

type DialPermitIssuer struct {
	Ledger LedgerStore
	Codec  *DialIdentityCodec
}

func (DialPermitIssuer) Issue

func (issuer DialPermitIssuer) Issue(ctx context.Context, claimID string, generation uint64) (Lease, DialIdentity, error)

type Directory

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

func NewDirectory

func NewDirectory(config DirectoryConfig) (*Directory, error)

func (*Directory) Close

func (*Directory) Create

func (d *Directory) Create(ctx context.Context, input CreateSession) (CreatedSession, error)

func (*Directory) Expire

func (d *Directory) Expire(ctx context.Context, limit int) (int, error)

func (*Directory) Lookup

func (d *Directory) Lookup(ctx context.Context, input ContinuationInput) (RouteDecision, error)

func (*Directory) String

func (d *Directory) String() string

func (*Directory) Touch

func (*Directory) TransferOwner

func (d *Directory) TransferOwner(ctx context.Context, input ContinuationInput, newOwner, newRoute string) (store.B5Session, error)

TransferOwner increments the owner epoch exactly once. The request is authenticated against the old owner/epoch; the same key is re-sealed with new-epoch AAD before the atomic directory CAS.

type DirectoryConfig

type DirectoryConfig struct {
	Store       SessionStore
	Sealer      KeySealer
	IdleTTL     time.Duration
	AbsoluteTTL time.Duration
	Now         func() time.Time
}

type DispositionProof

type DispositionProof struct {
	SchemaID       string
	SchemaVersion  uint16
	Disposition    b5.ConnectionDisposition
	Digest         [32]byte
	PoolTransfer   bool
	BackendAbsence bool
	InventoryEpoch uint64
	SourceDigest   [32]byte
}

func DispositionProofFromTerminal

func DispositionProofFromTerminal(resolution TerminalDisposition, sourceDigest [32]byte, poolTransfer, backendAbsence bool, inventoryEpoch uint64) (DispositionProof, error)

DispositionProofFromTerminal is the only normal constructor. It consumes the frozen b5terminal result instead of allowing the pool layer to infer a disposition from an error string.

type InventoryObservation

type InventoryObservation struct {
	Epoch      uint64
	ObservedAt time.Time
	Backends   []BackendIdentity
}

type KeySealer

type KeySealer interface {
	Seal(plaintext, aad []byte) (string, error)
	Open(ciphertext string, aad []byte) ([]byte, error)
	KeyID() string
}

KeySealer keeps the continuation key encrypted in the directory. AAD is canonical and includes the owner epoch, so an owner transfer must re-seal in the same CAS that increments the epoch.

type Lease

type Lease struct {
	ID, ClaimID, DatasourceID         string
	OwnerInstanceID, OwnerIncarnation string
	ClaimGeneration, Generation       uint64
	DialPermitID, DialToken           string
	DialAttempted                     bool
	State                             LeaseState
	Backend                           BackendIdentity
	Disposition                       b5.ConnectionDisposition
	ProofSchemaID                     string
	ProofSchemaVersion                uint16
	ProofDigest                       [32]byte
	InventoryEpoch                    uint64
	Charged                           bool
	Revision                          int64
}

type LeaseState

type LeaseState string
const (
	LeaseDialPermitOutstanding LeaseState = "DIAL_PERMIT_OUTSTANDING"
	LeaseInUse                 LeaseState = "IN_USE"
	LeaseTerminating           LeaseState = "TERMINATING"
	LeaseQuarantined           LeaseState = "QUARANTINED"
	LeaseFree                  LeaseState = "FREE"
	LeaseNeverAllocated        LeaseState = "NEVER_ALLOCATED"
	LeaseBackendAbsent         LeaseState = "BACKEND_ABSENT_CONFIRMED"
)

type LedgerStore

type LedgerStore interface {
	ConfigureBudget(context.Context, Budget) (Budget, error)
	Budget(context.Context, string) (Budget, error)
	AcquireClaim(context.Context, ClaimRequest) (Claim, error)
	Claim(context.Context, string) (Claim, error)
	ReservePlan(context.Context, PlanLease) error
	ReleasePlan(context.Context, string) error
	IssueDialPermit(context.Context, string, uint64, string, string) (Lease, error)
	MarkDialStarted(context.Context, string, uint64) (Lease, error)
	BindBackend(context.Context, string, uint64, BackendIdentity) (Lease, error)
	AcquireFree(context.Context, string, uint64, string, string) (Lease, error)
	BeginTermination(context.Context, string, uint64) (Lease, error)
	ApplyDisposition(context.Context, string, uint64, DispositionProof) (Lease, error)
	FenceOwner(context.Context, string, string) ([]Claim, error)
	ListClaimLeases(context.Context, string) ([]Lease, error)
	AbandonUndialed(context.Context, string, uint64, [32]byte) (Lease, error)
	ReconcileAbsence(context.Context, string, uint64, AbsenceProof) (Lease, error)
	ShrinkFencedClaim(context.Context, string, uint64) (Claim, error)
}

LedgerStore is the complete durable S3 surface. Implementations must linearize admission and every generation transition in their shared store.

type MemoryLedger

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

func NewMemoryLedger

func NewMemoryLedger() *MemoryLedger

func (*MemoryLedger) AbandonUndialed

func (l *MemoryLedger) AbandonUndialed(_ context.Context, id string, generation uint64, digest [32]byte) (Lease, error)

func (*MemoryLedger) AcquireClaim

func (l *MemoryLedger) AcquireClaim(_ context.Context, request ClaimRequest) (Claim, error)

func (*MemoryLedger) AcquireFree

func (l *MemoryLedger) AcquireFree(_ context.Context, id string, generation uint64, owner, incarnation string) (Lease, error)

func (*MemoryLedger) ApplyDisposition

func (l *MemoryLedger) ApplyDisposition(_ context.Context, id string, generation uint64, proof DispositionProof) (Lease, error)

func (*MemoryLedger) BeginTermination

func (l *MemoryLedger) BeginTermination(_ context.Context, id string, generation uint64) (Lease, error)

func (*MemoryLedger) BindBackend

func (l *MemoryLedger) BindBackend(_ context.Context, id string, generation uint64, backend BackendIdentity) (Lease, error)

func (*MemoryLedger) Budget

func (l *MemoryLedger) Budget(_ context.Context, datasource string) (Budget, error)

func (*MemoryLedger) Claim

func (l *MemoryLedger) Claim(_ context.Context, id string) (Claim, error)

func (*MemoryLedger) ConfigureBudget

func (l *MemoryLedger) ConfigureBudget(_ context.Context, budget Budget) (Budget, error)

func (*MemoryLedger) FenceOwner

func (l *MemoryLedger) FenceOwner(_ context.Context, owner, incarnation string) ([]Claim, error)

func (*MemoryLedger) IssueDialPermit

func (l *MemoryLedger) IssueDialPermit(_ context.Context, claimID string, generation uint64, permitID, token string) (Lease, error)

func (*MemoryLedger) ListClaimLeases

func (l *MemoryLedger) ListClaimLeases(_ context.Context, claimID string) ([]Lease, error)

func (*MemoryLedger) MarkDialStarted

func (l *MemoryLedger) MarkDialStarted(_ context.Context, id string, generation uint64) (Lease, error)

func (*MemoryLedger) ReconcileAbsence

func (l *MemoryLedger) ReconcileAbsence(_ context.Context, id string, generation uint64, proof AbsenceProof) (Lease, error)

func (*MemoryLedger) ReleasePlan

func (l *MemoryLedger) ReleasePlan(_ context.Context, id string) error

func (*MemoryLedger) ReservePlan

func (l *MemoryLedger) ReservePlan(_ context.Context, lease PlanLease) error

func (*MemoryLedger) ShrinkFencedClaim

func (l *MemoryLedger) ShrinkFencedClaim(_ context.Context, id string, generation uint64) (Claim, error)

type MemorySessionStore

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

MemorySessionStore is a deterministic CAS reference implementation. Multiple Directory instances may share it in race tests; cross-process deployments use B5SessionRepository.

func NewMemorySessionStore

func NewMemorySessionStore() *MemorySessionStore

func (*MemorySessionStore) CASOwner

func (s *MemorySessionStore) CASOwner(_ context.Context, id string, revision int64, owner string, epoch uint64, newOwner, route, ciphertext string, digest []byte) (store.B5Session, error)

func (*MemorySessionStore) CASStatus

func (s *MemorySessionStore) CASStatus(_ context.Context, id string, revision int64, from, to b5.SessionStatus, idle time.Time) (store.B5Session, error)

func (*MemorySessionStore) Create

func (*MemorySessionStore) Get

func (*MemorySessionStore) ListExpired

func (s *MemorySessionStore) ListExpired(_ context.Context, now time.Time, limit int) ([]store.B5Session, error)

type PGInventory

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

PGInventory uses a separately supplied administrative pool. It never uses the socket being reconciled. Exact termination predicates include backend start time and the authenticated dial token, so PID reuse cannot kill an unrelated backend.

func NewPGInventory

func NewPGInventory(ctx context.Context, db *sql.DB) (*PGInventory, error)

func (*PGInventory) ObservePermit

func (inventory *PGInventory) ObservePermit(ctx context.Context, token string) (InventoryObservation, error)

func (*PGInventory) Terminate

func (inventory *PGInventory) Terminate(ctx context.Context, backend BackendIdentity) error

type PlanLease

type PlanLease struct {
	ID, DatasourceID                  string
	OwnerInstanceID, OwnerIncarnation string
	Bytes                             int64
}

type ReapReport

type ReapReport struct {
	FencedClaims, ReconciledChildren, TerminatedBackends int
	ClaimsReleased, CapacityReleased                     int
	InventoryBlocked                                     bool
}

type Reaper

type Reaper struct {
	Ledger    LedgerStore
	Inventory BackendInventory
}

func (Reaper) ReapOwner

func (r Reaper) ReapOwner(ctx context.Context, owner, incarnation string) (ReapReport, error)

ReapOwner is restartable and idempotent. It first fences the old owner, then reconciles each durable child. Inventory errors never reduce a charged claim. A second observation after terminate is the only absence proof.

type RouteDecision

type RouteDecision struct {
	Session          store.B5Session
	Code             b5.ErrorCode
	RetrySameRequest bool
	StickyRoute      string
	Authorized       bool
}

type SQLLedger

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

func NewPostgresLedger

func NewPostgresLedger(db *sql.DB) (*SQLLedger, error)

func (*SQLLedger) AbandonUndialed

func (l *SQLLedger) AbandonUndialed(ctx context.Context, id string, generation uint64, digest [32]byte) (Lease, error)

func (*SQLLedger) AcquireClaim

func (l *SQLLedger) AcquireClaim(ctx context.Context, request ClaimRequest) (Claim, error)

func (*SQLLedger) AcquireFree

func (l *SQLLedger) AcquireFree(ctx context.Context, id string, generation uint64, owner, incarnation string) (Lease, error)

func (*SQLLedger) ApplyDisposition

func (l *SQLLedger) ApplyDisposition(ctx context.Context, id string, generation uint64, proof DispositionProof) (Lease, error)

func (*SQLLedger) BeginTermination

func (l *SQLLedger) BeginTermination(ctx context.Context, id string, generation uint64) (Lease, error)

func (*SQLLedger) BindBackend

func (l *SQLLedger) BindBackend(ctx context.Context, id string, generation uint64, backend BackendIdentity) (Lease, error)

func (*SQLLedger) Budget

func (l *SQLLedger) Budget(ctx context.Context, datasource string) (Budget, error)

func (*SQLLedger) Claim

func (l *SQLLedger) Claim(ctx context.Context, id string) (Claim, error)

func (*SQLLedger) ConfigureBudget

func (l *SQLLedger) ConfigureBudget(ctx context.Context, input Budget) (Budget, error)

func (*SQLLedger) ConfigureTombstones

func (l *SQLLedger) ConfigureTombstones(ctx context.Context, budget TombstoneBudget, now time.Time) error

func (*SQLLedger) FenceOwner

func (l *SQLLedger) FenceOwner(ctx context.Context, owner, incarnation string) ([]Claim, error)

func (*SQLLedger) GCTombstones

func (l *SQLLedger) GCTombstones(ctx context.Context, now time.Time, limit int) (int, error)

func (*SQLLedger) IssueDialPermit

func (l *SQLLedger) IssueDialPermit(ctx context.Context, claimID string, generation uint64, permitID, token string) (Lease, error)

func (*SQLLedger) ListClaimLeases

func (l *SQLLedger) ListClaimLeases(ctx context.Context, claimID string) ([]Lease, error)

func (*SQLLedger) MarkDialStarted

func (l *SQLLedger) MarkDialStarted(ctx context.Context, id string, generation uint64) (Lease, error)

func (*SQLLedger) PutTombstone

func (l *SQLLedger) PutTombstone(ctx context.Context, value Tombstone, now time.Time) error

func (*SQLLedger) ReconcileAbsence

func (l *SQLLedger) ReconcileAbsence(ctx context.Context, id string, generation uint64, proof AbsenceProof) (Lease, error)

func (*SQLLedger) ReleasePlan

func (l *SQLLedger) ReleasePlan(ctx context.Context, id string) error

func (*SQLLedger) ReservePlan

func (l *SQLLedger) ReservePlan(ctx context.Context, lease PlanLease) error

func (*SQLLedger) ShrinkFencedClaim

func (l *SQLLedger) ShrinkFencedClaim(ctx context.Context, id string, generation uint64) (Claim, error)

func (*SQLLedger) TombstoneUsage

func (l *SQLLedger) TombstoneUsage(ctx context.Context) (entries int, bytes int64, err error)

type TerminalDisposition

type TerminalDisposition struct {
	Schema        string
	SchemaVersion uint16
	Disposition   b5.ConnectionDisposition
}

type Tombstone

type Tombstone struct {
	ID              string
	SessionIDDigest [32]byte
	TerminalCode    string
	FinalSeq        uint64
	OwnerEpoch      uint64
	EventDigest     [32]byte
	ExpiresAt       time.Time
}

Tombstone is digest-only by construction: it has no SQL, plan, rows, response, reason, or secret field that a caller could accidentally retain.

type TombstoneBudget

type TombstoneBudget struct {
	MaxEntries, MaxChurnPerSecond int
	MaxBytes                      int64
}

Jump to

Keyboard shortcuts

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