feat: add durable mail delivery outbox

This commit is contained in:
2026-07-30 14:26:53 +02:00
parent dec5a4e350
commit b8029c24d6
13 changed files with 1913 additions and 17 deletions
+37 -2
View File
@@ -6,9 +6,11 @@ from pathlib import Path
from sqlalchemy import inspect
from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER
from govoplan_core.core.mail import CAPABILITY_MAIL_DELIVERY_OUTBOX
from govoplan_core.core.module_guards import drop_table_retirement_provider, persistent_table_uninstall_guard
from govoplan_core.core.modules import (
DocumentationCondition,
DocumentationConfigurationProviderRegistration,
DocumentationLink,
DocumentationTopic,
FrontendModule,
@@ -24,7 +26,10 @@ from govoplan_core.core.modules import (
)
from govoplan_core.core.views import ViewSurface
from govoplan_core.db.base import Base
from govoplan_mail.backend.documentation import documentation_topics
from govoplan_mail.backend.documentation import (
documentation_configuration_states,
documentation_topics,
)
from govoplan_mail.backend.db import models as mail_models # noqa: F401 - populate Mail ORM metadata
@@ -35,6 +40,9 @@ _mail_table_retirement_provider = drop_table_retirement_provider(
mail_models.MailProfilePolicy,
mail_models.MailMailboxFolderIndex,
mail_models.MailMailboxMessageIndex,
mail_models.MailDeliveryReconciliation,
mail_models.MailDeliveryAttempt,
mail_models.MailDeliveryCommand,
label="Mail",
)
@@ -90,6 +98,16 @@ PERMISSIONS = (
"Create, edit, and deactivate only the current account's user-scoped mail profiles within effective policy.",
),
_permission("mail:secret:manage", "Manage mail secrets", "Create or replace stored SMTP/IMAP credentials."),
_permission(
"mail:delivery:diagnostic",
"Inspect mail delivery diagnostics",
"Inspect bounded recipient-level refusal and attempt evidence for durable Mail commands.",
),
_permission(
"mail:delivery:reconcile",
"Reconcile mail delivery outcomes",
"Reconcile unknown delivery outcomes and explicitly authorize deliberate resend commands.",
),
_permission(
"mail:secret:manage_own",
"Manage own mail secrets",
@@ -109,6 +127,8 @@ ROLE_TEMPLATES = (
"mail:mailbox:read",
"mail:profile:write",
"mail:secret:manage",
"mail:delivery:diagnostic",
"mail:delivery:reconcile",
),
),
RoleTemplate(
@@ -158,6 +178,8 @@ manifest = ModuleManifest(
optional_dependencies=("campaigns", "addresses"),
provides_interfaces=(
ModuleInterfaceProvider(name="mail.campaign_delivery", version="0.2.0"),
ModuleInterfaceProvider(name="mail.delivery_commands", version="0.1.0"),
ModuleInterfaceProvider(name="mail.delivery_outbox", version="0.1.0"),
),
requires_interfaces=(
ModuleInterfaceRequirement(
@@ -219,11 +241,18 @@ manifest = ModuleManifest(
mail_models.MailProfilePolicy,
mail_models.MailMailboxFolderIndex,
mail_models.MailMailboxMessageIndex,
mail_models.MailDeliveryReconciliation,
mail_models.MailDeliveryAttempt,
mail_models.MailDeliveryCommand,
label="Mail",
),
),
capability_factories={
"mail.campaign_delivery": lambda context: __import__("govoplan_mail.backend.capabilities", fromlist=["campaign_capability"]).campaign_capability(context),
CAPABILITY_MAIL_DELIVERY_OUTBOX: lambda context: __import__(
"govoplan_mail.backend.capabilities",
fromlist=["delivery_outbox_capability"],
).delivery_outbox_capability(context),
},
documentation=(
DocumentationTopic(
@@ -427,7 +456,7 @@ manifest = ModuleManifest(
id="mail.reference.campaign-delivery-contract",
title="Integrate Campaign through the Mail delivery contract",
summary="Campaign freezes a Mail profile reference and opaque revision; Mail re-authorizes, revision-checks, resolves credentials, and performs the effect in one call.",
body="The mail.campaign_delivery 0.2 contract never returns decrypted credentials or resolved SMTP/IMAP configuration. Mail compares the expected random transport revision before decrypting protocol-specific credentials and returns only bounded sanitized outcomes. Campaign owns durable recipient jobs, retry, and reconciliation. Live report emailing remains disabled until the planned Mail-owned idempotent outbox and attempt ledger are implemented.",
body="The mail.campaign_delivery 0.2 contract never returns decrypted credentials or resolved SMTP/IMAP configuration. Mail compares the expected random transport revision before decrypting protocol-specific credentials and returns only bounded sanitized outcomes. Campaign owns ordinary recipient jobs; report messages use Mail's encrypted idempotent delivery-command and attempt ledger. Effect-start evidence prevents blind redelivery, unknown outcomes require explicit reconciliation, and raw recipient refusals require Mail diagnostic authority.",
layer="available",
documentation_types=("admin", "user"),
audience=("integrator", "campaign_manager", "campaign_sender", "release_reviewer"),
@@ -460,6 +489,12 @@ manifest = ModuleManifest(
),
),
documentation_providers=(documentation_topics,),
documentation_configuration_providers=(
DocumentationConfigurationProviderRegistration(
keys=("mail_profile_policy",),
resolve=documentation_configuration_states,
),
),
)