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
- Variables
- func AttestationHash(a RuntimeAttestation) (string, error)
- func CanonicalBytes(p RuntimePolicy) ([]byte, error)
- func Hash(p RuntimePolicy) (string, error)
- func HashBytes(canonical []byte) string
- type ActivationRequest
- type Capabilities
- type GrandfatherResult
- type Keyring
- type Mode
- type Module
- func (m *Module) ActivatePolicy(ctx context.Context, req ActivationRequest) (PolicySnapshot, error)
- func (m *Module) AttestRuntime(ctx context.Context, req RuntimeAttestationRequest) (RuntimeAttestation, error)
- func (m *Module) InspectAttestation(ctx context.Context) (RuntimeAttestation, error)
- func (m *Module) InspectPolicy(ctx context.Context) (PolicySnapshot, error)
- func (m *Module) RegisterOperatorRecipients(ctx context.Context, actor, reason string) (inserted []int, err error)
- type OperatorRecipients
- type PolicySnapshot
- type PolicySource
- type RuntimeAttestation
- type RuntimeAttestationRequest
- type RuntimePolicy
- type Secrets
- type TenantHeaderMode
- type ToggleMode
Constants ¶
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.
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.
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 ¶
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") // 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.
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 ¶
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 ¶
ActiveVersion reports which key new material is signed under.
func (*Keyring) Sign ¶
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.
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.
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 ¶
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 ¶
func (m *Module) AttestRuntime(ctx context.Context, req RuntimeAttestationRequest) (RuntimeAttestation, error)
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"`
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" )