Test MAS addresses against real Postfix; close MAS-WP-0002, add MAS-WP-0003
- experiments/postfix-probe: Postfix 3.7 -> pipe -> mas-gateway -> ticket service in Docker; 14 address forms x 5 configurations. 13/14 arrive byte-identical everywhere; quoted "ticket=PR42" is unquoted in transit. Legacy bang/percent rewrites never fire for MAS addresses. - docs/experiment-review.md: Email infrastructure section with results - MAS-WP-0003: provider-side live test on a real subdomain (needs DNS/MX) 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
This commit is contained in:
parent
8d3878d9c8
commit
e19d557a4c
13 changed files with 1179 additions and 20 deletions
|
|
@ -5,11 +5,12 @@ Status of MAS-WP-0002, Milestone 9. The answers below come from building
|
|||
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.
|
||||
**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**.
|
||||
|
||||
## Answers to the review questions
|
||||
|
||||
|
|
@ -62,11 +63,43 @@ Facets have no statute declaration at all, so they are always reported as
|
|||
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.
|
||||
**Postfix (tested):** 14 address forms were sent through 5 Postfix
|
||||
configurations (`experiments/postfix-probe/results/`):
|
||||
- relay domain with defaults;
|
||||
- local rewrite context, with and without `swap_bangpath` /
|
||||
`allow_percent_hack`;
|
||||
- domain in `mydestination` (local delivery class), with and without those
|
||||
two settings.
|
||||
|
||||
The results:
|
||||
- **13 of 14 forms arrived byte-identical in every configuration.** That
|
||||
includes `ticket!callback`, `ticket=ID+comments`, `ticket==X1`,
|
||||
`ticket=PR%2B42`, `ticket=ID+comments!callback` and `ticket=PR42$4.99`.
|
||||
`recipient_delimiter = +` does not change the envelope recipient that
|
||||
`pipe` passes on as `${recipient}`. Upper case is preserved.
|
||||
- **The quoted local-part `"ticket=PR42"` arrived unquoted** as
|
||||
`ticket=PR42`. Postfix drops quotes that are not needed (RFC 5321
|
||||
treats the two forms as equivalent), so a form the spec calls
|
||||
non-canonical became canonical in transit. **Spec gap:** §4 should say
|
||||
that a quoted local-part whose content is a valid dot-string is
|
||||
equivalent to the unquoted form, because an MAS receiver after an MTA
|
||||
cannot tell them apart.
|
||||
- **The `!` and `%` legacy rewrites did not trigger** for any MAS address,
|
||||
even with Postfix defaults and the domain in `mydestination`. In 3.7 the
|
||||
bang-path swap fired only for a bare `site!user` with no `@domain`
|
||||
(checked via local `sendmail`: it was rerouted to `site`). MAS addresses
|
||||
always carry an `@domain`. Setting `swap_bangpath = no` and
|
||||
`allow_percent_hack = no` is still a sensible hardening default for MAS
|
||||
hosts.
|
||||
- `$` passed through SMTP untouched. Rejecting it is purely a MAS parser
|
||||
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.
|
||||
|
||||
### Resource addressing: did it improve correlation?
|
||||
Yes. The service replies *from* `ticket=GS-…@great-service.example`, so an
|
||||
|
|
@ -107,10 +140,10 @@ 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.
|
||||
## 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.
|
||||
|
|
|
|||
8
experiments/postfix-probe/Dockerfile
Normal file
8
experiments/postfix-probe/Dockerfile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Real Postfix -> pipe transport -> mas-gateway -> reference ticket service.
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
postfix python3 python3-jsonschema ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
RUN useradd -r -s /usr/sbin/nologin mas
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
EXPOSE 25 8024 8025
|
||||
ENTRYPOINT ["/bin/sh", "/entrypoint.sh"]
|
||||
29
experiments/postfix-probe/README.md
Normal file
29
experiments/postfix-probe/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Postfix probe
|
||||
|
||||
A real Postfix delivers MAS addresses through the `pipe` transport to
|
||||
`mas-gateway` and on to the reference ticket service. A capture proxy
|
||||
(`capture.py`) records the envelope recipient each event carried, so the
|
||||
probe can tell whether Postfix rewrote anything.
|
||||
|
||||
```bash
|
||||
docker build -t mas-postfix-probe experiments/postfix-probe
|
||||
python3 experiments/postfix-probe/probe.py --profile defaults # relay domain
|
||||
python3 experiments/postfix-probe/probe.py --profile local-domain # domain in mydestination
|
||||
python3 experiments/postfix-probe/probe.py --profile local-domain-safe # + swap_bangpath/percent_hack off
|
||||
```
|
||||
|
||||
The other profiles are `defaults-local` and `mas-safe-local`. The repo is
|
||||
mounted read-only. In `results/` a `=` mark means the address arrived
|
||||
unchanged and `!` means it was rewritten. Findings are in
|
||||
`docs/experiment-review.md` under *Email infrastructure*.
|
||||
|
||||
The Postfix side is the `master.cf` entry in `entrypoint.sh`:
|
||||
|
||||
```
|
||||
mas unix - n n - - pipe
|
||||
flags=Rq user=mas argv=/usr/local/bin/mas-gateway --statute <statute>
|
||||
pipe --app-url http://127.0.0.1:8024/mas-events --recipient ${recipient}
|
||||
```
|
||||
|
||||
Set `mas_destination_recipient_limit = 1` so each pipe invocation handles
|
||||
one recipient.
|
||||
40
experiments/postfix-probe/capture.py
Normal file
40
experiments/postfix-probe/capture.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
"""Probe-only proxy: records each MAS event as delivered by Postfix, then
|
||||
forwards it to the ticket service and records the app's answer."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
|
||||
UPSTREAM = sys.argv[1]
|
||||
EVENTS = []
|
||||
|
||||
|
||||
class H(BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
body = self.rfile.read(int(self.headers.get("Content-Length", "0")))
|
||||
req = urllib.request.Request(UPSTREAM, body, {"Content-Type": "application/json"})
|
||||
try:
|
||||
with urllib.request.urlopen(req) as r:
|
||||
status, resp = r.status, r.read()
|
||||
except urllib.error.HTTPError as e:
|
||||
status, resp = e.code, e.read()
|
||||
EVENTS.append({"event": json.loads(body), "app_status": status, "app": json.loads(resp or b"null")})
|
||||
self._send(status, resp)
|
||||
|
||||
def do_GET(self):
|
||||
self._send(200, json.dumps(EVENTS).encode())
|
||||
|
||||
def _send(self, status, body):
|
||||
self.send_response(status)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def log_message(self, *a):
|
||||
pass
|
||||
|
||||
|
||||
ThreadingHTTPServer(("0.0.0.0", 8024), H).serve_forever()
|
||||
42
experiments/postfix-probe/entrypoint.sh
Normal file
42
experiments/postfix-probe/entrypoint.sh
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
# Repo is mounted read-only at /mas. PROFILE=defaults|mas-safe selects Postfix settings.
|
||||
set -e
|
||||
cat > /usr/local/bin/mas-gateway <<'SH'
|
||||
#!/bin/sh
|
||||
exec env PYTHONPATH=/mas python3 -m mas_gateway.main "$@"
|
||||
SH
|
||||
chmod +x /usr/local/bin/mas-gateway
|
||||
|
||||
postconf -e "maillog_file=/dev/stdout" "myhostname=mx.probe.test" "mydestination=" \
|
||||
"relay_domains=great-service.example" "transport_maps=inline:{great-service.example=mas:}" \
|
||||
"mynetworks=0.0.0.0/0" "smtpd_relay_restrictions=permit_mynetworks,reject" \
|
||||
"mas_destination_recipient_limit=1" "recipient_delimiter=+" "compatibility_level=3.6"
|
||||
# PROFILE: defaults - Postfix defaults; probe client is "remote" (no address rewriting)
|
||||
# defaults-local - defaults, but every client gets the local rewrite context
|
||||
# mas-safe-local - local rewrite context with swap_bangpath/allow_percent_hack off
|
||||
# local-domain - domain served via mydestination (local delivery class), defaults
|
||||
# local-domain-safe - local-domain with swap_bangpath/allow_percent_hack off
|
||||
case "$PROFILE" in
|
||||
local-domain*) postconf -e "relay_domains=" "transport_maps=" "mydestination=great-service.example" \
|
||||
"local_transport=mas" "local_recipient_maps=" ;;
|
||||
esac
|
||||
case "$PROFILE" in
|
||||
*-local) postconf -e "local_header_rewrite_clients=static:all" ;;
|
||||
esac
|
||||
case "$PROFILE" in
|
||||
mas-safe*|*-safe) postconf -e "swap_bangpath=no" "allow_percent_hack=no" ;;
|
||||
esac
|
||||
cat >> /etc/postfix/master.cf <<'CF'
|
||||
mas unix - n n - - pipe
|
||||
flags=Rq user=mas argv=/usr/local/bin/mas-gateway
|
||||
--statute /mas/examples/statute/great-service.example/.well-known/mas
|
||||
pipe --app-url http://127.0.0.1:8024/mas-events --recipient ${recipient}
|
||||
CF
|
||||
# Postfix runs `mas-gateway` as user mas; the service needs a writable DB.
|
||||
mkdir -p /var/lib/tickets && chown mas /var/lib/tickets
|
||||
su -s /bin/sh mas -c "PYTHONPATH=/mas python3 /mas/examples/ticket_service/ticket_service.py \
|
||||
--db /var/lib/tickets/t.sqlite3 --listen 0.0.0.0:8025" &
|
||||
python3 /mas/experiments/postfix-probe/capture.py http://127.0.0.1:8025/mas-events &
|
||||
echo "PROFILE=${PROFILE:-defaults}"
|
||||
postconf swap_bangpath allow_percent_hack recipient_delimiter local_header_rewrite_clients
|
||||
exec postfix start-fg
|
||||
118
experiments/postfix-probe/probe.py
Normal file
118
experiments/postfix-probe/probe.py
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Send MAS test addresses through a real Postfix and report what arrived.
|
||||
|
||||
python experiments/postfix-probe/probe.py --profile defaults
|
||||
python experiments/postfix-probe/probe.py --profile defaults-local
|
||||
python experiments/postfix-probe/probe.py --profile mas-safe-local
|
||||
|
||||
Starts the mas-postfix-probe container, sends one message per address via
|
||||
SMTP, then compares the envelope recipient Postfix handed to mas-gateway
|
||||
with the one that was sent.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import smtplib
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
D = "great-service.example"
|
||||
# (label, local-part as sent in RCPT TO). "{ID}" is filled with the created ticket id.
|
||||
CASES = [
|
||||
("P2 intake", "ticket!callback"),
|
||||
("resource", "ticket={ID}"),
|
||||
("resource + facet", "ticket={ID}+comments"),
|
||||
("extra '=' in id", "ticket==X1"),
|
||||
("registry function", "support"),
|
||||
("person-style", "bernd.worsch"),
|
||||
("upper-case local-part", "Ticket={ID}"),
|
||||
("percent data", "ticket=PR%2B42"),
|
||||
("percent hack form", "ticket%other.example"),
|
||||
("bang in resource", "ticket={ID}!web"),
|
||||
("facet + interaction", "ticket={ID}+comments!callback"),
|
||||
("reserved $", "ticket=PR42$4.99"),
|
||||
("quoted local-part", '"ticket=PR42"'),
|
||||
("unknown resource", "ticket=DOES-NOT-EXIST"),
|
||||
]
|
||||
|
||||
|
||||
def run(profile, port):
|
||||
name = f"mas-probe-{profile}"
|
||||
subprocess.run(["docker", "rm", "-f", name], capture_output=True)
|
||||
subprocess.run(["docker", "run", "-d", "--name", name, "-e", f"PROFILE={profile}",
|
||||
"-v", f"{ROOT}:/mas:ro", "-p", f"127.0.0.1:{port}:25",
|
||||
"-p", f"127.0.0.1:{port + 1}:8024", "mas-postfix-probe"], check=True,
|
||||
capture_output=True)
|
||||
try:
|
||||
for _ in range(60):
|
||||
try:
|
||||
smtplib.SMTP("127.0.0.1", port, timeout=2).quit()
|
||||
break
|
||||
except OSError:
|
||||
time.sleep(0.5)
|
||||
results, tid = [], None
|
||||
for label, lp in CASES:
|
||||
rcpt_lp = lp.replace("{ID}", tid or "MISSING")
|
||||
rcpt = f"{rcpt_lp}@{D}"
|
||||
msg = (f"From: probe@example.net\r\nTo: {rcpt}\r\nSubject: {label}\r\n"
|
||||
f"Message-ID: <{abs(hash(rcpt))}@example.net>\r\n\r\nprobe\r\n")
|
||||
before = len(events(port))
|
||||
try:
|
||||
with smtplib.SMTP("127.0.0.1", port, timeout=10) as s:
|
||||
refused = s.sendmail("probe@example.net", [rcpt], msg)
|
||||
smtp = "accepted" if not refused else f"refused {refused}"
|
||||
except smtplib.SMTPException as e:
|
||||
smtp = f"rejected: {e}"
|
||||
got = wait_event(port, before) if smtp == "accepted" else None
|
||||
arrived = got["event"]["recipient"] if got else None
|
||||
mas = got["event"]["mas"] if got else {}
|
||||
app = got["app"] if got else None
|
||||
if label == "P2 intake" and app:
|
||||
tid = app.get("ticket_id")
|
||||
results.append({"label": label, "sent": rcpt, "smtp": smtp, "arrived_as": arrived,
|
||||
"unchanged": arrived == rcpt,
|
||||
"mas_valid": mas.get("valid"), "kind": mas.get("kind"),
|
||||
"app_status": got["app_status"] if got else None,
|
||||
"app": (app or {}).get("status") if isinstance(app, dict) else app})
|
||||
logs = subprocess.run(["docker", "logs", name], capture_output=True, text=True)
|
||||
return results, logs.stdout + logs.stderr
|
||||
finally:
|
||||
subprocess.run(["docker", "rm", "-f", name], capture_output=True)
|
||||
|
||||
|
||||
def events(port):
|
||||
with urllib.request.urlopen(f"http://127.0.0.1:{port + 1}/") as r:
|
||||
return json.loads(r.read())
|
||||
|
||||
|
||||
def wait_event(port, before, timeout=15):
|
||||
end = time.time() + timeout
|
||||
while time.time() < end:
|
||||
ev = events(port)
|
||||
if len(ev) > before:
|
||||
return ev[-1]
|
||||
time.sleep(0.3)
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--profile", choices=["defaults", "defaults-local", "mas-safe-local", "local-domain", "local-domain-safe"], default="defaults")
|
||||
ap.add_argument("--port", type=int, default=2525)
|
||||
ap.add_argument("--json", metavar="FILE")
|
||||
ap.add_argument("--log", metavar="FILE")
|
||||
a = ap.parse_args()
|
||||
results, log = run(a.profile, a.port)
|
||||
for r in results:
|
||||
mark = "=" if r["unchanged"] else "!"
|
||||
print(f"{mark} {r['label']:<22} {r['sent']:<52} -> {r['arrived_as'] or r['smtp']}"
|
||||
f" [{r['kind']}/{r['app']}]")
|
||||
if a.json:
|
||||
Path(a.json).write_text(json.dumps({"profile": a.profile, "results": results}, indent=2) + "\n")
|
||||
if a.log:
|
||||
Path(a.log).write_text(log)
|
||||
sys.exit(0)
|
||||
159
experiments/postfix-probe/results/defaults-local.json
Normal file
159
experiments/postfix-probe/results/defaults-local.json
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"profile": "defaults-local",
|
||||
"results": [
|
||||
{
|
||||
"label": "P2 intake",
|
||||
"sent": "ticket!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 201,
|
||||
"app": "created"
|
||||
},
|
||||
{
|
||||
"label": "resource",
|
||||
"sent": "ticket=GS-9G9WEYSMDH@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-9G9WEYSMDH@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "resource + facet",
|
||||
"sent": "ticket=GS-9G9WEYSMDH+comments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-9G9WEYSMDH+comments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "extra '=' in id",
|
||||
"sent": "ticket==X1@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket==X1@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "registry function",
|
||||
"sent": "support@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "support@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "person-style",
|
||||
"sent": "bernd.worsch@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "bernd.worsch@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "upper-case local-part",
|
||||
"sent": "Ticket=GS-9G9WEYSMDH@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "Ticket=GS-9G9WEYSMDH@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "percent data",
|
||||
"sent": "ticket=PR%2B42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR%2B42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "percent hack form",
|
||||
"sent": "ticket%other.example@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket%other.example@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "bang in resource",
|
||||
"sent": "ticket=GS-9G9WEYSMDH!web@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-9G9WEYSMDH!web@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "facet + interaction",
|
||||
"sent": "ticket=GS-9G9WEYSMDH+comments!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-9G9WEYSMDH+comments!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "reserved $",
|
||||
"sent": "ticket=PR42$4.99@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42$4.99@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "quoted local-part",
|
||||
"sent": "\"ticket=PR42\"@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "unknown resource",
|
||||
"sent": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
}
|
||||
]
|
||||
}
|
||||
159
experiments/postfix-probe/results/defaults.json
Normal file
159
experiments/postfix-probe/results/defaults.json
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"profile": "defaults",
|
||||
"results": [
|
||||
{
|
||||
"label": "P2 intake",
|
||||
"sent": "ticket!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 201,
|
||||
"app": "created"
|
||||
},
|
||||
{
|
||||
"label": "resource",
|
||||
"sent": "ticket=GS-2H6T5NBH6K@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-2H6T5NBH6K@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "resource + facet",
|
||||
"sent": "ticket=GS-2H6T5NBH6K+comments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-2H6T5NBH6K+comments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "extra '=' in id",
|
||||
"sent": "ticket==X1@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket==X1@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "registry function",
|
||||
"sent": "support@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "support@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "person-style",
|
||||
"sent": "bernd.worsch@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "bernd.worsch@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "upper-case local-part",
|
||||
"sent": "Ticket=GS-2H6T5NBH6K@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "Ticket=GS-2H6T5NBH6K@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "percent data",
|
||||
"sent": "ticket=PR%2B42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR%2B42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "percent hack form",
|
||||
"sent": "ticket%other.example@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket%other.example@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "bang in resource",
|
||||
"sent": "ticket=GS-2H6T5NBH6K!web@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-2H6T5NBH6K!web@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "facet + interaction",
|
||||
"sent": "ticket=GS-2H6T5NBH6K+comments!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-2H6T5NBH6K+comments!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "reserved $",
|
||||
"sent": "ticket=PR42$4.99@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42$4.99@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "quoted local-part",
|
||||
"sent": "\"ticket=PR42\"@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "unknown resource",
|
||||
"sent": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
}
|
||||
]
|
||||
}
|
||||
159
experiments/postfix-probe/results/local-domain-safe.json
Normal file
159
experiments/postfix-probe/results/local-domain-safe.json
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"profile": "local-domain-safe",
|
||||
"results": [
|
||||
{
|
||||
"label": "P2 intake",
|
||||
"sent": "ticket!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 201,
|
||||
"app": "created"
|
||||
},
|
||||
{
|
||||
"label": "resource",
|
||||
"sent": "ticket=GS-VZZRSGXV12@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-VZZRSGXV12@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "resource + facet",
|
||||
"sent": "ticket=GS-VZZRSGXV12+comments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-VZZRSGXV12+comments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "extra '=' in id",
|
||||
"sent": "ticket==X1@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket==X1@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "registry function",
|
||||
"sent": "support@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "support@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "person-style",
|
||||
"sent": "bernd.worsch@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "bernd.worsch@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "upper-case local-part",
|
||||
"sent": "Ticket=GS-VZZRSGXV12@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "Ticket=GS-VZZRSGXV12@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "percent data",
|
||||
"sent": "ticket=PR%2B42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR%2B42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "percent hack form",
|
||||
"sent": "ticket%other.example@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket%other.example@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "bang in resource",
|
||||
"sent": "ticket=GS-VZZRSGXV12!web@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-VZZRSGXV12!web@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "facet + interaction",
|
||||
"sent": "ticket=GS-VZZRSGXV12+comments!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-VZZRSGXV12+comments!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "reserved $",
|
||||
"sent": "ticket=PR42$4.99@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42$4.99@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "quoted local-part",
|
||||
"sent": "\"ticket=PR42\"@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "unknown resource",
|
||||
"sent": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
}
|
||||
]
|
||||
}
|
||||
159
experiments/postfix-probe/results/local-domain.json
Normal file
159
experiments/postfix-probe/results/local-domain.json
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"profile": "local-domain",
|
||||
"results": [
|
||||
{
|
||||
"label": "P2 intake",
|
||||
"sent": "ticket!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 201,
|
||||
"app": "created"
|
||||
},
|
||||
{
|
||||
"label": "resource",
|
||||
"sent": "ticket=GS-D4QGSR85P2@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-D4QGSR85P2@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "resource + facet",
|
||||
"sent": "ticket=GS-D4QGSR85P2+comments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-D4QGSR85P2+comments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "extra '=' in id",
|
||||
"sent": "ticket==X1@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket==X1@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "registry function",
|
||||
"sent": "support@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "support@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "person-style",
|
||||
"sent": "bernd.worsch@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "bernd.worsch@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "upper-case local-part",
|
||||
"sent": "Ticket=GS-D4QGSR85P2@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "Ticket=GS-D4QGSR85P2@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "percent data",
|
||||
"sent": "ticket=PR%2B42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR%2B42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "percent hack form",
|
||||
"sent": "ticket%other.example@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket%other.example@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "bang in resource",
|
||||
"sent": "ticket=GS-D4QGSR85P2!web@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-D4QGSR85P2!web@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "facet + interaction",
|
||||
"sent": "ticket=GS-D4QGSR85P2+comments!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-D4QGSR85P2+comments!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "reserved $",
|
||||
"sent": "ticket=PR42$4.99@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42$4.99@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "quoted local-part",
|
||||
"sent": "\"ticket=PR42\"@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "unknown resource",
|
||||
"sent": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
}
|
||||
]
|
||||
}
|
||||
159
experiments/postfix-probe/results/mas-safe-local.json
Normal file
159
experiments/postfix-probe/results/mas-safe-local.json
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"profile": "mas-safe-local",
|
||||
"results": [
|
||||
{
|
||||
"label": "P2 intake",
|
||||
"sent": "ticket!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 201,
|
||||
"app": "created"
|
||||
},
|
||||
{
|
||||
"label": "resource",
|
||||
"sent": "ticket=GS-V5KFZ1PGY5@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-V5KFZ1PGY5@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "resource + facet",
|
||||
"sent": "ticket=GS-V5KFZ1PGY5+comments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-V5KFZ1PGY5+comments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "extra '=' in id",
|
||||
"sent": "ticket==X1@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket==X1@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "registry function",
|
||||
"sent": "support@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "support@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "person-style",
|
||||
"sent": "bernd.worsch@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "bernd.worsch@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "upper-case local-part",
|
||||
"sent": "Ticket=GS-V5KFZ1PGY5@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "Ticket=GS-V5KFZ1PGY5@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "percent data",
|
||||
"sent": "ticket=PR%2B42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR%2B42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "percent hack form",
|
||||
"sent": "ticket%other.example@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket%other.example@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "bang in resource",
|
||||
"sent": "ticket=GS-V5KFZ1PGY5!web@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-V5KFZ1PGY5!web@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "facet + interaction",
|
||||
"sent": "ticket=GS-V5KFZ1PGY5+comments!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=GS-V5KFZ1PGY5+comments!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "reserved $",
|
||||
"sent": "ticket=PR42$4.99@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42$4.99@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "quoted local-part",
|
||||
"sent": "\"ticket=PR42\"@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "unknown resource",
|
||||
"sent": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "MAS v0.1 reference experiment"
|
||||
domain: communication
|
||||
repo: mail-address-semantics
|
||||
status: active
|
||||
status: done
|
||||
owner: claude
|
||||
topic_slug: coulomb-social
|
||||
created: "2026-10-08"
|
||||
|
|
@ -812,11 +812,11 @@ The service must handle this deliberately and without generating abusive backsca
|
|||
|
||||
## Milestone 9 — Experiment review
|
||||
|
||||
> First-pass findings: `docs/experiment-review.md`. Open item: live MTA/provider test (Email infrastructure question).
|
||||
> Findings: `docs/experiment-review.md`. MTA half tested against real Postfix (`experiments/postfix-probe/`); provider half continues in MAS-WP-0003.
|
||||
|
||||
```task
|
||||
id: MAS-WP-0002-T10
|
||||
status: in_progress
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "31bbfb56-5541-5ace-aff7-94ed6f1df57a"
|
||||
```
|
||||
|
|
|
|||
94
workplans/MAS-WP-0003-provider-live-test.md
Normal file
94
workplans/MAS-WP-0003-provider-live-test.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
---
|
||||
id: MAS-WP-0003
|
||||
type: workplan
|
||||
title: "MAS live provider test on a real subdomain"
|
||||
domain: communication
|
||||
repo: mail-address-semantics
|
||||
status: ready
|
||||
owner: claude
|
||||
topic_slug: coulomb-social
|
||||
created: "2026-10-08"
|
||||
updated: "2026-10-08"
|
||||
---
|
||||
|
||||
# MAS live provider test on a real subdomain
|
||||
|
||||
Follow-up to MAS-WP-0002. The MTA half of the review question *which
|
||||
characters or address forms were rewritten or rejected?* was answered
|
||||
against a real Postfix (`experiments/postfix-probe/`,
|
||||
`docs/experiment-review.md`). This workplan answers the provider half. It
|
||||
has to be done before the MAS-WP-0002 decision gate.
|
||||
|
||||
coulomb.social mail is hosted by IONOS, so the test needs its own
|
||||
subdomain (for example `mas.coulomb.social`) with its own MX.
|
||||
|
||||
## Choose and provision the test subdomain
|
||||
|
||||
```task
|
||||
id: MAS-WP-0003-T01
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Needs the operator. Pick the subdomain. Add an MX (and A) record at IONOS
|
||||
pointing to railiance01 (92.205.62.239). Allow inbound TCP 25 on
|
||||
railiance01. Record the choice as a decision.
|
||||
|
||||
## Deploy Postfix + gateway + ticket service + statute
|
||||
|
||||
```task
|
||||
id: MAS-WP-0003-T02
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Deploy the probe configuration on railiance01, using `local-domain-safe`
|
||||
settings, TLS and the usual relay restrictions. Serve the statute (with
|
||||
the namespace set to the subdomain) at `https://<sub>/.well-known/mas`
|
||||
through Traefik. Verify the deployment with `mas discover <sub>`.
|
||||
Outbound replies need a relay with SPF/DKIM for the subdomain; until that
|
||||
exists, keep replies in the outbox only.
|
||||
|
||||
## Provider send matrix
|
||||
|
||||
```task
|
||||
id: MAS-WP-0003-T03
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Needs the operator to send from their accounts. Send from Gmail (web and
|
||||
mobile), Outlook.com and one corporate gateway to:
|
||||
- `ticket!callback@`
|
||||
- `ticket=ID@`
|
||||
- `ticket=ID+comments@`
|
||||
- `ticket=RE=2026-4711@`
|
||||
|
||||
Then reply to a service message sent from `ticket=ID@`. For each
|
||||
combination, record whether the provider's compose form accepts the
|
||||
address and what envelope recipient arrives.
|
||||
|
||||
## Forwarding and list paths
|
||||
|
||||
```task
|
||||
id: MAS-WP-0003-T04
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Send via one forwarding service (SRS) and one mailing list (VERP). Check
|
||||
whether `=` collides with SRS0/VERP encoding, and whether facets survive
|
||||
or are stripped.
|
||||
|
||||
## Close the review
|
||||
|
||||
```task
|
||||
id: MAS-WP-0003-T05
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue