docs: expand adaptive Mail guidance
This commit is contained in:
@@ -196,6 +196,7 @@ manifest = ModuleManifest(
|
|||||||
links=(
|
links=(
|
||||||
DocumentationLink(label="Mail profiles", href="/api/v1/mail/profiles", kind="api"),
|
DocumentationLink(label="Mail profiles", href="/api/v1/mail/profiles", kind="api"),
|
||||||
DocumentationLink(label="Tenant mail policy", href="/api/v1/mail/policies/tenant", kind="api"),
|
DocumentationLink(label="Tenant mail policy", href="/api/v1/mail/policies/tenant", kind="api"),
|
||||||
|
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||||
DocumentationLink(label="Public mail module documentation", href="https://govplan.add-ideas.de/modules/mail", kind="public"),
|
DocumentationLink(label="Public mail module documentation", href="https://govplan.add-ideas.de/modules/mail", kind="public"),
|
||||||
),
|
),
|
||||||
related_modules=("campaigns",),
|
related_modules=("campaigns",),
|
||||||
@@ -203,7 +204,7 @@ manifest = ModuleManifest(
|
|||||||
configuration_keys=("mail_profile_policy",),
|
configuration_keys=("mail_profile_policy",),
|
||||||
metadata={
|
metadata={
|
||||||
"kind": "reference",
|
"kind": "reference",
|
||||||
"route": "/mail",
|
"route": "/settings?section=mail-profiles",
|
||||||
"screen": "Mail profiles and policy",
|
"screen": "Mail profiles and policy",
|
||||||
"section": "Effective profile policy",
|
"section": "Effective profile policy",
|
||||||
"related_topic_ids": [
|
"related_topic_ids": [
|
||||||
@@ -228,15 +229,15 @@ manifest = ModuleManifest(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
links=(
|
links=(
|
||||||
DocumentationLink(label="Mail profiles", href="/mail", kind="runtime"),
|
DocumentationLink(label="Mail profiles", href="/settings?section=mail-profiles", kind="runtime"),
|
||||||
DocumentationLink(label="Mail profiles API", href="/api/v1/mail/profiles", kind="api"),
|
DocumentationLink(label="Mail profiles API", href="/api/v1/mail/profiles", kind="api"),
|
||||||
DocumentationLink(label="Campaigns", href="/campaigns", kind="runtime"),
|
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||||
),
|
),
|
||||||
related_modules=("campaigns", "access"),
|
related_modules=("campaigns", "access"),
|
||||||
unlocks=("Reusable, governed delivery identities without cross-module secret duplication.",),
|
unlocks=("Reusable, governed delivery identities without cross-module secret duplication.",),
|
||||||
metadata={
|
metadata={
|
||||||
"kind": "reference",
|
"kind": "reference",
|
||||||
"route": "/mail",
|
"route": "/settings?section=mail-profiles",
|
||||||
"screen": "Mail profiles",
|
"screen": "Mail profiles",
|
||||||
"section": "Profile ownership, credentials, and consumers",
|
"section": "Profile ownership, credentials, and consumers",
|
||||||
"related_topic_ids": [
|
"related_topic_ids": [
|
||||||
@@ -247,6 +248,159 @@ manifest = ModuleManifest(
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
DocumentationTopic(
|
||||||
|
id="mail.workflow.choose-and-test-profile",
|
||||||
|
title="Choose and test a reusable Mail profile",
|
||||||
|
summary="Select a profile visible in the current context, test its SMTP or IMAP connection, and let the consuming task store only its stable reference.",
|
||||||
|
body="Profile tests verify current connection and authentication using Mail-owned credentials. They do not prove later Campaign policy authorization, deliverability, recipient acceptance, or future availability. Use the picker rather than entering identifiers or copying transport settings into another module.",
|
||||||
|
layer="configured",
|
||||||
|
documentation_types=("user",),
|
||||||
|
audience=("mail_user", "campaign_manager"),
|
||||||
|
order=41,
|
||||||
|
conditions=(
|
||||||
|
DocumentationCondition(
|
||||||
|
required_modules=("mail",),
|
||||||
|
required_scopes=("mail:profile:read", "mail:profile:use", "mail:profile:test"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
links=(
|
||||||
|
DocumentationLink(label="Mail profiles", href="/settings?section=mail-profiles", kind="runtime"),
|
||||||
|
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||||
|
),
|
||||||
|
related_modules=("campaigns",),
|
||||||
|
unlocks=("Reusable SMTP/IMAP identities that consumers can reference without handling credentials.",),
|
||||||
|
metadata={
|
||||||
|
"kind": "workflow",
|
||||||
|
"route": "/settings?section=mail-profiles",
|
||||||
|
"screen": "Mail profiles",
|
||||||
|
"prerequisites": [
|
||||||
|
"Mail is installed and you may read, use, and test profiles visible in the current context.",
|
||||||
|
"A profile administrator has configured credentials and effective policy.",
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Open Mail profiles and choose a visible active profile.",
|
||||||
|
"Review its safe scope, SMTP/IMAP availability, and sender identity without expecting credential values.",
|
||||||
|
"Run the relevant SMTP or IMAP connectivity/authentication test against a non-production target first.",
|
||||||
|
"Return to the consuming task and select the same profile through its picker.",
|
||||||
|
],
|
||||||
|
"outcome": "The consuming task references an available Mail-owned profile and contains no copied transport configuration or credentials.",
|
||||||
|
"verification": "Reload both surfaces, confirm only the stable reference is retained by the consumer, and perform the consumer's own contextual policy validation.",
|
||||||
|
"related_topic_ids": [
|
||||||
|
"mail.profile-ownership-and-consumers",
|
||||||
|
"mail.reference.campaign-delivery-contract",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DocumentationTopic(
|
||||||
|
id="mail.workflow.read-mailbox",
|
||||||
|
title="Read a permitted mailbox without changing it",
|
||||||
|
summary="Choose an IMAP-enabled profile, browse folders, and inspect bounded message content through the read-only mailbox surface.",
|
||||||
|
body="Mailbox access requires both mailbox-read and profile-use authority for a profile visible in the actor's scope. Listing folders or messages must not mark mail read, move it, delete it, or expose unbounded content.",
|
||||||
|
layer="configured",
|
||||||
|
documentation_types=("user",),
|
||||||
|
audience=("mail_user",),
|
||||||
|
order=42,
|
||||||
|
conditions=(
|
||||||
|
DocumentationCondition(
|
||||||
|
required_modules=("mail",),
|
||||||
|
required_scopes=("mail:mailbox:read", "mail:profile:use"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
links=(
|
||||||
|
DocumentationLink(label="Mail", href="/mail", kind="runtime"),
|
||||||
|
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||||
|
),
|
||||||
|
unlocks=("Read-only access to an authorized operational mailbox without broadening profile administration rights.",),
|
||||||
|
metadata={
|
||||||
|
"kind": "workflow",
|
||||||
|
"route": "/mail",
|
||||||
|
"screen": "Mail",
|
||||||
|
"prerequisites": [
|
||||||
|
"An active visible profile has IMAP configured.",
|
||||||
|
"You may both use that profile and read its mailbox.",
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Open Mail and choose an authorized IMAP-enabled profile.",
|
||||||
|
"Select a folder and page through its bounded message index.",
|
||||||
|
"Open only the message needed for the task and close it when finished.",
|
||||||
|
],
|
||||||
|
"outcome": "The required message was inspected without changing provider mailbox state.",
|
||||||
|
"verification": "Refresh the provider mailbox independently and confirm no read, move, delete, reply, or flag mutation was caused by GovOPlaN.",
|
||||||
|
"related_topic_ids": [
|
||||||
|
"mail.workflow.choose-and-test-profile",
|
||||||
|
"mail.reference.credentials-egress-retirement",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DocumentationTopic(
|
||||||
|
id="mail.reference.credentials-egress-retirement",
|
||||||
|
title="Protect Mail credentials, network egress, and retirement",
|
||||||
|
summary="Keep secrets Mail-owned, pin every SMTP/IMAP peer, bound responses, and delete owned credentials immediately with non-secret audit evidence.",
|
||||||
|
body="Private-network connector access is deployment-wide, but every allowed hostname still resolves to an approved peer that is pinned at socket creation. Unsupported transports fail before connection. Deleting a profile immediately scrubs its owned encrypted SMTP/IMAP passwords and records non-secret audit when secrets existed; a scrub or audit failure rolls the action back. Destructive module retirement applies the same rule before table drop.",
|
||||||
|
layer="evidence",
|
||||||
|
documentation_types=("admin",),
|
||||||
|
audience=("mail_admin", "platform_operator", "security_reviewer", "release_reviewer"),
|
||||||
|
order=43,
|
||||||
|
conditions=(
|
||||||
|
DocumentationCondition(
|
||||||
|
required_modules=("mail",),
|
||||||
|
any_scopes=("mail:secret:manage", "mail:profile:write", "system:settings:read"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
links=(
|
||||||
|
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||||
|
DocumentationLink(label="Mail profiles API", href="/api/v1/mail/profiles", kind="api"),
|
||||||
|
),
|
||||||
|
related_modules=("audit",),
|
||||||
|
unlocks=("A testable Mail trust boundary with fail-closed egress and auditable secret disposal.",),
|
||||||
|
metadata={
|
||||||
|
"kind": "reference",
|
||||||
|
"route": "/settings?section=mail-profiles",
|
||||||
|
"screen": "Mail profiles",
|
||||||
|
"section": "Credentials, network egress, deletion, and retirement",
|
||||||
|
"verification": "Test DNS rebinding and denied addresses, prove the connected peer is pinned, inject credential-scrub and audit failures, repeat deletion for idempotency, and run the retirement preflight against a snapshot.",
|
||||||
|
"related_topic_ids": [
|
||||||
|
"mail.workflow.choose-and-test-profile",
|
||||||
|
"mail.reference.campaign-delivery-contract",
|
||||||
|
"campaigns.reference.composition-assurance",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DocumentationTopic(
|
||||||
|
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.",
|
||||||
|
layer="available",
|
||||||
|
documentation_types=("admin", "user"),
|
||||||
|
audience=("integrator", "campaign_manager", "campaign_sender", "release_reviewer"),
|
||||||
|
order=44,
|
||||||
|
conditions=(
|
||||||
|
DocumentationCondition(
|
||||||
|
required_modules=("mail", "campaigns"),
|
||||||
|
required_scopes=("mail:profile:use", "campaigns:campaign:read"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
links=(
|
||||||
|
DocumentationLink(label="Campaigns", href="/campaigns", kind="runtime"),
|
||||||
|
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||||
|
DocumentationLink(label="Campaign Mail profile boundary", href="govoplan-campaign/docs/MAIL_PROFILE_BOUNDARY.md", kind="repository"),
|
||||||
|
),
|
||||||
|
related_modules=("campaigns", "audit"),
|
||||||
|
unlocks=("Profile-backed campaign delivery without cross-module credential or configuration copies.",),
|
||||||
|
metadata={
|
||||||
|
"kind": "reference",
|
||||||
|
"route": "/campaigns/{campaign_id}/mail-settings",
|
||||||
|
"screen": "Campaign Mail settings",
|
||||||
|
"section": "Mail-owned profile and transport boundary",
|
||||||
|
"verification": "Prove stale revisions fail before credential decryption, SMTP never decrypts IMAP credentials, IMAP never decrypts SMTP credentials, provider details are sanitized, and the interface/version gate passes.",
|
||||||
|
"related_topic_ids": [
|
||||||
|
"mail.profile-ownership-and-consumers",
|
||||||
|
"campaigns.mail-profile-user-journey",
|
||||||
|
"campaigns.workflow.retry-and-reconcile",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
documentation_providers=(documentation_topics,),
|
documentation_providers=(documentation_topics,),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,11 +24,29 @@ class MailManifestTests(unittest.TestCase):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
topics = {topic.id: topic for topic in manifest.documentation}
|
topics = {topic.id: topic for topic in manifest.documentation}
|
||||||
|
self.assertTrue(
|
||||||
|
{
|
||||||
|
"mail.workflow.choose-and-test-profile",
|
||||||
|
"mail.workflow.read-mailbox",
|
||||||
|
"mail.reference.credentials-egress-retirement",
|
||||||
|
"mail.reference.campaign-delivery-contract",
|
||||||
|
}.issubset(topics)
|
||||||
|
)
|
||||||
ownership = topics["mail.profile-ownership-and-consumers"]
|
ownership = topics["mail.profile-ownership-and-consumers"]
|
||||||
self.assertEqual(ownership.metadata["kind"], "reference")
|
self.assertEqual(ownership.metadata["kind"], "reference")
|
||||||
self.assertEqual(ownership.metadata["route"], "/mail")
|
self.assertEqual(ownership.metadata["route"], "/settings?section=mail-profiles")
|
||||||
self.assertIn("campaigns.mail-profile-user-journey", ownership.metadata["related_topic_ids"])
|
self.assertIn("campaigns.mail-profile-user-journey", ownership.metadata["related_topic_ids"])
|
||||||
|
|
||||||
|
mailbox = topics["mail.workflow.read-mailbox"]
|
||||||
|
self.assertEqual(mailbox.metadata["route"], "/mail")
|
||||||
|
self.assertEqual(
|
||||||
|
mailbox.conditions[0].required_scopes,
|
||||||
|
("mail:mailbox:read", "mail:profile:use"),
|
||||||
|
)
|
||||||
|
|
||||||
|
campaign_contract = topics["mail.reference.campaign-delivery-contract"]
|
||||||
|
self.assertEqual(campaign_contract.conditions[0].required_modules, ("mail", "campaigns"))
|
||||||
|
|
||||||
def test_retirement_scrubs_credentials_before_dropping_mail_tables(self) -> None:
|
def test_retirement_scrubs_credentials_before_dropping_mail_tables(self) -> None:
|
||||||
events: list[str] = []
|
events: list[str] = []
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user