{"id":13707995,"url":"https://github.com/postalsys/mailauth","last_synced_at":"2026-03-09T16:56:39.190Z","repository":{"id":38142245,"uuid":"288201441","full_name":"postalsys/mailauth","owner":"postalsys","description":"Command line utility and a Node.js library for email authentication","archived":false,"fork":false,"pushed_at":"2025-01-23T13:45:35.000Z","size":72701,"stargazers_count":131,"open_issues_count":2,"forks_count":13,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-18T15:32:34.904Z","etag":null,"topics":["arc","bimi","dkim","dmarc","email","mailauth","mta-sts","spf"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/postalsys.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/contributing.md","funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["andris9"],"custom":["https://www.paypal.me/nodemailer"]}},"created_at":"2020-08-17T14:25:45.000Z","updated_at":"2025-04-01T09:58:36.000Z","dependencies_parsed_at":"2023-09-28T11:19:35.974Z","dependency_job_id":"8159a583-aed8-4fce-8a10-fa1c87eda6d2","html_url":"https://github.com/postalsys/mailauth","commit_stats":{"total_commits":134,"total_committers":3,"mean_commits":"44.666666666666664","dds":"0.32835820895522383","last_synced_commit":"8d62ca86754c78b2cc6d664d145fd426e50155d7"},"previous_names":[],"tags_count":80,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postalsys%2Fmailauth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postalsys%2Fmailauth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postalsys%2Fmailauth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/postalsys%2Fmailauth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/postalsys","download_url":"https://codeload.github.com/postalsys/mailauth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252639976,"owners_count":21780850,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arc","bimi","dkim","dmarc","email","mailauth","mta-sts","spf"],"created_at":"2024-08-02T22:01:52.438Z","updated_at":"2026-03-09T16:56:39.177Z","avatar_url":"https://github.com/postalsys.png","language":"JavaScript","funding_links":["https://github.com/sponsors/andris9","https://www.paypal.me/nodemailer"],"categories":["JavaScript"],"sub_categories":[],"readme":"# mailauth: Email Authentication for Node.js\n\n![mailauth Logo](https://github.com/postalsys/mailauth/raw/master/assets/mailauth.png)\n\n**mailauth** is a comprehensive Node.js library and command-line utility for email authentication. It provides tools to work with various email security protocols, including SPF, DKIM, DMARC, ARC, BIMI, and MTA-STS. With mailauth, you can verify and sign emails, handle authentication results, and enhance your email security setup.\n\n**Key Features:**\n\n- **SPF** verification\n- **DKIM** signing and verification\n- **DMARC** verification\n- **ARC** verification and sealing\n    - Sealing during authentication\n    - Sealing after message modifications\n- **BIMI** resolving and **VMC** validation\n- **MTA-STS** helper functions\n\nmailauth is a pure JavaScript implementation, requiring no external applications or compilation. It runs on any server or device with Node.js version 20.18.1 or later.\n\n\u003e [!NOTE]\n\u003e mailauth is used by [EmailEngine](https://emailengine.app/) for validating email authentication settings. See the [Email Authentication Testing documentation](https://learn.emailengine.app/docs/advanced/email-authentication-testing) for details.\n\n## Table of Contents\n\n1. [Installation](#installation)\n2. [Command-Line Usage](#command-line-usage)\n3. [Library Usage](#library-usage)\n    - [Authentication](#authentication)\n    - [DKIM](#dkim)\n        - [Signing](#dkim-signing)\n        - [Verification](#dkim-verification)\n    - [SPF](#spf)\n        - [Verification](#spf-verification)\n    - [ARC](#arc)\n        - [Validation](#arc-validation)\n        - [Sealing](#arc-sealing)\n    - [DMARC](#dmarc)\n        - [Helpers](#dmarc-helpers)\n    - [BIMI](#bimi)\n    - [MTA-STS](#mta-sts)\n        - [Policy Retrieval](#policy-retrieval)\n        - [MX Validation](#mx-validation)\n4. [Testing](#testing)\n5. [License](#license)\n\n## Installation\n\nFirst, install mailauth from npm:\n\n```bash\nnpm install mailauth\n```\n\nThen, import the desired methods into your script:\n\n```javascript\nconst { authenticate } = require('mailauth');\n```\n\n## Command-Line Usage\n\nmailauth includes a command-line utility called `mailauth`. For detailed information on how to use it, see the [command-line documentation](cli.md).\n\n## Library Usage\n\n### Authentication\n\nUse the `authenticate` function to validate DKIM signatures, SPF, DMARC, ARC, and BIMI for an email.\n\n#### Syntax\n\n```javascript\nawait authenticate(message [, options])\n// Returns: { dkim, spf, arc, dmarc, bimi, receivedChain, headers }\n```\n\n#### Parameters\n\n- **message**: A `String`, `Buffer`, or `Readable` stream representing the email message.\n- **options** (optional):\n    - **sender** (`string`): Email address from the MAIL FROM command. Defaults to the `Return-Path` header if not set.\n    - **ip** (`string`): IP address of the remote client that sent the message.\n    - **helo** (`string`): Hostname from the HELO/EHLO command.\n    - **trustReceived** (`boolean`): If `true`, parses `ip` and `helo` from the latest `Received` header if not provided. Defaults to `false`.\n    - **mta** (`string`): Hostname of the server performing the authentication. Defaults to `os.hostname()`. Included in Authentication headers.\n    - **minBitLength** (`number`): Minimum allowed bits for RSA public keys. Defaults to `1024`. Keys with fewer bits will fail validation.\n    - **disableArc** (`boolean`): If `true`, skips ARC checks.\n    - **disableDmarc** (`boolean`): If `true`, skips DMARC checks, also disabling dependent checks like BIMI.\n    - **disableBimi** (`boolean`): If `true`, skips BIMI checks.\n    - **seal** (`object`): Options for ARC sealing if the message doesn't have a broken ARC chain.\n        - **signingDomain** (`string`): ARC key domain name.\n        - **selector** (`string`): ARC key selector.\n        - **privateKey** (`string` or `Buffer`): Private key for signing (RSA or Ed25519).\n    - **resolver** (`async function`): Custom DNS resolver function. Defaults to [`dns.promises.resolve`](https://nodejs.org/api/dns.html#dns_dnspromises_resolve_hostname_rrtype).\n    - **maxResolveCount** (`number`): DNS lookup limit for SPF. Defaults to `10` as per [RFC7208](https://datatracker.ietf.org/doc/html/rfc7208#section-4.6.4).\n    - **maxVoidCount** (`number`): DNS lookup limit for SPF producing empty results. Defaults to `2` as per [RFC7208](https://datatracker.ietf.org/doc/html/rfc7208#section-4.6.4).\n\n#### Example\n\n```javascript\nconst { authenticate } = require('mailauth');\nconst dns = require('dns');\n\nconst message = /* Your email message here */;\n\nconst { dkim, spf, arc, dmarc, bimi, receivedChain, headers } = await authenticate(message, {\n  // SMTP transmission options\n  ip: '217.146.67.33',                 // SMTP client IP\n  helo: 'uvn-67-33.tll01.zonevs.eu',   // HELO/EHLO hostname\n  sender: 'andris@ekiri.ee',           // MAIL FROM address\n\n  // Uncomment to parse `ip` and `helo` from the latest `Received` header\n  // trustReceived: true,\n\n  // Server performing the authentication\n  mta: 'mx.ethereal.email',\n\n  // Optional DNS resolver function\n  resolver: async (name, rr) =\u003e await dns.promises.resolve(name, rr),\n});\n\n// Output authenticated message\nprocess.stdout.write(headers); // Includes terminating line break\nprocess.stdout.write(message);\n```\n\n**Sample Output:**\n\n```\nReceived-SPF: pass (mx.ethereal.email: domain of andris@ekiri.ee designates 217.146.67.33 as permitted sender) client-ip=217.146.67.33;\nAuthentication-Results: mx.ethereal.email;\n dkim=pass header.i=@ekiri.ee header.s=default header.a=rsa-sha256 header.b=TXuCNlsq;\n spf=pass (mx.ethereal.email: domain of andris@ekiri.ee designates 217.146.67.33 as permitted sender) smtp.mailfrom=andris@ekiri.ee\n smtp.helo=uvn-67-33.tll01.zonevs.eu;\n arc=pass (i=2 spf=neutral dkim=pass dkdomain=ekiri.ee);\n dmarc=none header.from=ekiri.ee\nFrom: ...\n```\n\nYou can see the full output, including structured data for DKIM, SPF, DMARC, and ARC, from [this example](https://gist.github.com/andris9/6514b5e7c59154a5b08636f99052ce37).\n\n**Note:** The `receivedChain` property is an array of parsed representations of the `Received:` headers.\n\n### DKIM\n\n#### DKIM Signing\n\nUse the `dkimSign` function to sign an email message with DKIM.\n\n##### Syntax\n\n```javascript\nconst { dkimSign } = require('mailauth/lib/dkim/sign');\n\nconst signResult = await dkimSign(message, options);\n// Returns: { signatures: String, errors: Array }\n```\n\n##### Parameters\n\n- **message**: A `String`, `Buffer`, or `Readable` stream representing the email message.\n- **options**:\n    - **canonicalization** (`string`): Canonicalization method. Defaults to `'relaxed/relaxed'`.\n    - **algorithm** (`string`): Signing and hashing algorithm. Defaults to `'rsa-sha256'`.\n    - **signTime** (`Date`): Signing time. Defaults to current time.\n    - **expires** (`Date`): Signature expiration time (`x=` tag). Optional.\n    - **headerList** (`Array`): List of header field names to sign. Optional; uses default set if not specified.\n    - **signatureData** (`Array`): Array of signature objects. Each object may contain:\n        - **signingDomain** (`string`): DKIM key domain name.\n        - **selector** (`string`): DKIM key selector.\n        - **privateKey** (`string` or `Buffer`): Private key for signing (RSA or Ed25519).\n        - **algorithm** (`string`, optional): Overrides parent `algorithm`.\n        - **canonicalization** (`string`, optional): Overrides parent `canonicalization`.\n        - **maxBodyLength** (`number`, optional): Maximum number of canonicalized body bytes to sign (`l=` tag). Not recommended for general use.\n\n##### Example\n\n```javascript\nconst { dkimSign } = require('mailauth/lib/dkim/sign');\nconst fs = require('fs');\n\nconst message = /* Your email message here */;\n\nconst signResult = await dkimSign(message, {\n  canonicalization: 'relaxed/relaxed',\n  algorithm: 'rsa-sha256',\n  signTime: new Date(),\n  signatureData: [\n    {\n      signingDomain: 'tahvel.info',\n      selector: 'test.rsa',\n      privateKey: fs.readFileSync('./test/fixtures/private-rsa.pem'),\n    },\n  ],\n});\n\n// Display signing errors if any\nif (signResult.errors.length) {\n  console.error('Signing errors:', signResult.errors);\n}\n\n// Output signed message\nprocess.stdout.write(signResult.signatures); // Includes terminating line break\nprocess.stdout.write(message);\n```\n\n**Sample Output:**\n\n```\nDKIM-Signature: a=rsa-sha256; v=1; c=relaxed/relaxed; d=tahvel.info;\n s=test.rsa; b=...\nFrom: ...\n```\n\n#### DKIM Signing as a Stream\n\nUse `DkimSignStream` to sign messages as part of a stream processing pipeline.\n\n##### Example\n\n```javascript\nconst { DkimSignStream } = require('mailauth/lib/dkim/sign');\nconst fs = require('fs');\n\nconst dkimSignStream = new DkimSignStream({\n    canonicalization: 'relaxed/relaxed',\n    algorithm: 'rsa-sha256',\n    signTime: new Date(),\n    signatureData: [\n        {\n            signingDomain: 'tahvel.info',\n            selector: 'test.rsa',\n            privateKey: fs.readFileSync('./test/fixtures/private-rsa.pem')\n        }\n    ]\n});\n\n// Read from stdin, write signed message to stdout\nprocess.stdin.pipe(dkimSignStream).pipe(process.stdout);\n```\n\n#### DKIM Verification\n\nUse the `dkimVerify` function to verify DKIM signatures in an email message.\n\n##### Syntax\n\n```javascript\nconst { dkimVerify } = require('mailauth/lib/dkim/verify');\n\nconst result = await dkimVerify(message);\n// Returns an object containing verification results\n```\n\nSee [DKIM Result Reference](docs/dkim.md) for details on the result object structure.\n\n##### Example\n\n```javascript\nconst { dkimVerify } = require('mailauth/lib/dkim/verify');\n\nconst message = /* Your email message here */;\n\nconst result = await dkimVerify(message);\n\nfor (const { info } of result.results) {\n  console.log(info);\n}\n```\n\n**Sample Output:**\n\n```\ndkim=neutral (invalid public key) header.i=@tahvel.info header.s=test.invalid header.b=\"b85yao+1\"\ndkim=pass header.i=@tahvel.info header.s=test.rsa header.b=\"BrEgDN4A\"\ndkim=policy policy.dkim-rules=weak-key header.i=@tahvel.info header.s=test.small header.b=\"d0jjgPun\"\n```\n\n### SPF\n\n#### SPF Verification\n\nUse the `spf` function to verify the SPF record for an email sender.\n\n##### Syntax\n\n```javascript\nconst { spf } = require('mailauth/lib/spf');\n\nconst result = await spf(options);\n// Returns an object containing SPF verification results\n```\n\nSee [SPF Result Reference](docs/spf.md) for details on the result object structure.\n\n##### Parameters\n\n- **options**:\n    - **sender** (`string`): MAIL FROM address.\n    - **ip** (`string`): SMTP client IP.\n    - **helo** (`string`): HELO/EHLO hostname.\n    - **mta** (`string`): Hostname of the MTA performing the check.\n\n##### Example\n\n```javascript\nconst { spf } = require('mailauth/lib/spf');\n\nconst result = await spf({\n    sender: 'andris@wildduck.email',\n    ip: '217.146.76.20',\n    helo: 'foo',\n    mta: 'mx.myhost.com'\n});\n\nconsole.log(result.header);\n```\n\n**Sample Output:**\n\n```\nReceived-SPF: pass (mx.myhost.com: domain of andris@wildduck.email\n designates 217.146.76.20 as permitted sender) client-ip=217.146.76.20;\n envelope-from=\"andris@wildduck.email\";\n```\n\n### ARC\n\n#### ARC Validation\n\nARC seals are validated automatically during the authentication step.\n\n##### Example\n\n```javascript\nconst { authenticate } = require('mailauth');\n\nconst message = /* Your email message here */;\n\nconst { arc } = await authenticate(message, {\n  trustReceived: true,\n});\n\nconsole.log(arc);\n```\n\nSee [ARC Result Reference](docs/arc.md) for details on the result object structure.\n\n**Sample Output:**\n\n```json\n{\n    \"status\": {\n        \"result\": \"pass\",\n        \"comment\": \"i=2 spf=neutral dkim=pass dkdomain=zonevs.eu dkim=pass dkdomain=srs3.zonevs.eu dmarc=fail fromdomain=zone.ee\"\n    },\n    \"i\": 2\n    // Additional properties...\n}\n```\n\n#### ARC Sealing\n\nYou can seal messages with ARC either during authentication or after modifications.\n\n##### Sealing During Authentication\n\nProvide the sealing key in the options to seal messages automatically during authentication.\n\n```javascript\nconst { authenticate } = require('mailauth');\nconst fs = require('fs');\n\nconst message = /* Your email message here */;\n\nconst { headers } = await authenticate(message, {\n  trustReceived: true,\n  seal: {\n    signingDomain: 'tahvel.info',\n    selector: 'test.rsa',\n    privateKey: fs.readFileSync('./test/fixtures/private-rsa.pem'),\n  },\n});\n\n// Output authenticated and sealed message\nprocess.stdout.write(headers); // Includes terminating line break\nprocess.stdout.write(message);\n```\n\n##### Sealing After Modifications\n\nIf you need to modify the message before sealing, first authenticate it, modify as needed, then seal using the authentication results.\n\n```javascript\nconst { authenticate, sealMessage } = require('mailauth');\nconst fs = require('fs');\n\nconst message = /* Your email message here */;\n\n// Step 1: Authenticate the message\nconst { arc, headers } = await authenticate(message, {\n  ip: '217.146.67.33',\n  helo: 'uvn-67-33.tll01.zonevs.eu',\n  mta: 'mx.ethereal.email',\n  sender: 'andris@ekiri.ee',\n});\n\n// Step 2: Modify the message as needed\n// ... your modifications ...\n\n// Step 3: Seal the modified message\nconst sealHeaders = await sealMessage(message, {\n  signingDomain: 'tahvel.info',\n  selector: 'test.rsa',\n  privateKey: fs.readFileSync('./test/fixtures/private-rsa.pem'),\n  authResults: arc.authResults,\n  cv: arc.status.result,\n});\n\n// Output the sealed message\nprocess.stdout.write(sealHeaders); // ARC headers\nprocess.stdout.write(headers);     // Authentication results\nprocess.stdout.write(message);\n```\n\n### DMARC\n\nDMARC is verified during the authentication process. Although the `dmarc` handler is exported, it requires input from previous steps like SPF and DKIM.\n\nSee [DMARC Result Reference](docs/dmarc.md) for details on the result object structure.\n\n#### DMARC Helpers\n\n##### `getDmarcRecord(domain [, resolver])`\n\nFetches and parses the DMARC DNS record for a domain or subdomain. Returns `false` if no record exists.\n\n###### Syntax\n\n```javascript\nconst getDmarcRecord = require('mailauth/lib/dmarc/get-dmarc-record');\n\nconst dmarcRecord = await getDmarcRecord(domain [, resolver]);\n// Returns an object with DMARC record details or `false` if not found\n```\n\n###### Parameters\n\n- **domain** (`string`): The domain to check for a DMARC record.\n- **resolver** (`function`, optional): Custom DNS resolver function. Defaults to `dns.resolve`.\n\n###### Example\n\n```javascript\nconst getDmarcRecord = require('mailauth/lib/dmarc/get-dmarc-record');\n\nconst dmarcRecord = await getDmarcRecord('ethereal.email');\nconsole.log(dmarcRecord);\n```\n\n**Sample Output:**\n\n```json\n{\n    \"v\": \"DMARC1\",\n    \"p\": \"none\",\n    \"pct\": 100,\n    \"rua\": \"mailto:re+joqy8fpatm3@dmarc.postmarkapp.com\",\n    \"sp\": \"none\",\n    \"aspf\": \"r\",\n    \"rr\": \"v=DMARC1; p=none; pct=100; rua=mailto:re+joqy8fpatm3@dmarc.postmarkapp.com; sp=none; aspf=r;\",\n    \"isOrgRecord\": false\n}\n```\n\n### BIMI\n\nBrand Indicators for Message Identification (BIMI) support is based on [draft-blank-ietf-bimi-02](https://tools.ietf.org/html/draft-blank-ietf-bimi-02). BIMI information is resolved during the authentication step, provided the message passes DMARC validation with a policy other than \"none\".\n\nSee [BIMI Result Reference](docs/bimi.md) for details on the result object structure.\n\n#### Example\n\n```javascript\nconst { authenticate } = require('mailauth');\n\nconst message = /* Your email message here */;\n\nconst { bimi } = await authenticate(message, {\n  ip: '217.146.67.33',\n  helo: 'uvn-67-33.tll01.zonevs.eu',\n  mta: 'mx.ethereal.email',\n  sender: 'andris@ekiri.ee',\n  bimiWithAlignedDkim: false, // If true, ignores SPF in DMARC and requires a valid DKIM signature\n});\n\nif (bimi?.location) {\n  console.log(`BIMI location: ${bimi.location}`);\n}\n```\n\n**Note:**\n\n- The `BIMI-Location` header is ignored by mailauth.\n- The `BIMI-Selector` header can be used for selector selection if available.\n\n#### Verified Mark Certificate (VMC)\n\nIf an Authority Evidence Document is specified in the BIMI record, its location is available in `bimi.authority`. mailauth exposes the certificate type (`\"VMC\"` or `\"CMC\"`) in `bimi.authority.vmc.type`.\n\n**Example Authority Evidence Documents:**\n\n- [CNN's VMC](https://amplify.valimail.com/bimi/time-warner/LysAFUdG-Hw-cnn_vmc.pem)\n- [Entrust's VMC](https://www.entrustdatacard.com/-/media/certificate/Entrust%20VMC%20July%2014%202020.pem)\n\n### MTA-STS\n\nmailauth provides functions to fetch and validate MTA-STS policies for a domain.\n\n#### Policy Retrieval\n\nUse the `getPolicy` function to fetch the MTA-STS policy for a domain.\n\n##### Syntax\n\n```javascript\nconst { getPolicy } = require('mailauth/lib/mta-sts');\n\nconst { policy, status } = await getPolicy(domain [, knownPolicy]);\n// Returns an object with the policy and status\n```\n\nSee [MTA-STS Result Reference](docs/mta-sts.md) for details on the result object structure.\n\n##### Parameters\n\n- **domain** (`string`): The domain to retrieve the policy for.\n- **knownPolicy** (`object`, optional): Previously cached policy for the domain.\n\n##### Example\n\n```javascript\nconst { getPolicy } = require('mailauth/lib/mta-sts');\n\nconst knownPolicy = /* Retrieve from your cache if available */;\nconst { policy, status } = await getPolicy('gmail.com', knownPolicy);\n\nif (policy.id !== knownPolicy?.id) {\n  // Update your cache with the new policy\n}\n\nif (policy.mode === 'enforce') {\n  // TLS must be used when sending to this domain\n}\n```\n\n**Possible Status Values:**\n\n- `\"not_found\"`: No policy was found.\n- `\"cached\"`: Existing policy is still valid.\n- `\"found\"`: New or updated policy found.\n- `\"renew\"`: Existing policy is valid; renew cache.\n- `\"errored\"`: Policy discovery failed due to a temporary error.\n\n#### MX Validation\n\nUse the `validateMx` function to check if an MX hostname is valid according to the MTA-STS policy.\n\n##### Syntax\n\n```javascript\nconst { validateMx } = require('mailauth/lib/mta-sts');\n\nconst validation = validateMx(mx, policy);\n// Returns an object indicating if the MX is valid\n```\n\n##### Parameters\n\n- **mx** (`string`): The resolved MX hostname.\n- **policy** (`object`): The MTA-STS policy object.\n\n##### Example\n\n```javascript\nconst { getPolicy, validateMx } = require('mailauth/lib/mta-sts');\n\nconst { policy } = await getPolicy('gmail.com');\n\nconst mx = 'alt4.gmail-smtp-in.l.google.com';\nconst policyMatch = validateMx(mx, policy);\n\nif (policy.mx \u0026\u0026 !policyMatch.valid) {\n    // The MX host is not listed in the policy; do not connect\n}\n```\n\n## Testing\n\nmailauth uses the following test suites:\n\n### SPF Test Suite\n\nBased on the [OpenSPF test suite](http://www.openspf.org/Test_Suite), with some differences:\n\n- Less strict whitespace checks.\n- Some macro tests are skipped.\n- Some tests are skipped where the invalid component is after a matching part.\n- All other tests pass.\n\n### ARC Test Suite from ValiMail\n\nBased on ValiMail's [arc_test_suite](https://github.com/ValiMail/arc_test_suite):\n\n- mailauth is less strict on header tags and casing.\n- Signing test suite is used for input; mailauth validates signatures and checks for the same `cv=` output.\n- All tests pass, aside from minor differences.\n\n## License\n\n\u0026copy; 2020-2026 Postal Systems OÜ\n\nLicensed under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpostalsys%2Fmailauth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpostalsys%2Fmailauth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpostalsys%2Fmailauth/lists"}