sendingpolicy

package
v1.8.7 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package sendingpolicy owns the authoritative sending-protection runtime policy: the typed payload, its closed enums and numeric invariants, its RFC 8785 canonical form, and the compare-and-swap activation that advances it.

Nothing here enforces anything. Task 2 establishes the authority that later slices read; every mode in the generation-zero policy is disabled, and the module is deliberately inert until a reviewed activation says otherwise.

Index

Constants

View Source
const (
	EnvFeedbackHMACKeys      = "E2A_SENDING_FEEDBACK_HMAC_KEYS"
	EnvOperatorRecipientsMap = "E2A_SENDING_PROTECTION_OPERATOR_EMAILS"
)

Environment variable names, fixed by the B1a prewire: the ops assemblers render exactly these keys, and docker-compose passes them through to the server environment.

View Source
const ContractLevel = 0

ContractLevel is the compiled provider-call closure contract this binary advertises. It stays 0 until Task 12's closure guard proves every SES path requires authorization; Task 12 is the only change allowed to raise it.

View Source
const SchemaVersion = 1

SchemaVersion is the only policy schema this binary understands. A stored policy carrying any other version is rejected before it can reach a provider authorization decision — an older binary must never silently reinterpret a newer operator's payload.

Variables

View Source
var (
	// ErrStaleGeneration means the stored policy generation moved between the
	// operator reading it and this write. Zero rows were written.
	ErrStaleGeneration = errors.New("sendingpolicy: stale policy generation")
	// ErrStaleAttestation means the attestation revision or its four-field
	// hash did not match what the caller expected. Zero rows were written.
	ErrStaleAttestation = errors.New("sendingpolicy: stale runtime attestation")
	// ErrAttestationCommitUnchanged means Commit returned an error and the
	// mandatory reread proved the reviewed prior revision/hash are still
	// current. The same forward request is safe to retry if still intended.
	ErrAttestationCommitUnchanged = errors.New("sendingpolicy: attestation commit left state unchanged")
	// ErrAttestationCommitUnknown means Commit returned an error and the
	// mandatory reread could not prove either the exact requested next state or
	// the unchanged prior state. The caller must inspect and fence/restore.
	ErrAttestationCommitUnknown = errors.New("sendingpolicy: attestation commit outcome is unknown")
	// ErrPolicyHashMismatch means the reviewed hash does not describe the
	// policy actually being submitted.
	ErrPolicyHashMismatch = errors.New("sendingpolicy: policy hash mismatch")
	// ErrRegistryConflict means an operator-recipient version already exists
	// with a different key identity or commitment. Append-only history is
	// never rewritten, so this is always zero writes.
	ErrRegistryConflict = errors.New("sendingpolicy: operator recipient registry conflict")
	// ErrOperatorRecipientUnavailable means the policy-selected logical
	// version is absent from either this process's immutable secret map or the
	// permanent registry. Policy activation must fail before writing anything.
	ErrOperatorRecipientUnavailable = errors.New("sendingpolicy: selected operator recipient is unavailable")
	// ErrBillingContractTooLow means a budget-enforcement transition was
	// attempted without both active and rollback billing images attested at
	// sending-protection contract level 1 or higher.
	ErrBillingContractTooLow = errors.New("sendingpolicy: billing contract is too low for budget enforcement")
	// ErrInvalidBillingDigest means an attestation did not name a canonical
	// immutable sha256 image digest. Empty is reserved for contract level 0.
	ErrInvalidBillingDigest = errors.New("sendingpolicy: invalid billing image digest")
	// ErrAlreadyGrandfathered means the one-shot ramp-grandfathering marker
	// already exists. The retry is a documented no-op: it can never widen the
	// grandfathered set, so it performs zero writes.
	ErrAlreadyGrandfathered = errors.New("sendingpolicy: sending domains were already grandfathered")
)

Sentinel errors. Callers distinguish "you lost a race, re-read and decide" from "your request was malformed", because the correct operator response to each is different: one is a retry, the other is a fix.

Functions

func AttestationHash

func AttestationHash(a RuntimeAttestation) (string, error)

AttestationHash is the lowercase SHA-256 an operator passes back as -expected-attestation-sha256. It covers only the four state fields, never the revision: the revision is compared separately and explicitly.

func CanonicalBytes

func CanonicalBytes(p RuntimePolicy) ([]byte, error)

CanonicalBytes exposes the canonical form for storage and operator readback.

func Hash

func Hash(p RuntimePolicy) (string, error)

Hash returns the lowercase hex SHA-256 of the canonical form. This is the value an operator reviews and then passes back as -expected-policy-sha256, so it must be derived from exactly the bytes that get stored.

func HashBytes

func HashBytes(canonical []byte) string

HashBytes returns the lowercase hex SHA-256 of already-canonical bytes. The store uses it to re-derive a stored row's hash without re-canonicalizing, so a row whose bytes drifted from its recorded hash is detected rather than silently re-blessed.

Types

type ActivationRequest

type ActivationRequest struct {
	ExpectedGeneration int64
	Policy             RuntimePolicy
	Actor              string
	Reason             string

	// GrandfatherCurrentSendingDomains performs the one-shot phase-3 snapshot
	// in the same transaction as the policy CAS: insert the singleton marker,
	// lock the domains table against concurrent sender transitions, and flip
	// every currently sending-verified, ramp-inactive domain to exempt. A
	// second attempt fails with ErrAlreadyGrandfathered and writes nothing.
	GrandfatherCurrentSendingDomains bool
}

ActivationRequest is one reviewed policy change.

type Capabilities

type Capabilities struct {
	SendingProtectionContract int               `json:"sending_protection_contract"`
	RuntimePolicySource       string            `json:"runtime_policy_source"`
	OperatorCommitments       map[string]string `json:"operator_notice_recipient_commitments"`
}

Capabilities is the operator-facing readback printed by -print-capabilities and compared across blue/green slots by the ops deploy gate. It carries commitments only: no addresses, no key material, no customer data.

func BuildCapabilities

func BuildCapabilities(source PolicySource, secrets Secrets) Capabilities

BuildCapabilities assembles the readback. A missing operator map yields an empty commitments object rather than an error: the self-host disabled mode legitimately has none, and the deploy gate treats absence as absence.

type GrandfatherResult

type GrandfatherResult struct {
	DomainsExempted int64
}

GrandfatherResult reports what the one-shot snapshot did.

type Keyring

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

Keyring is the immutable HMAC keyring loaded from E2A_SENDING_FEEDBACK_HMAC_KEYS. One version signs new material; every version stays available to verify feedback that was signed before a rotation.

func LoadKeyring

func LoadKeyring(raw string) (*Keyring, error)

LoadKeyring parses and validates the keyring secret. Every failure is fatal at startup: a deployment that cannot sign feedback correlations must not come up believing it can.

func (*Keyring) ActiveVersion

func (k *Keyring) ActiveVersion() int

ActiveVersion reports which key new material is signed under.

func (*Keyring) Sign

func (k *Keyring) Sign(msg []byte) (version int, mac []byte)

Sign returns the HMAC-SHA256 of msg under the active key, with the version it used so a verifier can select the same key after a rotation.

func (*Keyring) Verify

func (k *Keyring) Verify(version int, msg, mac []byte) bool

Verify checks msg against mac under a specific version. An unknown version is a failure, never a fallback to the active key: accepting material signed by a key this process does not hold would defeat the correlation entirely.

func (*Keyring) Versions

func (k *Keyring) Versions() []int

Versions lists every loaded version in ascending order. Used by the deploy gate to prove one slot's keyring is a superset of the other's.

type Mode

type Mode string

Mode is a three-state rollout control. The three states are independent per control: a policy may run the budget in shadow while the detector is still disabled, and vice versa.

const (
	// ModeDisabled computes nothing and blocks nothing.
	ModeDisabled Mode = "disabled"
	// ModeShadow computes the decision and records it, but never denies.
	ModeShadow Mode = "shadow"
	// ModeEnforce computes the decision and acts on it.
	ModeEnforce Mode = "enforce"
)

type Module

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

Module is the single concrete owner of sending-protection policy state. Later slices expose narrow role interfaces (Gate, FeedbackProcessor, Admin) backed by this same object; the Postgres store stays private because there is only ever one adapter.

func NewModule

func NewModule(pool *pgxpool.Pool, secrets Secrets) *Module

NewModule binds the module to a pool and the immutable trust roots parsed at startup. It performs no I/O: a server that never reads the policy (self-host on config source) must not pay for a query.

func (*Module) ActivatePolicy

func (m *Module) ActivatePolicy(ctx context.Context, req ActivationRequest) (PolicySnapshot, error)

ActivatePolicy advances the policy by exactly one generation, under a compare-and-swap on the generation the operator reviewed.

Everything happens in one transaction holding the singleton's row lock, so a concurrent activation either waits and then loses on generation, or wins and makes this one lose. There is no path that writes the policy without also writing its audit event.

func (*Module) AttestRuntime

AttestRuntime compare-and-swaps the billing attestation on both the expected revision and the canonical hash of the four fields it replaces.

Requiring both is what defeats an ABA retry: a delayed writer whose four expected fields happen to match a later state is still rejected, because the revision advanced past it. A higher revision is always stale, never a candidate for replacement.

func (*Module) InspectAttestation

func (m *Module) InspectAttestation(ctx context.Context) (RuntimeAttestation, error)

InspectAttestation reads the current runtime attestation.

func (*Module) InspectPolicy

func (m *Module) InspectPolicy(ctx context.Context) (PolicySnapshot, error)

InspectPolicy reads the current policy without locking. It is the readback half of every operator command and the dry-run of an activation.

func (*Module) RegisterOperatorRecipients

func (m *Module) RegisterOperatorRecipients(ctx context.Context, actor, reason string) (inserted []int, err error)

RegisterOperatorRecipients inserts registry rows for versions that are not yet recorded, and refuses any disagreement with history.

The table's triggers already make UPDATE and DELETE impossible, so the only way to be wrong here is to insert a row that contradicts an existing one. That is checked explicitly and fails the whole transaction, because a partial registration would leave the operator unable to tell which versions are trustworthy.

type OperatorRecipients

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

OperatorRecipients is the immutable versioned operator mailbox map loaded from E2A_SENDING_PROTECTION_OPERATOR_EMAILS.

The addresses are secret deployment configuration, not part of the account model, so this type deliberately exposes commitments freely and mailboxes narrowly: capabilities and registry rows carry only the HMAC commitment, and only the notice sender ever resolves an actual address.

func LoadOperatorRecipients

func LoadOperatorRecipients(raw string) (*OperatorRecipients, error)

LoadOperatorRecipients parses and validates the operator map, deriving the key identity and per-version commitments that the registry and the capability readback compare against.

func (*OperatorRecipients) Commitment

func (o *OperatorRecipients) Commitment(version int) (string, bool)

Commitment returns the lowercase HMAC-SHA256 binding a version to its mailbox. This is what the append-only registry stores and what the capability readback advertises.

func (*OperatorRecipients) Commitments

func (o *OperatorRecipients) Commitments() map[string]string

Commitments returns a defensive copy of every version's commitment, keyed by canonical decimal version for the capability payload.

func (*OperatorRecipients) KeyID

func (o *OperatorRecipients) KeyID() string

KeyID is the lowercase HMAC-SHA256 of the commitment key over a fixed label. It identifies the key across slots and registry rows without revealing it.

func (*OperatorRecipients) Mailbox

func (o *OperatorRecipients) Mailbox(version int) (string, bool)

Mailbox resolves the address for a version. Only the notice sender calls it; nothing else in the system needs the plaintext.

func (*OperatorRecipients) Versions

func (o *OperatorRecipients) Versions() []int

Versions lists every configured version in ascending order.

type PolicySnapshot

type PolicySnapshot struct {
	Generation    int64
	SchemaVersion int
	Policy        RuntimePolicy
	PolicySHA256  string
	ActivatedAt   time.Time
	ActivatedBy   string
}

PolicySnapshot is the stored policy plus the metadata an operator needs to express the next compare-and-swap.

type PolicySource

type PolicySource string

PolicySource selects where the runtime policy is read from. Self-hosted deployments stay on the config file; the hosted deployment selects the database so that activation is an audited CAS rather than a redeploy.

const (
	// PolicySourceConfig is the default for every deployment, including
	// self-host, and is what a binary uses before B15 flips the hosted service.
	PolicySourceConfig PolicySource = "config"
	// PolicySourceDatabase reads the singleton policy row.
	PolicySourceDatabase PolicySource = "database"
)

func SourceFromConfig

func SourceFromConfig(cfg *config.Config) (PolicySource, error)

SourceFromConfig returns the validated policy source. An absent value is the config source, matching config.Validate: a deployment that never mentions sending protection stays on the file it already has.

type RuntimeAttestation

type RuntimeAttestation struct {
	Revision                int64
	ActiveBillingDigest     string
	ActiveBillingContract   int
	RollbackBillingDigest   string
	RollbackBillingContract int
	UpdatedAt               time.Time
	UpdatedBy               string
}

RuntimeAttestation records which billing images the deployment has verified, so a policy activation cannot outrun the artifacts that support it.

type RuntimeAttestationRequest

type RuntimeAttestationRequest struct {
	ExpectedRevision int64
	ExpectedSHA256   string

	ActiveBillingDigest     string
	ActiveBillingContract   int
	RollbackBillingDigest   string
	RollbackBillingContract int

	Actor  string
	Reason string
}

RuntimeAttestationRequest is a CAS over both the revision and the canonical hash of the four fields being replaced.

type RuntimePolicy

type RuntimePolicy struct {
	AllCustomerGlobalDailyRecipients int              `json:"all_customer_global_daily_recipients"`
	BounceMinOutcomes                int              `json:"bounce_min_outcomes"`
	BouncePauseBasisPoints           int              `json:"bounce_pause_basis_points"`
	BudgetHoldMaxDays                int              `json:"budget_hold_max_days"`
	BudgetMode                       Mode             `json:"budget_mode"`
	ComplaintPauseBasisPoints        int              `json:"complaint_pause_basis_points"`
	CriticalOperationalDailyRecip    int              `json:"critical_operational_daily_recipients"`
	DailyUnlimitedPlanCodes          []string         `json:"daily_unlimited_plan_codes"`
	DefaultAccountDailyRecipients    int              `json:"default_account_daily_recipients"`
	DetectorIntervalSeconds          int              `json:"detector_interval_seconds"`
	DetectorMode                     Mode             `json:"detector_mode"`
	DetectorWindowDays               int              `json:"detector_window_days"`
	OperatorNoticeRecipientVersion   int              `json:"operator_notice_recipient_version"`
	ProbationGlobalDailyRecipients   int              `json:"probation_global_daily_recipients"`
	RampDays                         int              `json:"ramp_days"`
	RampEnabled                      bool             `json:"ramp_enabled"`
	RampStartDaily                   int              `json:"ramp_start_daily"`
	RampTargetDaily                  int              `json:"ramp_target_daily"`
	SendingControlAuditRetentionDays int              `json:"sending_control_audit_retention_days"`
	SendingFeedbackPostAcctRetention int              `json:"sending_feedback_post_account_retention_days"`
	SharedDomainAccountDailyRecip    int              `json:"shared_domain_account_daily_recipients"`
	SharedReputationBounceMinOutcome int              `json:"shared_reputation_bounce_min_outcomes"`
	TenantHeaderCanaryAccountIDs     []string         `json:"tenant_header_canary_account_ids"`
	TenantHeaderMode                 TenantHeaderMode `json:"tenant_header_mode"`
	TenantProvisioningMode           ToggleMode       `json:"tenant_provisioning_mode"`
	TenantSuppressionSyncMode        ToggleMode       `json:"tenant_suppression_sync_mode"`
	ViolationOperationalDailyRecip   int              `json:"violation_operational_daily_recipients"`
}

RuntimePolicy is the whole sending-protection policy as one immutable typed value. Field names and JSON keys are load-bearing: the canonical form of this struct is hashed, reviewed by a human, and then required by hash at activation, so renaming a key is a policy-breaking change.

func DisabledPolicy

func DisabledPolicy() RuntimePolicy

DisabledPolicy returns the generation-zero policy: every control off, every numeric bound at its documented default. Migration 112 seeds exactly this value, so canonicalizing it must reproduce the hash that migration recorded. The keyring and registry tests use that equality as their anchor fixture.

func FromConfig

func FromConfig(cfg *config.Config) (RuntimePolicy, error)

FromConfig assembles the typed runtime policy from the validated config the server itself would use.

The schedule fields come from the `sending_ramp` block and the rest from `sending_protection`, because sending_ramp stays the custom-domain ramp SSOT and duplicating its numbers into a second block is how the two drift apart. The result is validated here, so an operator command and the server agree on what the config means before either acts on it.

func ParsePolicy

func ParsePolicy(raw []byte) (RuntimePolicy, error)

ParsePolicy decodes and validates a stored or configured policy. Unknown fields are rejected: a payload written by a newer binary carrying a control this one does not implement must fail closed, not be silently ignored.

func (RuntimePolicy) AllControlsDisabled

func (p RuntimePolicy) AllControlsDisabled() bool

AllControlsDisabled reports whether every sending-protection control is off.

RampEnabled is deliberately not part of this: the custom-domain ramp predates sending protection and self-hosts already run it without the new secrets. What the secrets protect -- provider authorization signing and operator notices -- only activates with the controls below.

func (RuntimePolicy) Validate

func (p RuntimePolicy) Validate() error

Validate enforces every closed enum and numeric invariant. It is called on the config-parsed policy at startup and again on any policy read from the database, because a row written by a newer binary must not be trusted just because it parsed.

type Secrets

type Secrets struct {
	Keyring    *Keyring
	Recipients *OperatorRecipients
}

Secrets bundles the two immutable trust roots a running server holds.

func LoadSecretsFromEnv

func LoadSecretsFromEnv(source PolicySource, policy RuntimePolicy) (Secrets, error)

LoadSecretsFromEnv loads and validates both B1a-prewired secrets.

Presence rules follow the plan exactly: a config-source deployment whose policy keeps every control disabled may omit them (self-host compatibility -- the secrets guard mechanisms that are not running). Database source, or any enabled control, requires both; and a value that is PRESENT must always be valid regardless of mode, because a malformed secret discovered at activation time is far worse than one discovered at boot. Errors are redacted by construction -- see keyring.go.

type TenantHeaderMode

type TenantHeaderMode string

TenantHeaderMode controls the SES tenant header. Unlike Mode, its middle state is a canary over an explicit account list rather than a shadow computation — a header is either sent or not; there is nothing to simulate.

const (
	TenantHeaderDisabled TenantHeaderMode = "disabled"
	TenantHeaderCanary   TenantHeaderMode = "canary"
	TenantHeaderEnforce  TenantHeaderMode = "enforce"
)

type ToggleMode

type ToggleMode string

ToggleMode is a two-state control for operations that have no meaningful shadow: provisioning either creates tenants or it does not, and suppression sync either writes to the provider or it does not.

const (
	ToggleDisabled ToggleMode = "disabled"
	ToggleEnforce  ToggleMode = "enforce"
)

Jump to

Keyboard shortcuts

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