mailshield

MoonBit-native DMARC and SPF policy parser with mail-domain risk scoring

email
dmarc
spf
dns
security
policy
moon add JM-ai-nb/mailshield@0.1.0
Download zip
Author
Version
0.1.0
License
MIT
Last updated
20 hours ago
Downloads
2
README

#MailShield

MailShield is a MoonBit-native mail-domain security policy parser and risk scoring library. It does not send mail or query DNS. It accepts DNS TXT record text or policy-file text that callers already have, then returns structured records, findings, scores, action items, Markdown reports and JSON reports.

#Problem

Mail domains are often abused because policy records are present but weak: SPF may use +all, DMARC may remain at p=none, rollout percentage may be below 100, or SPF DNS lookup usage may be close to the receiver-side limit. MailShield turns these risks into deterministic MoonBit data that can be tested in CI, tenant onboarding tools, security dashboards and classroom examples.

#Scope

  • Parse SPF v=spf1 records, qualifiers, mechanisms, redirect, exp and DNS lookup count.
  • Parse DMARC v=DMARC1, p, sp, pct, rua, ruf, aspf and adkim.
  • Parse DKIM selector TXT records and flag key type, testing mode, service scope and hash policy risks.
  • Parse MTA-STS policy text, TLS-RPT TXT records and BIMI TXT records.
  • Produce single-domain reports, full mail-security suite reports, batch reports and profile assessments.
  • Export Markdown, compact JSON, severity summaries and prioritized action items.

Out of scope: DNS resolution, SMTP sending, DKIM cryptographic verification, spam filtering and receiver-specific policy decisions.

#Installation

moon add JM-ai-nb/mailshield

Mooncakes package: JM-ai-nb/mailshield

#Minimal Usage

test {
let report = @mailshield.analyze_suite(
"example.com",
"v=spf1 ip4:203.0.113.0/24 include:_spf.example.net -all",
"v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:dmarc@example.com",
"v=DKIM1; k=ed25519; p=AbCdEfGhIjKlMnOpQrStUvWxYz1234567890; s=email; h=sha256",
"version: STSv1\nmode: enforce\nmx: mail.example.com\nmax_age: 604800",
"v=TLSRPTv1; rua=mailto:tls@example.com",
"",
)
inspect(report.coverage_label(), content="broad")
}

Run the example:

moon run examples/basic

Run the CLI smoke entry:

moon run cmd/main

#API

  • parse_spf(text): parse one SPF TXT value.
  • parse_dmarc(text): parse one DMARC TXT value.
  • parse_dkim(text): parse one DKIM selector TXT value.
  • parse_mtasts(text): parse one MTA-STS policy file body.
  • parse_tlsrpt(text): parse one TLS-RPT TXT value.
  • parse_bimi(text): parse one BIMI TXT value.
  • analyze_domain(domain, spf, dmarc): combine SPF and DMARC checks.
  • analyze_suite(domain, spf, dmarc, dkim, mtasts, tlsrpt, bimi): combine six mail-domain controls.
  • analyze_suite_rows(rows): batch analyze pipe-separated domain rows.
  • assess_profile(report, profile): assess startup, monitoring, production or brand readiness.
  • report_markdown(domain, spf, dmarc): export a human-readable report.
  • report_json(domain, spf, dmarc): export compact JSON.
  • suite_report_markdown(...) and suite_report_json(...): export full suite reports.
  • quick_score(spf, dmarc) and quick_verdict(spf, dmarc): simple summary helpers.

#Verification

moon check --deny-warn moon build moon test --deny-warn moon fmt --check moon info moon run examples/basic moon run cmd/main

Current effective MoonBit code size is above 3,000 non-empty non-comment lines.

#License And Third-Party Notes

MailShield uses the MIT License. It is an original MoonBit implementation, does not port third-party source code, does not include external media assets, and depends only on the MoonBit standard core library at runtime.

#Maintenance Materials

  • SUBMISSION.md: hackathon proposal.
  • docs/API.md: API notes.
  • docs/design.md: design notes.
  • docs/research.md: Mooncakes search and differentiation notes.
  • docs/issues.md: issue-style development notes.
  • docs/test-record.md: verification record.
  • docs/release-checklist.md: release checklist.
  • CHANGELOG.md: release history.

#
ActionItem

pub(all) struct ActionItem {
id : String
module_name : String
title : String
priority : Int
detail : String
next_step : String
} derive(Eq,
Debug
)

#
ActionItem::to_markdown

fn ActionItem::to_markdown(self : ActionItem) -> String

#
BatchSecurityReport

pub(all) struct BatchSecurityReport {
reports : Array[MailSecuritySuiteReport]
domains_checked : Int
average_score : Int
lowest_score : Int
highest_score : Int
summary : ReportSummary
actions : Array[ActionItem]
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
BatchSecurityReport::has_failures

fn BatchSecurityReport::has_failures(self : BatchSecurityReport) -> Bool

#
BatchSecurityReport::monitor_count

fn BatchSecurityReport::monitor_count(self : BatchSecurityReport) -> Int

#
BatchSecurityReport::protected_count

fn BatchSecurityReport::protected_count(self : BatchSecurityReport) -> Int

#
BatchSecurityReport::risky_count

fn BatchSecurityReport::risky_count(self : BatchSecurityReport) -> Int

#
BatchSecurityReport::to_json

fn BatchSecurityReport::to_json(self : BatchSecurityReport) -> String

#
BatchSecurityReport::to_markdown

fn BatchSecurityReport::to_markdown(self : BatchSecurityReport) -> String

#
BimiRecord

pub(all) struct BimiRecord {
raw : String
valid_version : Bool
version : String
logo_url : String
authority_url : String
logo_svg : Bool
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
BimiRecord::has_authority

fn BimiRecord::has_authority(self : BimiRecord) -> Bool

fn BimiRecord::has_https_logo(self : BimiRecord) -> Bool

#
BimiRecord::is_configured

fn BimiRecord::is_configured(self : BimiRecord) -> Bool

#
DkimRecord

pub(all) struct DkimRecord {
raw : String
valid_version : Bool
key_type : String
key_length_hint : Int
public_key_length : Int
testing_mode : Bool
service_count : Int
hash_count : Int
notes : String
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
DkimRecord::is_configured

fn DkimRecord::is_configured(self : DkimRecord) -> Bool

#
DkimRecord::is_testing

fn DkimRecord::is_testing(self : DkimRecord) -> Bool

#
DkimRecord::key_strength_label

fn DkimRecord::key_strength_label(self : DkimRecord) -> String

#
DkimRecord::uses_ed25519

fn DkimRecord::uses_ed25519(self : DkimRecord) -> Bool

#
DkimRecord::uses_rsa

fn DkimRecord::uses_rsa(self : DkimRecord) -> Bool

#
DmarcRecord

pub(all) struct DmarcRecord {
raw : String
valid_version : Bool
policy : String
subdomain_policy : String
pct : Int
rua_count : Int
ruf_count : Int
alignment_spf : String
alignment_dkim : String
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
MailPolicyReport

pub(all) struct MailPolicyReport {
domain : String
spf : SpfRecord
dmarc : DmarcRecord
score : Int
verdict : String
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
MailPolicyReport::has_failures

fn MailPolicyReport::has_failures(self : MailPolicyReport) -> Bool

#
MailPolicyReport::to_json

fn MailPolicyReport::to_json(self : MailPolicyReport) -> String

#
MailPolicyReport::to_markdown

fn MailPolicyReport::to_markdown(self : MailPolicyReport) -> String

#
MailPolicyReport::warning_count

fn MailPolicyReport::warning_count(self : MailPolicyReport) -> Int

#
MailSecurityProfile

pub(all) struct MailSecurityProfile {
name : String
minimum_score : Int
require_spf_hardfail : Bool
require_dmarc_enforcement : Bool
require_dkim : Bool
require_mtasts : Bool
require_tlsrpt : Bool
require_bimi : Bool
allow_testing_modes : Bool
} derive(Eq,
Debug
)

#
MailSecuritySuiteInput

pub(all) struct MailSecuritySuiteInput {
domain : String
spf_text : String
dmarc_text : String
dkim_text : String
mtasts_text : String
tlsrpt_text : String
bimi_text : String
} derive(Eq,
Debug
)

#
MailSecuritySuiteReport

pub(all) struct MailSecuritySuiteReport {
domain : String
spf : SpfRecord
dmarc : DmarcRecord
dkim : DkimRecord
mtasts : MtastsRecord
tlsrpt : TlsRptRecord
bimi : BimiRecord
score : Int
verdict : String
summary : ReportSummary
actions : Array[ActionItem]
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
MailSecuritySuiteReport::action_count

fn MailSecuritySuiteReport::action_count(self : MailSecuritySuiteReport) -> Int

#
MailSecuritySuiteReport::coverage_label

fn MailSecuritySuiteReport::coverage_label(self : MailSecuritySuiteReport) -> String

#
MailSecuritySuiteReport::has_failures

fn MailSecuritySuiteReport::has_failures(self : MailSecuritySuiteReport) -> Bool

#
MailSecuritySuiteReport::has_warnings

fn MailSecuritySuiteReport::has_warnings(self : MailSecuritySuiteReport) -> Bool

#
MailSecuritySuiteReport::to_json

fn MailSecuritySuiteReport::to_json(self : MailSecuritySuiteReport) -> String

#
MailSecuritySuiteReport::to_markdown

fn MailSecuritySuiteReport::to_markdown(self : MailSecuritySuiteReport) -> String

#
MtastsRecord

pub(all) struct MtastsRecord {
raw : String
valid_version : Bool
version : String
mode : String
max_age : Int
id : String
mx_count : Int
mx_hosts : Array[String]
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
MtastsRecord::cache_window_label

fn MtastsRecord::cache_window_label(self : MtastsRecord) -> String

#
MtastsRecord::has_mx_hosts

fn MtastsRecord::has_mx_hosts(self : MtastsRecord) -> Bool

#
MtastsRecord::is_configured

fn MtastsRecord::is_configured(self : MtastsRecord) -> Bool

#
MtastsRecord::is_enforcing

fn MtastsRecord::is_enforcing(self : MtastsRecord) -> Bool

#
MtastsRecord::is_testing

fn MtastsRecord::is_testing(self : MtastsRecord) -> Bool

#
PolicyFinding

pub(all) struct PolicyFinding {
id : String
severity : Severity
title : String
detail : String
evidence : String
repair : String
penalty : Int
} derive(Eq,
Debug
)

#
ProfileAssessment

pub(all) struct ProfileAssessment {
profile_name : String
passed : Bool
score : Int
missing_count : Int
failed_requirements : Array[String]
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
ProfileAssessment::to_json

fn ProfileAssessment::to_json(self : ProfileAssessment) -> String

#
ProfileAssessment::to_markdown

fn ProfileAssessment::to_markdown(self : ProfileAssessment) -> String

#
ReportSummary

pub(all) struct ReportSummary {
pass_count : Int
warn_count : Int
fail_count : Int
total_count : Int
} derive(Eq,
Debug
)

#
ReportSummary::has_failures

fn ReportSummary::has_failures(self : ReportSummary) -> Bool

#
ReportSummary::has_warnings

fn ReportSummary::has_warnings(self : ReportSummary) -> Bool

#
ReportSummary::is_clean

fn ReportSummary::is_clean(self : ReportSummary) -> Bool

#
ReportSummary::to_short_text

fn ReportSummary::to_short_text(self : ReportSummary) -> String

#
Severity

pub(all) enum Severity {
Pass
Warn
Fail
} derive(Eq,
Debug
)

#
SpfMechanism

pub(all) struct SpfMechanism {
raw : String
qualifier : String
name : String
value : String
dns_lookup : Bool
terminal : Bool
} derive(Eq,
Debug
)

#
SpfRecord

pub(all) struct SpfRecord {
raw : String
valid_version : Bool
mechanisms : Array[SpfMechanism]
redirect : String
explanation : String
dns_lookup_count : Int
all_qualifier : String
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
TlsRptRecord

pub(all) struct TlsRptRecord {
raw : String
valid_version : Bool
version : String
rua_count : Int
mailto_count : Int
https_count : Int
findings : Array[PolicyFinding]
} derive(Eq,
Debug
)

#
TlsRptRecord::is_configured

fn TlsRptRecord::is_configured(self : TlsRptRecord) -> Bool

#
TlsRptRecord::uses_https

fn TlsRptRecord::uses_https(self : TlsRptRecord) -> Bool

#
TlsRptRecord::uses_mailto

fn TlsRptRecord::uses_mailto(self : TlsRptRecord) -> Bool

#
action_items

fn action_items(findings : Array[PolicyFinding]) -> Array[ActionItem]

#
analyze_domain

fn analyze_domain(domain : String, spf_text : String, dmarc_text : String) -> MailPolicyReport

#
analyze_suite

fn analyze_suite(domain : String, spf_text : String, dmarc_text : String, dkim_text : String, mtasts_text : String, tlsrpt_text : String, bimi_text : String) -> MailSecuritySuiteReport

#
analyze_suite_input

fn analyze_suite_input(input : MailSecuritySuiteInput) -> MailSecuritySuiteReport

#
analyze_suite_inputs

fn analyze_suite_inputs(inputs : Array[MailSecuritySuiteInput]) -> BatchSecurityReport

#
analyze_suite_rows

fn analyze_suite_rows(rows : Array[String]) -> BatchSecurityReport

#
assess_profile

fn assess_profile(report : MailSecuritySuiteReport, profile : MailSecurityProfile) -> ProfileAssessment

#
batch_from_reports

fn batch_from_reports(reports : Array[MailSecuritySuiteReport]) -> BatchSecurityReport

#
mailshield_version

fn mailshield_version() -> String

#
parse_bimi

fn parse_bimi(text : String) -> BimiRecord

#
parse_dkim

fn parse_dkim(text : String) -> DkimRecord

#
parse_dmarc

fn parse_dmarc(text : String) -> DmarcRecord

#
parse_mtasts

fn parse_mtasts(text : String) -> MtastsRecord

#
parse_spf

fn parse_spf(text : String) -> SpfRecord

#
parse_suite_input

fn parse_suite_input(row : String) -> MailSecuritySuiteInput

#
parse_tlsrpt

fn parse_tlsrpt(text : String) -> TlsRptRecord

#
profile_brand

fn profile_brand() -> MailSecurityProfile

#
profile_monitoring

fn profile_monitoring() -> MailSecurityProfile

#
profile_name_list

fn profile_name_list() -> Array[String]

#
profile_production

fn profile_production() -> MailSecurityProfile

#
profile_startup

fn profile_startup() -> MailSecurityProfile

#
quick_score

fn quick_score(spf_text : String, dmarc_text : String) -> Int

#
quick_verdict

fn quick_verdict(spf_text : String, dmarc_text : String) -> String

fn recommended_profile(report : MailSecuritySuiteReport) -> String

#
report_json

fn report_json(domain : String, spf_text : String, dmarc_text : String) -> String

#
report_markdown

fn report_markdown(domain : String, spf_text : String, dmarc_text : String) -> String

#
suite_input

fn suite_input(domain : String, spf_text : String, dmarc_text : String, dkim_text : String, mtasts_text : String, tlsrpt_text : String, bimi_text : String) -> MailSecuritySuiteInput

#
suite_report_json

fn suite_report_json(domain : String, spf_text : String, dmarc_text : String, dkim_text : String, mtasts_text : String, tlsrpt_text : String, bimi_text : String) -> String

#
suite_report_markdown

fn suite_report_markdown(domain : String, spf_text : String, dmarc_text : String, dkim_text : String, mtasts_text : String, tlsrpt_text : String, bimi_text : String) -> String

#
summarize_findings

fn summarize_findings(findings : Array[PolicyFinding]) -> ReportSummary

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io