Prepare platform sender migration and verify return-mail boundaries
Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
This commit is contained in:
tegwick 2026-10-31 11:35:08 +01:00
parent e56654933c
commit d864333581
10 changed files with 1091 additions and 3 deletions

View file

@ -111,7 +111,7 @@
| task | CSOC-WP-0014-T06 | progress | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0014-T07 | todo | — | workplans/CSOC-WP-0014-cohort-transfer-bubble-offramp.md |
| task | CSOC-WP-0015-T01 | done | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T02 | todo | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T02 | progress | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T03 | done | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T04 | done | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |
| task | CSOC-WP-0015-T05 | todo | — | workplans/CSOC-WP-0015-communication-runtime-acceptance.md |

View file

@ -0,0 +1,178 @@
{
"schema": "coulomb.communication-mail-dns-observation.v1",
"observed_at": "2026-10-31T10:22:14.208342+00:00",
"selector_source": "https://www.ionos.com/help/domains/configuring-mail-servers-and-other-related-records/email-authentication-with-dkim/",
"queries": [
{
"server": "ns1082.ui-dns.com",
"name": "coulomb.social",
"type": "MX",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9022\n;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ncoulomb.social.\t\t3600\tIN\tMX\t10 mx01.ionos.de.\ncoulomb.social.\t\t3600\tIN\tMX\t10 mx00.ionos.de.\n\n"
},
{
"server": "ns1082.ui-dns.com",
"name": "coulomb.social",
"type": "TXT",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59826\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ncoulomb.social.\t\t3600\tIN\tTXT\t\"v=spf1 include:_spf-eu.ionos.com ~all\"\n\n"
},
{
"server": "ns1082.ui-dns.com",
"name": "_dmarc.coulomb.social",
"type": "TXT",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30510\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\n_dmarc.coulomb.social.\t3600\tIN\tTXT\t\"v=DMARC1; p=none\"\n\n"
},
{
"server": "ns1082.ui-dns.com",
"name": "s1-ionos._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38847\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns1-ionos._domainkey.coulomb.social. 3600 IN CNAME s1.dkim.ionos.com.\n\n"
},
{
"server": "ns1082.ui-dns.com",
"name": "s2-ionos._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38467\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns2-ionos._domainkey.coulomb.social. 3600 IN CNAME s2.dkim.ionos.com.\n\n"
},
{
"server": "ns1082.ui-dns.com",
"name": "s42582890._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38456\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns42582890._domainkey.coulomb.social. 3600 IN CNAME s42582890.dkim.ionos.com.\n\n"
},
{
"server": "ns1121.ui-dns.de",
"name": "coulomb.social",
"type": "MX",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23805\n;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ncoulomb.social.\t\t3600\tIN\tMX\t10 mx00.ionos.de.\ncoulomb.social.\t\t3600\tIN\tMX\t10 mx01.ionos.de.\n\n"
},
{
"server": "ns1121.ui-dns.de",
"name": "coulomb.social",
"type": "TXT",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29488\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ncoulomb.social.\t\t3600\tIN\tTXT\t\"v=spf1 include:_spf-eu.ionos.com ~all\"\n\n"
},
{
"server": "ns1121.ui-dns.de",
"name": "_dmarc.coulomb.social",
"type": "TXT",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38938\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\n_dmarc.coulomb.social.\t3600\tIN\tTXT\t\"v=DMARC1; p=none\"\n\n"
},
{
"server": "ns1121.ui-dns.de",
"name": "s1-ionos._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61796\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns1-ionos._domainkey.coulomb.social. 3600 IN CNAME s1.dkim.ionos.com.\n\n"
},
{
"server": "ns1121.ui-dns.de",
"name": "s2-ionos._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29455\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns2-ionos._domainkey.coulomb.social. 3600 IN CNAME s2.dkim.ionos.com.\n\n"
},
{
"server": "ns1121.ui-dns.de",
"name": "s42582890._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43275\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns42582890._domainkey.coulomb.social. 3600 IN CNAME s42582890.dkim.ionos.com.\n\n"
},
{
"server": "ns1047.ui-dns.biz",
"name": "coulomb.social",
"type": "MX",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51444\n;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ncoulomb.social.\t\t3600\tIN\tMX\t10 mx01.ionos.de.\ncoulomb.social.\t\t3600\tIN\tMX\t10 mx00.ionos.de.\n\n"
},
{
"server": "ns1047.ui-dns.biz",
"name": "coulomb.social",
"type": "TXT",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63384\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ncoulomb.social.\t\t3600\tIN\tTXT\t\"v=spf1 include:_spf-eu.ionos.com ~all\"\n\n"
},
{
"server": "ns1047.ui-dns.biz",
"name": "_dmarc.coulomb.social",
"type": "TXT",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42564\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\n_dmarc.coulomb.social.\t3600\tIN\tTXT\t\"v=DMARC1; p=none\"\n\n"
},
{
"server": "ns1047.ui-dns.biz",
"name": "s1-ionos._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29324\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns1-ionos._domainkey.coulomb.social. 3600 IN CNAME s1.dkim.ionos.com.\n\n"
},
{
"server": "ns1047.ui-dns.biz",
"name": "s2-ionos._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28199\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns2-ionos._domainkey.coulomb.social. 3600 IN CNAME s2.dkim.ionos.com.\n\n"
},
{
"server": "ns1047.ui-dns.biz",
"name": "s42582890._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5757\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns42582890._domainkey.coulomb.social. 3600 IN CNAME s42582890.dkim.ionos.com.\n\n"
},
{
"server": "ns1045.ui-dns.org",
"name": "coulomb.social",
"type": "MX",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60470\n;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ncoulomb.social.\t\t3600\tIN\tMX\t10 mx01.ionos.de.\ncoulomb.social.\t\t3600\tIN\tMX\t10 mx00.ionos.de.\n\n"
},
{
"server": "ns1045.ui-dns.org",
"name": "coulomb.social",
"type": "TXT",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60103\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ncoulomb.social.\t\t3600\tIN\tTXT\t\"v=spf1 include:_spf-eu.ionos.com ~all\"\n\n"
},
{
"server": "ns1045.ui-dns.org",
"name": "_dmarc.coulomb.social",
"type": "TXT",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6244\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\n_dmarc.coulomb.social.\t3600\tIN\tTXT\t\"v=DMARC1; p=none\"\n\n"
},
{
"server": "ns1045.ui-dns.org",
"name": "s1-ionos._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19973\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns1-ionos._domainkey.coulomb.social. 3600 IN CNAME s1.dkim.ionos.com.\n\n"
},
{
"server": "ns1045.ui-dns.org",
"name": "s2-ionos._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13871\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns2-ionos._domainkey.coulomb.social. 3600 IN CNAME s2.dkim.ionos.com.\n\n"
},
{
"server": "ns1045.ui-dns.org",
"name": "s42582890._domainkey.coulomb.social",
"type": "CNAME",
"returncode": 0,
"response": ";; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5332\n;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 1220\n;; ANSWER SECTION:\ns42582890._domainkey.coulomb.social. 3600 IN CNAME s42582890.dkim.ionos.com.\n\n"
}
],
"mail_sent": false,
"message_authentication_verified": false,
"mailbox_accessed": false
}

View file

@ -0,0 +1,230 @@
{
"schema": "coulomb.communication-mail-native-observation.v1",
"observed_at": "2026-10-31T10:21:51.260753+00:00",
"namespace": {
"name": "email-connect",
"namespace": null,
"uid": "5678c5d7-e4f5-4a62-8f24-1c6e1d906319",
"generation": null
},
"deployment": {
"name": "email-connect",
"namespace": "email-connect",
"uid": "2e98e52a-a2d1-4c52-9ac8-6efbc0a18e72",
"generation": 74,
"desired_replicas": 1,
"ready_replicas": 1,
"observed_generation": 74,
"containers": [
{
"name": "email-connect",
"image": "forgejo.coulomb.social/coulomb/email-connect@sha256:5d79fd1fec15d3a279383820f170d263de3a55f5308df01a0a34732ebaea6d3d",
"envFrom": [
{
"configMapRef": {
"name": "email-connect-config"
}
},
{
"secretRef": {
"name": "email-connect-runtime",
"optional": false
}
}
],
"public_env_overrides": {}
}
]
},
"configmap": {
"name": "email-connect-config",
"namespace": "email-connect",
"uid": "558d3338-c4c2-490c-a5e5-af206c39eb51",
"generation": null,
"public_settings": {
"EMAIL_CONNECT_SENDER": "forgejo@coulomb.social",
"EMAIL_CONNECT_SMTP_HOST": "smtp.ionos.de",
"EMAIL_CONNECT_SMTP_PORT": "587",
"EMAIL_CONNECT_SMTP_SECURITY": null,
"EMAIL_CONNECT_PORTAL_URL": "https://users.coulomb.social"
}
},
"smtp_projection": {
"name": "email-connect-runtime",
"namespace": "email-connect",
"uid": "fb6ab8d5-9f26-4f41-b1d9-b2ea596c7534",
"generation": 2,
"store": {
"kind": "ClusterSecretStore",
"name": "openbao-email-connect"
},
"remote_keys": [
{
"key": "platform/workloads/email-connect/transactional",
"property": "EMAIL_CONNECT_INGEST_TOKEN",
"secretKey": "EMAIL_CONNECT_INGEST_TOKEN"
},
{
"key": "platform/workloads/email-connect/transactional",
"property": "EMAIL_CONNECT_SMTP_USERNAME",
"secretKey": "EMAIL_CONNECT_SMTP_USERNAME"
},
{
"key": "platform/workloads/email-connect/transactional",
"property": "EMAIL_CONNECT_SMTP_PASSWORD",
"secretKey": "EMAIL_CONNECT_SMTP_PASSWORD"
}
],
"conditions": [
{
"type": "Ready",
"status": "True",
"reason": "SecretSynced"
}
]
},
"pods": [
{
"name": "email-connect-66cf8b84dd-24qkr",
"namespace": "email-connect",
"uid": "8f6a2a7b-ed0b-4ec0-9ffc-9f5bbaed6f0b",
"generation": 1,
"phase": "Running",
"containers": [
{
"name": "email-connect",
"ready": true,
"imageID": "forgejo.coulomb.social/coulomb/email-connect@sha256:5d79fd1fec15d3a279383820f170d263de3a55f5308df01a0a34732ebaea6d3d",
"restartCount": 0
}
]
}
],
"network_policies": [
{
"name": "email-connect-default-deny",
"spec": {
"podSelector": {},
"policyTypes": [
"Ingress",
"Egress"
]
}
},
{
"name": "email-connect-egress",
"spec": {
"egress": [
{
"ports": [
{
"port": 53,
"protocol": "UDP"
},
{
"port": 53,
"protocol": "TCP"
}
],
"to": [
{
"namespaceSelector": {
"matchLabels": {
"kubernetes.io/metadata.name": "kube-system"
}
}
}
]
},
{
"ports": [
{
"port": 587,
"protocol": "TCP"
}
],
"to": [
{
"ipBlock": {
"cidr": "0.0.0.0/0",
"except": [
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16"
]
}
}
]
}
],
"podSelector": {
"matchLabels": {
"app.kubernetes.io/name": "email-connect"
}
},
"policyTypes": [
"Egress"
]
}
},
{
"name": "email-connect-user-engine-ingress",
"spec": {
"ingress": [
{
"from": [
{
"namespaceSelector": {
"matchLabels": {
"kubernetes.io/metadata.name": "user-engine"
}
},
"podSelector": {
"matchLabels": {
"app.kubernetes.io/name": "user-engine"
}
}
}
],
"ports": [
{
"port": 8080,
"protocol": "TCP"
}
]
}
],
"podSelector": {
"matchLabels": {
"app.kubernetes.io/name": "email-connect"
}
},
"policyTypes": [
"Ingress"
]
}
}
],
"cronjobs": [],
"secrets_read": false,
"mail_sent": false,
"mailbox_accessed": false,
"production_data_read": false,
"running_process_configuration": {
"public_environment": {
"EMAIL_CONNECT_SENDER": "forgejo@coulomb.social",
"EMAIL_CONNECT_SMTP_HOST": "smtp.ionos.de",
"EMAIL_CONNECT_SMTP_PORT": "587",
"EMAIL_CONNECT_SMTP_SECURITY": null,
"EMAIL_CONNECT_PORTAL_URL": "https://users.coulomb.social"
},
"transactional_source_sha256": "36fdfd8cb58f10148cd491a7e34d7aea798e4289fce760af70a48776bede204d"
},
"additional_packaged_sources_sha256": {
"callers": "06fe961b9030dba2c0b26b92f8097e8fbd24e205d5fa79642aa9af60382886dc",
"scanner": "6266cbad2dbc610c1f6df1c051ded2ee55b43d7ff30af6eab38721924931c1ed",
"storage": "57fbd6d3b2e7541c0bf52d87a4f7361a4ff4a03511620d2a1c3789632b73afeb",
"parser": "786b291256ddfac5a8622c743622508cdd9f45f06156d0f5487ce399fad1287c"
},
"additional_packaged_sources_match_checkout": true
}

View file

@ -0,0 +1,37 @@
{
"schema": "coulomb.mail-return-boundary-rehearsal.v1",
"completed_at": "2026-10-31T10:25:09.945402+00:00",
"source_sha256": {
"src/email_connect/__init__.py": "0f1ae5e9251f80b0bd5220b1d7bf91735d6ea273e07aa26cb7852a7950be7087",
"src/email_connect/adapter_contract.py": "5059b2eaaac2408f4e247bb468a97960eb3f823e63c3eeb151a1c2250e1f3bbf",
"src/email_connect/callers.py": "06fe961b9030dba2c0b26b92f8097e8fbd24e205d5fa79642aa9af60382886dc",
"src/email_connect/cli.py": "6ac6ae8acd091ba2d2102589be3b75fe3a48392309e40e61f8383deb3329ad42",
"src/email_connect/config.py": "6ca89fb7c3e1f35bc4faedfba8c3313d4366b2cb7830ef88ed3f7d2313f7867e",
"src/email_connect/evidence.py": "42cca03234a79dc78033ec1fbaf0b1785e6089ef3f3128e2e9d666ed239a3582",
"src/email_connect/mailbox.py": "7f0fe7e19ce1f814269609c4236152042f0f49bf3b517114b3b3f6f482471362",
"src/email_connect/models.py": "7f8136646b663a097280286dccce76a90abb7190ea0712fff7da32d47526521c",
"src/email_connect/parser.py": "786b291256ddfac5a8622c743622508cdd9f45f06156d0f5487ce399fad1287c",
"src/email_connect/recipients.py": "68792f44f0d2ba204649542aa8bd6b0f209d89c675baa4fc0767f1250a29045f",
"src/email_connect/reporting.py": "8f8ce5f98e4347b778893d08a9e58f2bab3b44b80c159fcf52d0b5772429737c",
"src/email_connect/scanner.py": "6266cbad2dbc610c1f6df1c051ded2ee55b43d7ff30af6eab38721924931c1ed",
"src/email_connect/storage.py": "57fbd6d3b2e7541c0bf52d87a4f7361a4ff4a03511620d2a1c3789632b73afeb",
"src/email_connect/transactional.py": "36fdfd8cb58f10148cd491a7e34d7aea798e4289fce760af70a48776bede204d"
},
"probe_sha256": "90dec8a764015cf7fb077f45744b27d39fcb77990a5a16185a2ec3581c54fb0c",
"transactional_source_matches_native": true,
"smtp_transport": "in-process synthetic replacement; no SMTP connection",
"message_id_correlates_with_ledger_and_synthetic_bounce": true,
"explicit_reply_to_or_return_path_headers": false,
"scanner_deduplicates_evidence": true,
"scanner_updates_transactional_suppression": false,
"ordinary_sender_suppress_and_unsuppress_denied": true,
"synthetic_operator_suppression_blocks_send_and_survives_restart": true,
"suppression_scope": "recipient-global across callers and tenants",
"explicit_synthetic_operator_unsuppression_verified": true,
"native_operator_admission_proved": false,
"received_mail_authenticity_or_correlation_authority_proved": false,
"python_network_operations_blocked": true,
"temporary_ledgers_removed": true,
"mail_sent": false,
"production_mailbox_accessed": false
}

View file

@ -0,0 +1,18 @@
{
"workplan_id": "38208f72-c285-56fa-be44-de9b59a4e031",
"id": "9392ef52-3bfe-42fd-b6d5-ec0730656775",
"topic_id": null,
"title": "Platform-root owns platform mailbox replies and bounces",
"description": "For platform@coulomb.social human replies and bounces, including registration and invitation mail, the user selected platform-root with Bernd Worsch as the human operator. This settles accountable support/return-mail ownership under CSOC-WP-0015-T02.",
"decision_type": "pending",
"status": "resolved",
"rationale": "Explicit user response in this session: platform-root, with me as the human operator (recommended). Operational mailbox access, email-connect platform SMTP admission, correlated handling, retention and delivery proof remain separate unfinished implementation/acceptance requirements. No forwarding rule or automatic mailbox scan was requested.",
"decided_by": "Bernd Worsch (explicit user response)",
"decided_at": "2026-10-31T10:24:03.783410Z",
"deadline": null,
"escalation_note": null,
"superseded_by": null,
"created_at": "2026-10-31T10:24:06.946680Z",
"updated_at": "2026-10-31T10:24:06.946680Z",
"workstream_id": "38208f72-c285-56fa-be44-de9b59a4e031"
}

View file

@ -0,0 +1,231 @@
{
"schema": "coulomb.platform-mail-sender-candidate.v1",
"observed_at": "2026-10-31T10:28:30.447584+00:00",
"deployment_uid": "2e98e52a-a2d1-4c52-9ac8-6efbc0a18e72",
"deployment_generation": 74,
"image": "forgejo.coulomb.social/coulomb/email-connect@sha256:5d79fd1fec15d3a279383820f170d263de3a55f5308df01a0a34732ebaea6d3d",
"new_resources": [
{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": {
"name": "email-connect-platform-smtp-eso",
"namespace": "email-connect"
},
"automountServiceAccountToken": false
},
{
"apiVersion": "external-secrets.io/v1",
"kind": "SecretStore",
"metadata": {
"name": "openbao-email-connect-platform-smtp",
"namespace": "email-connect"
},
"spec": {
"provider": {
"vault": {
"server": "http://openbao.openbao.svc:8200",
"path": "platform",
"version": "v2",
"auth": {
"kubernetes": {
"mountPath": "kubernetes",
"role": "email-connect-platform-smtp-eso",
"serviceAccountRef": {
"name": "email-connect-platform-smtp-eso",
"audiences": [
"openbao"
]
}
}
}
}
}
}
},
{
"apiVersion": "external-secrets.io/v1",
"kind": "ExternalSecret",
"metadata": {
"name": "email-connect-platform-smtp",
"namespace": "email-connect"
},
"spec": {
"refreshInterval": "1h",
"secretStoreRef": {
"kind": "SecretStore",
"name": "openbao-email-connect-platform-smtp"
},
"target": {
"name": "email-connect-platform-smtp",
"creationPolicy": "Owner",
"deletionPolicy": "Retain",
"template": {
"type": "Opaque"
}
},
"data": [
{
"secretKey": "password",
"remoteRef": {
"key": "workloads/railiance-telemetry/smtp",
"property": "SMTP_PASSWORD"
}
}
]
}
}
],
"deployment_json_patch": [
{
"op": "test",
"path": "/metadata/uid",
"value": "2e98e52a-a2d1-4c52-9ac8-6efbc0a18e72"
},
{
"op": "test",
"path": "/metadata/resourceVersion",
"value": "83014220"
},
{
"op": "test",
"path": "/spec/template/spec/containers/0/image",
"value": "forgejo.coulomb.social/coulomb/email-connect@sha256:5d79fd1fec15d3a279383820f170d263de3a55f5308df01a0a34732ebaea6d3d"
},
{
"op": "test",
"path": "/spec/template/spec/containers/0/envFrom",
"value": [
{
"configMapRef": {
"name": "email-connect-config"
}
},
{
"secretRef": {
"name": "email-connect-runtime",
"optional": false
}
}
]
},
{
"op": "test",
"path": "/spec/template/spec/containers/0/env",
"value": [
{
"name": "EMAIL_CONNECT_CALLERS_FILE",
"value": "/etc/email-connect-callers/callers.json"
}
]
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/envFrom",
"value": [
{
"configMapRef": {
"name": "email-connect-config"
}
}
]
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/env",
"value": [
{
"name": "EMAIL_CONNECT_CALLERS_FILE",
"value": "/etc/email-connect-callers/callers.json"
},
{
"name": "EMAIL_CONNECT_SMTP_USERNAME",
"value": "platform@coulomb.social"
},
{
"name": "EMAIL_CONNECT_SMTP_PASSWORD",
"valueFrom": {
"secretKeyRef": {
"name": "email-connect-platform-smtp",
"key": "password",
"optional": false
}
}
}
]
}
],
"configmap_json_patch": [
{
"op": "test",
"path": "/metadata/uid",
"value": "558d3338-c4c2-490c-a5e5-af206c39eb51"
},
{
"op": "test",
"path": "/data/EMAIL_CONNECT_SENDER",
"value": "forgejo@coulomb.social"
},
{
"op": "replace",
"path": "/data/EMAIL_CONNECT_SENDER",
"value": "platform@coulomb.social"
}
],
"native_server_dry_run": [
{
"kind": "ServiceAccount",
"name": "email-connect-platform-smtp-eso",
"accepted": true
},
{
"kind": "SecretStore",
"name": "openbao-email-connect-platform-smtp",
"accepted": true
},
{
"kind": "ExternalSecret",
"name": "email-connect-platform-smtp",
"accepted": true
},
{
"kind": "Deployment",
"name": "email-connect",
"accepted": true,
"only_envFrom_and_env_changed": true
},
{
"kind": "ConfigMap",
"name": "email-connect-config",
"accepted": true,
"only_sender_changed": true
}
],
"live_caller_policy": {
"name": "email-connect-callers-user-engine-276257849bf2",
"sha256": "276257849bf260d844e8e0767deda7edd2f3d807f12794b869f871b75b255a07",
"callers": [
{
"caller_id": "user-engine",
"operations": [
"send",
"delivery-status",
"status",
"verify",
"consume",
"cancel"
],
"templates": [
"family_member.invited",
"family_invitation.resent"
]
}
]
},
"platform_mailbox_admission_executed": false,
"resources_installed": false,
"mail_sent": false,
"secrets_read": false,
"execution_ready": false,
"preparer_sha256": "c7d8bbda1b2807b50d46ee04703cb1d0b465c514f578185a041d7b34e59a95d5"
}

View file

@ -0,0 +1,131 @@
# Platform sender and return-mail operations
WP-0015-T02 is in progress. **Ownership is settled; sender migration and monitored
return-mail operation are not yet deployed or accepted.** Decision
9392ef52-3bfe-42fd-b6d5-ec0730656775 records the user's choice: platform-root is
accountable for replies and bounces at platform@coulomb.social, with Bernd Worsch
as the human operator, including registration and invitation mail.
This decision does not create a forwarding rule to Gmail. The existing Gmail
address remains the separately accepted backup-alert destination. No mailbox
credentials, message bodies or live recipient records were read during this work.
## Current evidence
| Requirement | Verified evidence | Remaining return |
| --- | --- | --- |
| Sender | Native generation74, one Ready email-connect replica, image5d79fd1f; ConfigMap and container environment both use forgejo@coulomb.social | Admit and deploy the dedicated platform sender below |
| Credential custody | Existing email-connect runtime projection is Ready; platform mailbox is separately admitted for telemetry and Authelia | Admit email-connect as an additional scoped consumer through railiance-platform; existing consumers' grants are not transferable |
| Support/return owner | User explicitly selected platform-root and Bernd; decision9392ef52 | Prove that this operator can access the intended inbox and handle selected returns |
| Public DNS | All four authoritative servers agree on two IONOS MX records, IONOS SPF include, DMARC p=none, and three documented DKIM CNAME records | Inspect recipient authentication results and actual envelope/signature alignment for a specifically authorized new-sender message; DNS presence alone does not prove message authentication |
| Return correlation | Synthetic accepted Message-ID is retained in the delivery ledger and recovered from a synthetic hard bounce by the actual scanner | Native selected-message correlation, authoritative recipient/caller binding and operator handling |
| Suppression | Existing scoped API denies ordinary senders; synthetic operator suppression blocks later sends and survives restart | No operator caller is currently admitted. Suppression is global by recipient across callers/tenants; that effect needs explicit operator-policy admission |
| Retention | Source has no automatic evidence-retention/purge operation connecting the scanner and transactional ledger | Agree mailbox/evidence retention and controlled expiry while preserving pending sends, challenges and suppression decisions |
Native observation: `communication-mail-native-2026-10-31.json`. The packaged
transactional, caller, scanner, parser and storage source hashes match the owner
checkout. The separately captured live policy grants user-engine send/status/
verification/consume/cancel only; it grants no suppress/unsuppress operation.
No CronJob exists in the email-connect namespace; this observation does not rule
out a manual or external operator elsewhere.
DNS evidence: `communication-mail-dns-2026-10-31.json`, 24 authoritative queries.
The DKIM selector names and CNAME targets were checked against
[IONOS's own documentation](https://www.ionos.com/help/domains/configuring-mail-servers-and-other-related-records/email-authentication-with-dkim/).
The current records are `s1-ionos`, `s2-ionos` and `s42582890`, pointing to their
corresponding `dkim.ionos.com` names. The observed DMARC record is exactly
`v=DMARC1; p=none`; it contains no reporting destination. No DNS change is proposed
by this packet, and no signed message or destination DKIM public-key TXT was
verified here.
## Concrete sender candidate
`platform-mail-sender-2026-10-31/candidate.json` contains the exact native
preconditions, three new resources and two guarded JSON patches. All five
operations passed native server dry-run; nothing was installed.
- Dedicated ServiceAccount `email-connect/email-connect-platform-smtp-eso`, with
automatic token mounting disabled.
- Namespace SecretStore `openbao-email-connect-platform-smtp`, using that exact
service account, the OpenBao audience and dedicated Kubernetes-auth role.
- ExternalSecret `email-connect-platform-smtp`, projecting only `SMTP_PASSWORD`
from existing `platform/workloads/railiance-telemetry/smtp` into key `password`.
No mailbox credential copy or rotation is proposed.
- Change the existing ConfigMap sender to `platform@coulomb.social`.
- Remove the old runtime Secret from Deployment `envFrom`. Set the public SMTP
username to platform@coulomb.social and take the password from the new
projection explicitly. Preserve the current caller-policy and caller-credential
mounts; no legacy shared ingest fallback is reintroduced.
The whole Deployment spec comparison permits only those environment changes;
image, probes, resources, PVC, service account and volume mounts remain identical.
The ConfigMap comparison permits only the sender change. Existing legacy SMTP
custody is retained outside the pod for controlled rollback; it is not deleted.
Existing SMTP egress and fixed invitation/verification templates remain unchanged.
The owner must admit the exact role/policy with a read-only mailbox path,
OpenBao audience, maximum15-minute token and no default policy, with positive
consumer/negative other-consumer and coding-agent boundaries. Reuse the fixed
owner lane demonstrated in RPF-WP-0052 and
`railiance-platform/scripts/authelia_smtp_delivery.py`; its historical approval
applies to Authelia, not this additional consumer. SMTP AUTH proof must not submit
mail. The application sender migration needs a separate supervised configuration
executor and independent postflight after the scoped projection is Ready.
This candidate is **not execution-ready**: no owner admission helper/receipt or
supervised rollout packet exists for this new consumer yet. Resource-version and
identity tests must be refreshed and re-reviewed at execution time. Do not apply
these individual patches by hand as a substitute for that packet.
Rollback must reconcile any in-flight sends, restore the prior sender and exact
credential references, and verify Ready without replaying uncertain submissions.
Keep both prior and new custody evidence until that outcome is established. No
ledger restore, mailbox rotation or deletion of the existing projections belongs
to this sender change.
Reproduce preparation with a new output path:
```sh
python3 scripts/prepare-platform-mail-sender.py --output /tmp/new-platform-mail-candidate.json
```
This script performs reads and `--dry-run=server` operations only, refuses unknown
environment fields and preserves existing output. Its resulting packet records
the preparer hash. Dry-run does not prove that the new OpenBao role exists or that
the projected password is usable.
## Return-mail boundary rehearsal and operating design
Run the retained offline probe:
```sh
python3 scripts/rehearse-mail-return-boundary.py \
--owner-repo /home/worsch/email-connect \
--native-evidence docs/release/communication-mail-native-2026-10-31.json
```
It uses temporary ledgers, synthetic identities and an in-process SMTP replacement;
Python connection/bind/DNS/sendto operations are denied. It exercises real owner
sources and removes its fixtures afterward. Proof:
`mail-return-boundary-rehearsal-2026-10-31.json`.
The scanner classifies and deduplicates a hard bounce, preserving Original-Message-ID,
but leaves transactional suppression unchanged. A synthetic operator policy can
suppress and explicitly unsuppress; an ordinary sender receives403. Suppression
also blocks a different synthetic caller/tenant using the same recipient. This
is a capability/boundary proof, not a native operator grant or an automatic return
processor. The SMTP source emits Message-ID and From but no explicit Reply-To or
Return-Path header; actual return delivery remains provider/recipient evidence.
For the initial operating path, use an attended, explicitly selected return rather
than a blanket mailbox scan. Correlate the accepted Message-ID and authoritative
outbound recipient/caller record before considering suppression. A parsed body or
matching text can be forged and is not sufficient authority. Unknown/unmatched
returns, temporary deferrals and automatic replies remain review items; they must
not silently acquire permanent suppression. No automatic replies are introduced.
The next implementation must provide a bounded, auditable operator action, define
its global-recipient effect and release policy, and agree retention. It must retain
proof of why suppression changed and refuse unrelated/uncertain records. Productive
inbox access, selected-message authentication/correlation and the operator response
remain acceptance gates alongside the sender rollout.

View file

@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""Prepare a fixed email-connect sender candidate using reads and server dry-runs.
No create/apply/patch is executed without --dry-run=server. This does not admit
OpenBao access, install a resource, scan a mailbox or send an email.
"""
import argparse, os, subprocess,shlex,json,hashlib
from pathlib import Path
from datetime import datetime,timezone
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', type=Path, required=True)
args = parser.parse_args()
if not __debug__:
raise SystemExit('Assertions must be enabled')
if args.output.exists():
raise SystemExit('Choose a new candidate output; existing evidence is retained')
code=r'''
import subprocess,json,hashlib
from datetime import datetime,timezone
K=['k3s','kubectl']
def get(kind,name):
return json.loads(subprocess.check_output(K+['get',kind,name,'-n','email-connect','-o','json'],timeout=20))
d=get('deployment','email-connect');cm=get('configmap','email-connect-config')
assert d['metadata']['uid']=='2e98e52a-a2d1-4c52-9ac8-6efbc0a18e72'
assert d['status']['observedGeneration']==d['metadata']['generation'] and d['status']['readyReplicas']==1
c=d['spec']['template']['spec']['containers'][0]
assert c['name']=='email-connect' and len(d['spec']['template']['spec']['containers'])==1
assert c['image']=='forgejo.coulomb.social/coulomb/email-connect@sha256:5d79fd1fec15d3a279383820f170d263de3a55f5308df01a0a34732ebaea6d3d'
assert c['envFrom']==[{'configMapRef':{'name':'email-connect-config'}},{'secretRef':{'name':'email-connect-runtime','optional':False}}]
assert cm['data']['EMAIL_CONNECT_SENDER']=='forgejo@coulomb.social'
assert all(e['name'] not in {'EMAIL_CONNECT_SMTP_USERNAME','EMAIL_CONNECT_SMTP_PASSWORD','EMAIL_CONNECT_SENDER'} for e in c['env'])
assert c['env']==[{'name':'EMAIL_CONNECT_CALLERS_FILE','value':'/etc/email-connect-callers/callers.json'}], 'unexpected environment; refuse to copy unknown values'
role='email-connect-platform-smtp-eso';store='openbao-email-connect-platform-smtp';secret='email-connect-platform-smtp'
def obj(api,kind,name,**extra):return {'apiVersion':api,'kind':kind,'metadata':{'name':name,'namespace':'email-connect'},**extra}
resources=[obj('v1','ServiceAccount',role,automountServiceAccountToken=False),obj('external-secrets.io/v1','SecretStore',store,spec={'provider':{'vault':{'server':'http://openbao.openbao.svc:8200','path':'platform','version':'v2','auth':{'kubernetes':{'mountPath':'kubernetes','role':role,'serviceAccountRef':{'name':role,'audiences':['openbao']}}}}}}),obj('external-secrets.io/v1','ExternalSecret',secret,spec={'refreshInterval':'1h','secretStoreRef':{'kind':'SecretStore','name':store},'target':{'name':secret,'creationPolicy':'Owner','deletionPolicy':'Retain','template':{'type':'Opaque'}},'data':[{'secretKey':'password','remoteRef':{'key':'workloads/railiance-telemetry/smtp','property':'SMTP_PASSWORD'}}]})]
for x in resources:
out=subprocess.check_output(K+['get',x['kind'],x['metadata']['name'],'-n','email-connect','--ignore-not-found','-o','name'],timeout=20)
assert not out.strip(),'candidate resource already exists'
base='/spec/template/spec/containers/0'
dpatch=[{'op':'test','path':'/metadata/uid','value':d['metadata']['uid']},{'op':'test','path':'/metadata/resourceVersion','value':d['metadata']['resourceVersion']},{'op':'test','path':base+'/image','value':c['image']},{'op':'test','path':base+'/envFrom','value':c['envFrom']},{'op':'test','path':base+'/env','value':c['env']},{'op':'replace','path':base+'/envFrom','value':[{'configMapRef':{'name':'email-connect-config'}}]},{'op':'replace','path':base+'/env','value':c['env']+[{'name':'EMAIL_CONNECT_SMTP_USERNAME','value':'platform@coulomb.social'},{'name':'EMAIL_CONNECT_SMTP_PASSWORD','valueFrom':{'secretKeyRef':{'name':secret,'key':'password','optional':False}}}]}]
cmpatch=[{'op':'test','path':'/metadata/uid','value':cm['metadata']['uid']},{'op':'test','path':'/data/EMAIL_CONNECT_SENDER','value':'forgejo@coulomb.social'},{'op':'replace','path':'/data/EMAIL_CONNECT_SENDER','value':'platform@coulomb.social'}]
def patchdry(kind,name,patch):
return json.loads(subprocess.check_output(K+['patch',kind,name,'-n','email-connect','--type=json','--dry-run=server','-p',json.dumps(patch),'-o','json'],timeout=30))
checks=[]
for x in resources:
p=subprocess.run(K+['create','--dry-run=server','-f','-','-o','json'],input=json.dumps(x).encode(),capture_output=True,timeout=30)
if p.returncode:raise RuntimeError('resource dry-run failed: '+x['kind'])
y=json.loads(p.stdout);checks.append({'kind':y['kind'],'name':y['metadata']['name'],'accepted':True})
da=patchdry('deployment','email-connect',dpatch);ca=patchdry('configmap','email-connect-config',cmpatch)
expected=json.loads(json.dumps(d['spec']));expected['template']['spec']['containers'][0]['envFrom']=dpatch[-2]['value'];expected['template']['spec']['containers'][0]['env']=dpatch[-1]['value']
assert da['spec']==expected,'unexpected deployment spec change'
expectedcm=dict(cm['data'],EMAIL_CONNECT_SENDER='platform@coulomb.social');assert ca['data']==expectedcm
checks.extend([{'kind':'Deployment','name':'email-connect','accepted':True,'only_envFrom_and_env_changed':True},{'kind':'ConfigMap','name':'email-connect-config','accepted':True,'only_sender_changed':True}])
volumes=d['spec']['template']['spec']['volumes']; policy_name=next(v['configMap']['name'] for v in volumes if v['name']=='caller-policy')
pol=get('configmap',policy_name)['data']['callers.json'];parsed=json.loads(pol)
publiccallers=[{k:x.get(k) for k in ('caller_id','operations','templates')} for x in parsed['callers']]
print(json.dumps({'schema':'coulomb.platform-mail-sender-candidate.v1','observed_at':datetime.now(timezone.utc).isoformat(),'deployment_uid':d['metadata']['uid'],'deployment_generation':d['metadata']['generation'],'image':c['image'],'new_resources':resources,'deployment_json_patch':dpatch,'configmap_json_patch':cmpatch,'native_server_dry_run':checks,'live_caller_policy':{'name':policy_name,'sha256':hashlib.sha256(pol.encode()).hexdigest(),'callers':publiccallers},'platform_mailbox_admission_executed':False,'resources_installed':False,'mail_sent':False,'secrets_read':False,'execution_ready':False}))
'''
r=subprocess.run(['ssh','-o','BatchMode=yes','-o','ConnectTimeout=10','railiance01','sudo -n python3 -c '+shlex.quote(code)],capture_output=True,text=True,timeout=180)
if r.returncode:raise SystemExit('read/dry-run preparation failed: '+r.stderr[-1500:])
j=json.loads(r.stdout)
j['preparer_sha256']=hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
fd=os.open(args.output,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
with os.fdopen(fd,'w') as target:
json.dump(j,target,indent=2);target.write('\n');target.flush();os.fsync(target.fileno())
print(json.dumps({k:j[k] for k in ('observed_at','deployment_generation','native_server_dry_run','live_caller_policy','execution_ready')},indent=2))

View file

@ -0,0 +1,169 @@
#!/usr/bin/env python3
"""Exercise owner mail sources using temporary ledgers and synthetic mail only.
This probe documents present capabilities and missing integration. It is not an
inbound worker, sender deployment, credential admission or suppression operator.
"""
from __future__ import annotations
import argparse
import hashlib
import io
import json
import sys
import tempfile
from datetime import datetime, timezone
from pathlib import Path
from unittest.mock import patch
def main():
if not __debug__:
raise SystemExit('Assertions must be enabled')
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--owner-repo', type=Path, required=True)
parser.add_argument('--native-evidence', type=Path, required=True)
args = parser.parse_args()
source = args.owner_repo.resolve() / 'src'
native = json.loads(args.native_evidence.read_text())
module = source / 'email_connect/transactional.py'
assert hashlib.sha256(module.read_bytes()).hexdigest() == native[
'running_process_configuration']['transactional_source_sha256']
# Fail any attempted Python network connection/bind, even if a future source
# change accidentally substitutes a real transport for these fixtures.
def no_network(event, _args):
if event in {'socket.connect', 'socket.bind', 'socket.getaddrinfo', 'socket.sendto'}:
raise RuntimeError('Offline rehearsal forbids network access')
sys.addaudithook(no_network)
sys.path.insert(0, str(source))
from email_connect.callers import CallerPolicy, ScopedTransactionalApplication
from email_connect.config import (AppConfig, MailboxConfig, ReportsConfig,
ScanConfig, SourceConfig, StorageConfig)
from email_connect.scanner import scan_mailbox
from email_connect.storage import StateStore
from email_connect.transactional import SMTPProvider, SQLiteDeliveryStore
captured = []
class SyntheticSMTP:
def __init__(self, *args, **kwargs):
self.tls = False
def __enter__(self):
return self
def __exit__(self, *args):
pass
def starttls(self, context):
assert context.check_hostname
self.tls = True
def login(self, username, password):
assert self.tls and username == 'fixture' and password == 'fixture-only'
def send_message(self, message):
assert message['To'] == 'recipient@example.test'
captured.append(message)
def policy(name, operations, tenant='tenant:fixture'):
return CallerPolicy(caller_id=name, token='fixture-credential-' + name,
source=name, bindings=frozenset({(tenant, 'client:fixture')}),
operations=frozenset(operations),
templates=frozenset({'family_member.invited'}),
rate_per_minute=60, burst=30, max_payload_bytes=8192)
sender = policy('fixture-sender', {'send', 'delivery-status'})
operator = policy('fixture-operator', {'suppress', 'unsuppress'})
other = policy('fixture-other', {'send'}, 'tenant:other')
def event(name, caller=sender, tenant='tenant:fixture'):
return {'id': name, 'source': caller.source, 'type': 'family_member.invited',
'tenant': tenant, 'client_id': 'client:fixture',
'data': {'primary_email': 'recipient@example.test', 'invitation_id': name}}
def invoke(app, payload, path='/v1/send', caller=sender):
raw = json.dumps(payload).encode(); result = {}
env = {'PATH_INFO': path, 'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': str(len(raw)),
'wsgi.input': io.BytesIO(raw), 'HTTP_AUTHORIZATION': 'Bearer ' + caller.token,
'HTTP_IDEMPOTENCY_KEY': payload.get('id', '')}
body = b''.join(app(env, lambda status, headers: result.update(status=int(status[:3]))))
return result['status'], json.loads(body)
with tempfile.TemporaryDirectory(prefix='csoc-mail-return-') as tmp, patch(
'email_connect.transactional.smtplib.SMTP', SyntheticSMTP):
root = Path(tmp)
ledger = SQLiteDeliveryStore(str(root / 'transactional.sqlite'))
provider = SMTPProvider('smtp.example.test', 587, 'fixture', 'fixture-only',
'platform@example.test')
app = ScopedTransactionalApplication(ledger, provider, 'https://issuer.example.test',
[sender, operator, other])
assert invoke(app, event('invitation-1'))[0] == 202
assert len(captured) == 1
message = captured[0]
reference = str(message['Message-ID'])
assert message['From'] == 'platform@example.test'
assert message['Reply-To'] is None and message['Return-Path'] is None
assert ledger.db.execute('SELECT count(*) FROM deliveries WHERE provider_ref=?',
(reference,)).fetchone()[0] == 1
fixture = root / 'mailbox'; fixture.mkdir()
(fixture / 'bounce.eml').write_text(
'From: mailer-daemon@example.test\nTo: platform@example.test\n'
'Subject: Delivery Status Notification (Failure)\n'
'Date: Sat, 31 Oct 2026 10:00:00 +0000\n'
'Message-ID: <fixture-bounce@example.test>\n'
'Content-Type: text/plain; charset=utf-8\n\n'
'Delivery failure.\nFinal-Recipient: rfc822; recipient@example.test\n'
'Action: failed\nStatus: 5.1.1\nDiagnostic-Code: smtp; 550 User unknown\n'
'Original-Message-ID: ' + reference + '\n')
config = AppConfig(mailbox=MailboxConfig(id='fixture-mailbox', protocol='fixture'),
scan=ScanConfig(), storage=StorageConfig(str(root / 'scanner.sqlite')),
reports=ReportsConfig(str(root / 'reports')),
source=SourceConfig(fixture_dir=str(fixture)))
scan = scan_mailbox(config)
evidence = StateStore(config.storage.path)
rows = evidence.evidence_rows()
assert scan.scan.messages_seen == 1 and len(rows) == 1
assert rows[0]['original_message_id'] == reference
assert rows[0]['affected_email_address'] == 'recipient@example.test'
assert rows[0]['assessment_subclass'] == 'fail.hard_bounce'
assert not ledger.is_suppressed('recipient@example.test')
repeat = scan_mailbox(config)
assert repeat.scan.messages_new == 0 and repeat.scan.evidence_events_created == 0
payload = {'recipient': 'recipient@example.test', 'reason': 'hard_bounce'}
assert invoke(app, payload, '/v1/suppressions')[0] == 403
assert not ledger.is_suppressed('recipient@example.test')
assert invoke(app, payload, '/v1/suppressions', operator)[0] == 200
assert invoke(app, event('invitation-2')) == (400, {'error': 'recipient_suppressed'})
assert invoke(app, event('invitation-3', other, 'tenant:other'), caller=other) == (
400, {'error': 'recipient_suppressed'})
assert len(captured) == 1
ledger.db.close()
ledger = SQLiteDeliveryStore(str(root / 'transactional.sqlite'))
assert ledger.is_suppressed('recipient@example.test')
app = ScopedTransactionalApplication(ledger, provider, 'https://issuer.example.test',
[sender, operator, other])
assert invoke(app, payload, '/v1/suppressions/remove')[0] == 403
assert invoke(app, payload, '/v1/suppressions/remove', operator)[0] == 200
assert not ledger.is_suppressed('recipient@example.test')
assert len(captured) == 1
ledger.db.close(); evidence.close()
assert not root.exists()
print(json.dumps({
'schema': 'coulomb.mail-return-boundary-rehearsal.v1',
'completed_at': datetime.now(timezone.utc).isoformat(),
'source_sha256': {str(p.relative_to(args.owner_repo.resolve())):
hashlib.sha256(p.read_bytes()).hexdigest()
for p in sorted((source / 'email_connect').glob('*.py'))},
'probe_sha256': hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
'transactional_source_matches_native': True,
'smtp_transport': 'in-process synthetic replacement; no SMTP connection',
'message_id_correlates_with_ledger_and_synthetic_bounce': True,
'explicit_reply_to_or_return_path_headers': False,
'scanner_deduplicates_evidence': True,
'scanner_updates_transactional_suppression': False,
'ordinary_sender_suppress_and_unsuppress_denied': True,
'synthetic_operator_suppression_blocks_send_and_survives_restart': True,
'suppression_scope': 'recipient-global across callers and tenants',
'explicit_synthetic_operator_unsuppression_verified': True,
'native_operator_admission_proved': False,
'received_mail_authenticity_or_correlation_authority_proved': False,
'python_network_operations_blocked': True,
'temporary_ledgers_removed': True,
'mail_sent': False, 'production_mailbox_accessed': False,
}, indent=2))
if __name__ == '__main__':
main()

View file

@ -18,7 +18,14 @@ Residual acceptance from CSOC-WP-0010, split at the user's request. Existing imp
## Current execution map — 2026-10-31
T01 caller policy, T03 independent ledger recovery and the narrowed T04 implementation/native rehearsal/application release are complete. T02 still owns the general platform sender, reply/inbox/support, DNS and bounce operation; backup-specific Gmail receipt does not close it. T05 exposes productive flow verdicts and T06 the remaining native reset-authority owner return.
T01 caller policy, T03 independent ledger recovery and the narrowed T04
implementation/native rehearsal/application release are complete. T02 owns the
platform sender and return-mail operation. Platform-root with Bernd as human
operator is now explicitly accepted; see decision 9392ef52 below. Sender admission,
inbox access and return-message correlation remain unverified. Suppression
handling and retention also remain open. Backup-specific
Gmail receipt does not close those gates. T05 exposes productive flow verdicts
and T06 the remaining native reset-authority owner return.
The [four-workplan assessment](../docs/release/goal-progress-audit-2026-10-31.md)
records the scope split and effort estimate. Task counts are not effort percentages.
@ -46,13 +53,34 @@ flow verdicts remain T04. See docs/release/acceptance-audit-2026-10-27.md.
```task
id: CSOC-WP-0015-T02
status: todo
status: progress
priority: high
state_hub_task_id: "0604cb15-72c7-5d15-bb42-0335d4ec80df"
```
Review replacing the receiver forgejo sender with platform identity through its credential owner, then name reply/inbox/support owner and prove authorized inbound access, DNS MX/SPF/DKIM/DMARC, correlated bounce/suppression and retention. No unsolicited scans or auto responses. Existing native personal reset is accepted and stays separately evidenced.
2026-10-31 concrete follow-through: T02 advanced from todo to progress. User
selected platform-root with Bernd Worsch as human operator for platform-mail
replies/bounces, including registration/invitation; decision9392ef52-3bfe-42fd-b6d5-ec0730656775
is resolved. Native generation74/source hashes confirm the receiver still uses
forgejo@coulomb.social and has no admitted suppression caller. Four authoritative
nameservers agree on MX/SPF/DMARC and three documented DKIM CNAMEs. This is DNS
publication evidence, not delivered-message authentication.
Prepared three scoped SMTP projection resources and exact sender/environment
patches; all five native server dry-runs pass with unchanged image/PVC/caller
policy and unrelated spec. New consumer custody and supervised rollout remain
unimplemented, so execution_ready is false. Offline actual-owner-source proof
shows accepted Message-ID/bounce correlation and scanner deduplication, but no
scanner-to-transactional suppression bridge; the existing operator API works only
under synthetic admission and affects a recipient across callers/tenants. No
production send, mailbox scan, credential read or runtime change occurred.
Concrete packet and remaining sequence:
docs/release/platform-mail-sender-review-2026-10-31.md. Owner identity need not be
asked again. Next: fixed owner SMTP admission and supervised sender rollout;
bounded operator return handling plus retention and productive access proof.
## T03 — Complete independent ledger recovery and copy custody
```task