- mas_core: P2-maximal parser with required_profile, generator, statute schema + semantic lint + defaults-only inheritance, exact-host discovery with typed failures and same-origin redirects, pure policy resolver - mas_conformance: vendored spec fixtures (drift-tested) + property and statute cases - mas CLI: parse / explain / discover / lint-statute / test - mas-gateway: generic mail -> MAS event -> HTTP (MTA pipe + webhook) - examples: static great-service.example statute, reference ticket service - docs/experiment-review.md: first-pass findings; live MTA test still open Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 623586@bnt-lap001 Assistant-Session: 10b30928-5b3b-4c11-9934-5ecca07367d8
6 KiB
MAS v0.1 experiment review (first pass, 2026-10-08)
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,
and from running the test suite (135 tests, all offline except the
loopback fixture servers).
Scope caveat: every "email path" here is simulated. Raw RFC 5322
messages go through the real mas-gateway pipe entrypoint, but no
production MTA or mailbox provider was involved. The Email infrastructure
question is therefore still open and needs the live test described at the
end.
Answers to the review questions
Syntax: did the parser stay simple?
Yes. The parser is about 100 lines with no I/O. It always uses the maximal
(P2) grammar and records required_profile, so ticket!callback parses
without knowing anything about the namespace. All 31 spec fixtures and 27
added property cases pass.
Gaps found in the spec:
- Resource-type case. The ABNF allows
Ticket=PR42, but the statute schema only registers lower-case type names. Such an address parses, but it can never match a declared type, even when the statute saysascii-insensitive. The spec should either make resource types lower-case in the grammar, or state that the equivalence policy also applies to the type name. - Interaction-mode names. The value grammar allows
call.backanda=b. The schema's mode-name pattern does not. Those modes parse but can never be enabled. This is harmless, but it should be documented. - Registry keys. The schema pattern already blocks
+ ! $ ^, but it still allows=and leading or trailing dots. Semantic lint is needed to reject a key liketicket=GS-1(registry-key-resource).
Discovery: was exact-host metadata sufficient?
For this experiment, yes. Implementation choices the spec does not settle:
application/json(or+json) is required as the content type (InvalidContentType). The spec only asks for 2xx plus the schema. It should say whether the content type is checked.- Redirects are followed only when scheme, host and port all match. That
is stricter than "same host". A port change counts as
CrossHostRedirectand an https→http change asSchemeDowngrade. mas_versionis checked before the schema runs, so a future version is reported asUnsupportedVersionand not as a schema error.- Operational finding: a service must not depend on discovering its
own statute. If the statute endpoint is down, the ticket service sees
namespace_confirmed = falseand correctly refuses to act. The mail is then declined. Operators should pin their own statute in local configuration (mas-gateway --statute). Discovery is for third-party namespaces.
Policy: could all inference stay explicit?
Yes. Policy is a pure function. Without a statute, kind is always
opaque, = alone gives no resource semantics, and ! stays
"unconfirmed". mas explain lists each of these as an unknown.
Folding is applied only when a statute declares it, and dot folding never
touches resource identifiers.
Facets have no statute declaration at all, so they are always reported as "application-defined". A later version could let statutes list the facets they recognise.
Email infrastructure: which forms were rewritten or rejected?
Not yet tested. Known risks to check:
=overlaps with SRS (SRS0=...) and VERP encodings.!is UUCP bang-path syntax. Some validators and web forms reject it.- Postfix
recipient_delimiter = +strips the facet for lookups. That fits the facet-degradation design.
Resource addressing: did it improve correlation?
Yes. The service replies from ticket=GS-…@great-service.example, so an
ordinary reply lands on the resource address. Correlation uses the address
only. The tests send an unrelated subject and a facet-stripped address,
and both correlate.
Subject lines: could identifiers disappear?
Yes. Replies use the customer's own subject, and no ticket id is ever put in it (asserted in the tests).
Lifecycle: did closed resources stay understandable?
Yes. A closed ticket stays addressable. Mail to it is archived separately
and the ticket is not changed. A single notice goes only to the original
requester, and never to automated senders (Auto-Submitted,
Precedence). Mail to an unknown resource is quarantined with no reply.
For an MTA pipe, the gateway exits 0 when the app declines and 75 when the
app is unreachable, so it never generates a bounce (no backscatter).
Interaction: was !callback useful?
Partly. It expresses intent cleanly, but the service still needs a phone
number and a time. The address cannot carry those, so the first reply has
to ask by email. !callback therefore starts a negotiation; it does not
replace one.
The bare ticket in ticket!callback is an opaque target. It has no
defined link to the resource type ticket. The spec's canonical example
(§7) implies such a link. The statute has to register ticket as an
intake function, and the service has to know what that means. The spec
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?
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. 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.
Recommended next step (before the decision gate)
Run the live path: a real domain, the static statute served at
/.well-known/mas, Postfix with the mas-gateway pipe transport, and test
mail sent from Gmail, Outlook and one corporate gateway to
ticket!callback@, ticket=ID@ and ticket=ID+comments@. Record which
addresses arrive unchanged. That evidence answers the open
Email infrastructure question.