MAS-WP-0003-T05: provider results in review, spec change proposals, gate recommendation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 252470@bnt-lap001
Assistant-Session: ad628c8f-4be2-4979-94c9-76c7ed6f8241
This commit is contained in:
tegwick 2026-10-18 13:34:21 +02:00
parent d4585eb542
commit d7264e7ec5
3 changed files with 237 additions and 17 deletions

View file

@ -1,4 +1,4 @@
# MAS v0.1 experiment review (first pass, 2026-10-08)
# MAS v0.1 experiment review (2026-10-08, provider pass 2026-10-18)
Status of MAS-WP-0002, Milestone 9. The answers below come from building
`mas_core`, the `mas` CLI, `mas-gateway` and the reference ticket service,
@ -8,9 +8,15 @@ loopback fixture servers).
**Scope:** the MTA half of the email path was tested against a real
Postfix (3.7.11, Debian bookworm, in Docker). The setup was SMTP → Postfix
→ `pipe` transport → `mas-gateway` → reference ticket service
(`experiments/postfix-probe/`). Mailbox providers (Gmail, Outlook,
corporate gateways) and public DNS/MX were not involved. That half moves
to **MAS-WP-0003**.
(`experiments/postfix-probe/`).
**Provider pass (MAS-WP-0003, 2026-10-18):** the same stack ran as a live MX
for `mas.coulomb.social` on railiance01 (`deploy/railiance/`), with the
statute at `https://mas.coulomb.social/.well-known/mas`. Real mail came from
Gmail and Microsoft 365, and replies went out through Scaleway TEM. The
evidence is the gateway's JSONL event log plus the Postfix log. Forwarding
and mailing-list paths (T04) are still pending. Spec consequences are
collected in `docs/spec-change-proposals.md`.
## Answers to the review questions
@ -95,11 +101,56 @@ The results:
decision. The gateway forwarded it as `valid: false`, and the app
ignored it.
**Still untested (MAS-WP-0003):**
- whether senders' providers and web forms accept `!` and `=` in recipient
addresses;
- overlap with SRS (`SRS0=...`) and VERP when forwarding;
- facet stripping by providers.
**Providers (tested, MAS-WP-0003):** four address forms were sent from
four sender setups:
- forms: `ticket!callback@`, `ticket=ID@`, `ticket=ID+comments@`,
`ticket=RE=2026-4711@`;
- sender setups: Gmail web, the Gmail mobile app, and a Microsoft 365 tenant via
both Outlook mobile and Outlook desktop.
| Sender | Compose form | Envelope recipient and To header | Transport |
|---|---|---|---|
| Gmail web | accepted all 4 unquoted | byte-identical | 209.85.x, STARTTLS, BDAT |
| Gmail mobile | accepted all 4 unquoted | byte-identical | same |
| M365, Outlook mobile | accepted all 4 unquoted | byte-identical | EOP 40.107.149.x |
| M365, Outlook desktop | accepted all 4 unquoted | byte-identical | EOP 52.101.171.x |
- **Nothing was rewritten, quoted, rejected or stripped.** That covers `!`,
a doubled `=` and the `+comments` facet. The gateway classified every
message as the probe predicted: P2 function with `callback` → 201
created; P1 resource → 202; the facet was recognised.
- **The reply direction also holds.** The service replied From
`ticket=GS-85QJ8WC09P@mas.coulomb.social` through Scaleway TEM. Gmail
showed the `=` From address as sent, with SPF, DKIM
(d=mas.coulomb.social) and DMARC all passing. A plain Gmail "Reply" went
back to that address byte-identical, with correct `In-Reply-To` and
`References`, and was correlated to the ticket (200).
- **Not covered:** consumer Outlook.com (the operator has no account; it
uses the same Exchange Online path as M365), the Outlook-on-the-web
compose form, and other providers such as Apple, Yahoo and GMX. Two
providers is evidence that `!` and `=` work in practice; it is not a
survey.
**Operational findings from the live run:**
- **Stock Postfix kills P2.** `smtpd_relay_restrictions` must contain a
reject-class entry, or smtpd refuses to start. Also, unless
`allow_untrusted_routing = yes`, Postfix ignores access-map results for
local-parts containing `!` or `%` (it treats them as sender-specified
routing). `reject_unauth_destination` then refuses every P2 address. The
working MAS host config relays only `@DOMAIN` through an explicit
regexp, turns off `swap_bangpath` and `allow_percent_hack`, and ends the
list in `reject`. A host that "just enables MAS" on a default Postfix
will silently lose all `!` mail.
- **A TEM "blackhole" MX swallowed inbound mail.** Scaleway TEM's domain
setup suggests `MX 10 blackhole.tem.scaleway.com`. Added next to the real
MX at the same priority, it sent about half of all inbound mail to a
discard host, and nobody got a bounce. Sending providers' setup wizards
are a deliverability risk for MAS subdomains that receive mail.
- **Outbound port 25 was blocked** at the hosting provider (and on a
second host). Replies need an authenticated submission relay.
**Still untested (T04):** overlap with SRS (`SRS0=...`) and VERP when
forwarding, and facet stripping by forwarders and lists.
### Resource addressing: did it improve correlation?
Yes. The service replies *from* `ticket=GS-…@great-service.example`, so an
@ -133,6 +184,12 @@ should either say this explicitly, or define "type intake" semantics for
an opaque target that names a declared resource type.
### Security: did any address start acting like a capability?
(Addendum from the live run: an intake address such as `ticket!callback@`
that auto-replies turns any forged `From:` into outbound mail to a third
party. That is a backscatter/reflection vector at the application layer.
The live deployment therefore caps all replies sent (300 in total, T06)
and discards Postfix's own bounces. See SCP-5.)
Not for reading or control. Knowing a ticket address grants no read access
and no admin action. It does let anyone **append correspondence** to an
open ticket, which is the intended function of a communication endpoint.
@ -140,10 +197,20 @@ That is also an injection and spam vector. Ticket ids are non-guessable
(10 Crockford base32 characters), but sender policy belongs to the
application, not to MAS.
## Next step (MAS-WP-0003, before the decision gate)
Run the provider half on a real subdomain:
- an MX pointing to a Postfix configured like the probe;
- the statute served at `https://<sub>/.well-known/mas`;
- test mail from Gmail, Outlook and one corporate gateway to
`ticket!callback@`, `ticket=ID@` and `ticket=ID+comments@`, plus replies
to the service's `ticket=ID@` From address.
## Decision gate (MAS-WP-0002), recommendation
The gate asks which additions answer a need the experiment demonstrated.
This is a recommendation; the decision is the founder's.
| Candidate | Demonstrated need? | Recommendation |
|---|---|---|
| Postfix/Exim integration | **Yes.** Default Postfix drops every P2 address, and the correct relay config is non-obvious | Build first: a documented, tested Postfix profile (later Exim) based on `deploy/railiance/entrypoint.sh` |
| Spec v0.1.1 errata | **Yes**, SCP-1 to SCP-5 | Do alongside the integration |
| Address composer | Weak. Providers accepted hand-typed addresses; the 64-octet budget and facet/interaction exclusivity are the only traps | Defer; `mas generate` in the CLI covers it |
| Mail-client inspector | Not shown; nothing was rewritten that a user would need to see | Defer |
| TypeScript / Go implementations | Not shown; no consumer outside Python yet | Defer until a second consumer exists |
| Signed statutes | Not shown; pinned local statutes covered the operator case (see Discovery) | Defer |
| P3 economic profile | Not exercised | Defer |
T04 (SRS/VERP) could change the `!`/`=` row for forwarded mail. Revisit
this table once T04 is done.

View file

@ -0,0 +1,145 @@
# MAS v0.1 spec change proposals (from MAS-WP-0002/0003)
Status: **draft for review**. These are proposals against
`mas-v0.1-spec/`. The spec bundle stays unchanged until they are accepted,
because `mas_conformance/` and `mas_core/schema/` are vendored copies and
the tests check them for drift. Evidence references point to
`docs/experiment-review.md`.
Proposed target: a v0.1.1 errata release for SCP-1 to SCP-3 and SCP-5,
which clarify or tighten without changing what canonical addresses mean.
SCP-4 is informative only.
---
## SCP-1: quoted local-parts that are valid dot-strings are equivalent
**Sections:** §4 Canonical ASCII local-part, §8 Address equivalence.
**Evidence:** Postfix delivered `"ticket=PR42"@…` as `ticket=PR42` in all
five probe configurations (RFC 5321 treats unnecessary quoting as
equivalent). A receiver behind an MTA cannot tell which form the sender
used.
**Proposed text (§4, after the second paragraph):**
> A quoted local-part whose content, after removing the quotes and with no
> quoted-pairs, is a valid canonical MAS local-part is equivalent to that
> unquoted form. Receivers MUST treat the two identically. Generators MUST
> emit the unquoted form.
**Impact:** clarification only. No parser change, since quoted input still
does not parse. A new conformance fixture should record the equivalence.
---
## SCP-2: resource-type names are lower-case
**Sections:** §6.1 Resource types, grammar `resource-type`, §8.
**Evidence:** the ABNF accepts `Ticket=PR42`, but the statute schema only
registers lower-case type names. The address parses but can never match a
declared type, even under `ascii-insensitive`.
**Options:**
- **(a) recommended:** restrict `resource-type` in the grammar to lower-case
ALPHA, DIGIT and `-`. `Ticket=PR42` then parses as an opaque target, which
is the safe outcome under §6.1 ("no semantics solely because of `=`").
- (b) keep the grammar and state in §8 that the statute's equivalence policy
also applies to the type name.
(a) keeps the type a protocol token rather than user data, and it matches
the schema. (b) makes type matching depend on the statute, which is
exactly the hidden inference §8 tries to avoid.
**Impact:** (a) is a grammar tightening. None of the 31 shared conformance
fixtures uses an upper-case type, so a new fixture (`Ticket=PR42` →
opaque) records the change. No repo test uses one either.
Live traffic in MAS-WP-0003 used only lower-case types.
---
## SCP-3: type-intake semantics for `TYPE!MODE`
**Section:** §7 Interaction semantics.
**Evidence:** in `ticket!callback`, the target `ticket` is an opaque
target with no defined link to the resource type `ticket`. The canonical
§7 example implies one. In the experiment, the statute had to register
`ticket` as a function (registry key) and the service had to know what
that means. Live, `ticket!callback@mas.coulomb.social` worked from Gmail
and M365 only because of that registry entry.
**Proposed text (§7, new paragraph):**
> An opaque target that equals a resource type declared in the statute,
> and that the statute also registers as a function, is that type's
> *intake*: mail to it requests creation or selection of a resource of
> that type. Without such a registration the target has no intake
> semantics, even if it equals a declared resource type.
Also add a statute-schema field such as `"intake_for": "ticket"` on
registry entries, so the link is declared rather than implied by equal
names.
**Impact:** additive; the schema gains an optional field. The policy
resolver reports `intake_for` when it is present.
---
## SCP-4 (informative): deliverability evidence for `!` and `=`
**Section:** §12 Deliverability and rewriting.
**Evidence:** Gmail (web and app) and Microsoft 365 (Outlook mobile and
desktop) accepted and delivered all four test forms byte-identical. That
includes `!`, a doubled `=` and a `+` facet, both inbound and in a reply to
an `=` From address. Postfix 3.7 never applied the legacy `!`/`%` rewrites
to MAS addresses carrying an `@domain`. Pending: SRS and VERP (MAS-WP-0003
T04).
**Proposal:** keep the normative text. Add an informative note recording
the tested paths, and an implementation note that a default Postfix relay
configuration rejects every local-part containing `!` (see SCP-5 and the
review's operational findings). The "cooperating environments" wording for
`!` stays until T04 has covered forwarding.
---
## SCP-5: intake auto-replies are a reflection vector
**Section:** §14 Security (items 4 and 5).
**Evidence:** an intake address that answers every request (as
`ticket!callback@` does with "your request is now ticket …") sends mail to
whatever `From:` the request carried. A forged `From:` makes the service
send mail to a third party, which is backscatter in all but name. §14.5
only says "avoid backscatter" and is usually read as covering MTA bounces.
The live deployment had to add an application-level cap (T06) and discard
MTA bounces to keep the relay bounded.
**Proposed text (§14, new item):**
> 10. A receiver that automatically replies to mail sent to an intake or
> interaction address SHOULD reply only when the sender is
> authenticated (for example DMARC alignment pass), and MUST bound
> the volume of such replies (rate limits or caps). Replies MUST NOT
> be sent to automated mail (`Auto-Submitted`, `Precedence`).
**Impact:** normative for receivers. The reference ticket service already
skips automated mail and has a send cap. A DMARC check would need the
gateway to pass `Authentication-Results` in the event, which is a small
event-schema addition.
---
## Lower-priority gaps (no proposal yet)
Recorded in the review, not urgent:
- interaction-mode names allowed by the value grammar but not by the schema
(`call.back`, `a=b`);
- registry keys containing `=` or leading/trailing dots, which need semantic
lint;
- discovery: whether the content type is checked, and the exact redirect
same-origin rule;
- statute-declared facets.

View file

@ -199,7 +199,7 @@ counted.
```task
id: MAS-WP-0003-T05
status: todo
status: progress
priority: high
state_hub_task_id: "63e86851-9c7d-5ce8-977f-5d09268d9dcc"
```
@ -208,3 +208,11 @@ Fold the results into `docs/experiment-review.md`. Turn the spec gaps
(quoted equivalence, resource-type case, type-intake semantics for
`ticket!MODE`) into spec change proposals. Then take the MAS-WP-0002
decision gate.
- 2026-10-18: provider results (T02/T03) and operational findings are in
`docs/experiment-review.md`. `docs/spec-change-proposals.md` holds
SCP-1 (quoted equivalence), SCP-2 (lower-case resource types), SCP-3
(type intake), SCP-4 (informative deliverability) and SCP-5 (intake
auto-reply reflection). The review ends with a decision-gate
recommendation; the founder decides. Pending: T04 results, then
revisit SCP-4 and the gate table.