mailauth 4.12.1 → 4.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "4.12.1"
2
+ ".": "4.13.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.13.0](https://github.com/postalsys/mailauth/compare/v4.12.1...v4.13.0) (2026-02-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * **bimi:** add BIMI headers to VMC validation output ([77ce4e8](https://github.com/postalsys/mailauth/commit/77ce4e847d79b2acf319c3b79e865225e4c97040))
9
+ * **dkim:** add timestamp, expiration, and validity status to output ([2267eb7](https://github.com/postalsys/mailauth/commit/2267eb77fbcaf79a6c0fb59681815628186a3ae9))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * update Node.js requirement to >=20.18.1 ([3280a59](https://github.com/postalsys/mailauth/commit/3280a597430cf47b640b1cc31e661ab18becf145)), closes [#109](https://github.com/postalsys/mailauth/issues/109)
15
+
3
16
  ## [4.12.1](https://github.com/postalsys/mailauth/compare/v4.12.0...v4.12.1) (2026-02-01)
4
17
 
5
18
 
package/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2020-2025 Postal Systems OÜ
1
+ Copyright (c) 2020-2026 Postal Systems OÜ
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
  - **BIMI** resolving and **VMC** validation
16
16
  - **MTA-STS** helper functions
17
17
 
18
- mailauth is a pure JavaScript implementation, requiring no external applications or compilation. It runs on any server or device with Node.js version 16 or later.
18
+ mailauth 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.
19
19
 
20
20
  > [!NOTE]
21
21
  > 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.
@@ -163,6 +163,8 @@ const signResult = await dkimSign(message, options);
163
163
  - **canonicalization** (`string`): Canonicalization method. Defaults to `'relaxed/relaxed'`.
164
164
  - **algorithm** (`string`): Signing and hashing algorithm. Defaults to `'rsa-sha256'`.
165
165
  - **signTime** (`Date`): Signing time. Defaults to current time.
166
+ - **expires** (`Date`): Signature expiration time (`x=` tag). Optional.
167
+ - **headerList** (`Array`): List of header field names to sign. Optional; uses default set if not specified.
166
168
  - **signatureData** (`Array`): Array of signature objects. Each object may contain:
167
169
  - **signingDomain** (`string`): DKIM key domain name.
168
170
  - **selector** (`string`): DKIM key selector.
@@ -250,6 +252,8 @@ const result = await dkimVerify(message);
250
252
  // Returns an object containing verification results
251
253
  ```
252
254
 
255
+ See [DKIM Result Reference](docs/dkim.md) for details on the result object structure.
256
+
253
257
  ##### Example
254
258
 
255
259
  ```javascript
@@ -287,6 +291,8 @@ const result = await spf(options);
287
291
  // Returns an object containing SPF verification results
288
292
  ```
289
293
 
294
+ See [SPF Result Reference](docs/spf.md) for details on the result object structure.
295
+
290
296
  ##### Parameters
291
297
 
292
298
  - **options**:
@@ -338,6 +344,8 @@ const { arc } = await authenticate(message, {
338
344
  console.log(arc);
339
345
  ```
340
346
 
347
+ See [ARC Result Reference](docs/arc.md) for details on the result object structure.
348
+
341
349
  **Sample Output:**
342
350
 
343
351
  ```json
@@ -419,6 +427,8 @@ process.stdout.write(message);
419
427
 
420
428
  DMARC is verified during the authentication process. Although the `dmarc` handler is exported, it requires input from previous steps like SPF and DKIM.
421
429
 
430
+ See [DMARC Result Reference](docs/dmarc.md) for details on the result object structure.
431
+
422
432
  #### DMARC Helpers
423
433
 
424
434
  ##### `getDmarcRecord(domain [, resolver])`
@@ -467,6 +477,8 @@ console.log(dmarcRecord);
467
477
 
468
478
  Brand 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".
469
479
 
480
+ See [BIMI Result Reference](docs/bimi.md) for details on the result object structure.
481
+
470
482
  #### Example
471
483
 
472
484
  ```javascript
@@ -518,6 +530,8 @@ const { policy, status } = await getPolicy(domain [, knownPolicy]);
518
530
  // Returns an object with the policy and status
519
531
  ```
520
532
 
533
+ See [MTA-STS Result Reference](docs/mta-sts.md) for details on the result object structure.
534
+
521
535
  ##### Parameters
522
536
 
523
537
  - **domain** (`string`): The domain to retrieve the policy for.
@@ -604,6 +618,6 @@ Based on ValiMail's [arc_test_suite](https://github.com/ValiMail/arc_test_suite)
604
618
 
605
619
  ## License
606
620
 
607
- © 2020-2024 Postal Systems OÜ
621
+ © 2020-2026 Postal Systems OÜ
608
622
 
609
623
  Licensed under the [MIT License](LICENSE).
package/cli.md CHANGED
@@ -268,6 +268,7 @@ mailauth vmc [options]
268
268
  - `--authority <url>`, `-a <url>`: URL of the VMC resource.
269
269
  - `--authorityPath <path>`, `-p <path>`: Path to a local VMC file, used to avoid network requests.
270
270
  - `--domain <domain>`, `-d <domain>`: Sender domain to validate against the certificate.
271
+ - `--date <timestamp>`, `-t <timestamp>`: ISO-formatted timestamp for certificate expiration checks. Defaults to the current time.
271
272
 
272
273
  #### Example
273
274
 
@@ -421,6 +422,6 @@ When this option is used, mailauth will not perform actual DNS queries but will
421
422
 
422
423
  ## License
423
424
 
424
- &copy; 2020-2024 Postal Systems OÜ
425
+ &copy; 2020-2026 Postal Systems OÜ
425
426
 
426
427
  Licensed under the [MIT License](LICENSE).
package/docs/arc.md ADDED
@@ -0,0 +1,176 @@
1
+ # ARC Validation Result Reference
2
+
3
+ This document describes the result object returned by ARC (Authenticated Received Chain) validation and sealing.
4
+
5
+ ## Overview
6
+
7
+ ARC validation is performed automatically during the authentication step. ARC allows intermediate mail handlers to sign messages, preserving authentication results across forwarding.
8
+
9
+ ```javascript
10
+ const { authenticate } = require('mailauth');
11
+
12
+ const { arc } = await authenticate(message, {
13
+ trustReceived: true
14
+ });
15
+ ```
16
+
17
+ ## Validation Result Object Fields
18
+
19
+ | Field | Type | Presence | Description |
20
+ |-------|------|----------|-------------|
21
+ | `status` | `object` | Always | Validation status object (see below) |
22
+ | `chain` | `array` | Non-enumerable | Array of ARC chain entries (hidden from JSON serialization) |
23
+ | `i` | `number\|false` | Always | Last instance number in chain, or `false` if no chain |
24
+ | `signature` | `object` | Chain exists | Verification result for last ARC-Message-Signature |
25
+ | `authenticationResults` | `object` | Chain exists | Parsed last ARC-Authentication-Results |
26
+ | `info` | `string` | Result not "none" | Formatted Authentication-Results header value |
27
+
28
+ ## status Object
29
+
30
+ | Field | Type | Presence | Description |
31
+ |-------|------|----------|-------------|
32
+ | `result` | `string` | Always | ARC result code (see below) |
33
+ | `comment` | `string` | On pass/fail | Description or error message |
34
+ | `shouldSeal` | `boolean` | On fail | Whether to continue sealing despite failure |
35
+ | `policy` | `object` | Policy issue | Policy violation details (e.g., `{"dkim-rules": "weak-key"}`) |
36
+
37
+ ## authenticationResults Object
38
+
39
+ When ARC validation passes, the `authenticationResults` object contains parsed results from the last ARC-Authentication-Results header:
40
+
41
+ | Field | Type | Description |
42
+ |-------|------|-------------|
43
+ | `mta` | `string` | Hostname of the MTA that added this ARC set |
44
+ | `arc` | `object` | ARC result (`{result: "pass\|fail\|none", ...}`) |
45
+ | `spf` | `object` | SPF result (`{result: "pass\|fail\|...", ...}`) |
46
+ | `dmarc` | `object` | DMARC result (`{result: "pass\|fail\|none", header: {...}}`) |
47
+ | `dkim` | `array` | Array of DKIM results |
48
+
49
+ ## Result Values
50
+
51
+ | Result | Description |
52
+ |--------|-------------|
53
+ | `none` | No ARC chain present in message |
54
+ | `pass` | ARC chain validated successfully |
55
+ | `fail` | ARC chain validation failed |
56
+
57
+ ## Comment Values (on failure)
58
+
59
+ | Comment Pattern | Description |
60
+ |-----------------|-------------|
61
+ | `"i={n} seal signature validation failed"` | ARC-Seal cryptographic verification failed |
62
+ | `"i={n} no valid signature"` | ARC-Message-Signature verification failed |
63
+ | `"i={n} multiple {header} values"` | Duplicate ARC headers for same instance |
64
+ | `"chain-length={n}"` | Chain exceeds 50 instances |
65
+ | `"i={n} expected={m}"` | Missing or out-of-order instance numbers |
66
+ | `"i={n} no {header} set"` | Missing required ARC header |
67
+ | `"i=1 cv={value}"` | First instance must have `cv=none` |
68
+ | `"i={n} cv={value}"` | Non-first instance must have `cv=pass` |
69
+ | `"i={n} invalid as c"` | Invalid canonicalization for ARC-Seal |
70
+ | `"i={n} invalid ams h"` | ARC-Message-Signature signed arc-seal (forbidden) |
71
+ | `"no key for {domain}"` | DNS key not found |
72
+ | `"unknown key version for {domain}"` | Unsupported key version |
73
+ | `"unknown key type for {domain}"` | Unsupported key type |
74
+ | `"invalid public key for {domain}"` | Malformed public key |
75
+ | `"weak key for {domain}"` | RSA key too short |
76
+
77
+ ## Seal Result Object
78
+
79
+ When sealing a message with `sealMessage()`, the result contains:
80
+
81
+ | Field | Type | Description |
82
+ |-------|------|-------------|
83
+ | `headers` | `string[]` | Array of ARC headers to prepend: `[ARC-Seal, ARC-Message-Signature, ARC-Authentication-Results]` |
84
+
85
+ ## Example Output
86
+
87
+ ### ARC Pass
88
+
89
+ ```json
90
+ {
91
+ "status": {
92
+ "result": "pass",
93
+ "comment": "i=2 spf=pass dkim=pass dkdomain=example.com dmarc=pass fromdomain=example.com"
94
+ },
95
+ "i": 2,
96
+ "signature": {
97
+ "id": "abc123...",
98
+ "signingDomain": "forwarder.example.net",
99
+ "selector": "arc",
100
+ "status": {
101
+ "result": "pass"
102
+ }
103
+ },
104
+ "authenticationResults": {
105
+ "mta": "mx.forwarder.example.net",
106
+ "spf": {
107
+ "result": "pass",
108
+ "smtp": {
109
+ "mailfrom": "user@example.com"
110
+ }
111
+ },
112
+ "dkim": [
113
+ {
114
+ "result": "pass",
115
+ "header": {
116
+ "i": "@example.com",
117
+ "s": "selector1"
118
+ }
119
+ }
120
+ ],
121
+ "dmarc": {
122
+ "result": "pass",
123
+ "header": {
124
+ "from": "example.com"
125
+ }
126
+ }
127
+ },
128
+ "info": "arc=pass (i=2 spf=pass dkim=pass dkdomain=example.com dmarc=pass fromdomain=example.com)"
129
+ }
130
+ ```
131
+
132
+ ### ARC Fail
133
+
134
+ ```json
135
+ {
136
+ "status": {
137
+ "result": "fail",
138
+ "comment": "i=2 seal signature validation failed",
139
+ "shouldSeal": true
140
+ },
141
+ "i": 2,
142
+ "info": "arc=fail (i=2 seal signature validation failed)"
143
+ }
144
+ ```
145
+
146
+ ### ARC None
147
+
148
+ ```json
149
+ {
150
+ "status": {
151
+ "result": "none"
152
+ },
153
+ "i": 0
154
+ }
155
+ ```
156
+
157
+ ### Seal Headers Output
158
+
159
+ When using `sealMessage()`:
160
+
161
+ ```javascript
162
+ const { sealMessage } = require('mailauth');
163
+
164
+ const sealHeaders = await sealMessage(message, {
165
+ signingDomain: 'example.com',
166
+ selector: 'arc',
167
+ privateKey: privateKey,
168
+ authResults: 'mx.example.com; spf=pass; dkim=pass',
169
+ cv: 'pass'
170
+ });
171
+
172
+ // sealHeaders is a Buffer containing:
173
+ // ARC-Seal: i=1; a=rsa-sha256; cv=pass; d=example.com; s=arc; ...
174
+ // ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; ...
175
+ // ARC-Authentication-Results: i=1; mx.example.com; spf=pass; dkim=pass
176
+ ```
package/docs/bimi.md ADDED
@@ -0,0 +1,272 @@
1
+ # BIMI Result Reference
2
+
3
+ This document describes the result object returned by BIMI (Brand Indicators for Message Identification) resolution.
4
+
5
+ ## Overview
6
+
7
+ BIMI allows organizations to display brand logos in email clients. BIMI information is resolved during the authentication step, provided the message passes DMARC validation with a policy other than "none".
8
+
9
+ ```javascript
10
+ const { authenticate } = require('mailauth');
11
+
12
+ const { bimi } = await authenticate(message, {
13
+ ip: '192.0.2.1',
14
+ helo: 'mail.example.com',
15
+ sender: 'user@example.com'
16
+ });
17
+ ```
18
+
19
+ ## Result Object Fields
20
+
21
+ | Field | Type | Presence | Description |
22
+ |-------|------|----------|-------------|
23
+ | `status` | `object` | Always | Resolution status object (see below) |
24
+ | `location` | `string` | Found | HTTPS URL for the logo SVG file (from `l=` tag) |
25
+ | `authority` | `string` | Found | HTTPS URL for the VMC/CMC certificate (from `a=` tag) |
26
+ | `preference` | `string` | When `p=` present | Logo preference value from DNS record |
27
+ | `rr` | `string` | Found | Raw BIMI DNS TXT record |
28
+ | `info` | `string` | Always | Formatted Authentication-Results header value |
29
+
30
+ ## status Object
31
+
32
+ | Field | Type | Presence | Description |
33
+ |-------|------|----------|-------------|
34
+ | `result` | `string` | Always | BIMI result code (see below) |
35
+ | `comment` | `string` | On skip/fail | Reason for skip or failure |
36
+ | `header` | `object` | Always | Header information |
37
+ | `policy` | `object` | VMC found | Authority policy details |
38
+
39
+ ### status.header Object
40
+
41
+ | Field | Type | Presence | Description |
42
+ |-------|------|----------|-------------|
43
+ | `selector` | `string` | Record found | BIMI selector used (e.g., `"default"`) |
44
+ | `d` | `string` | Record found | Domain where BIMI record was found |
45
+
46
+ ### status.policy Object
47
+
48
+ | Field | Type | Description |
49
+ |-------|------|-------------|
50
+ | `authority` | `string` | VMC validation status (`"none"` before validation) |
51
+ | `authority-uri` | `string` | URL of the authority evidence document |
52
+
53
+ ## Result Values
54
+
55
+ | Result | Description |
56
+ |--------|-------------|
57
+ | `pass` | BIMI record found and valid |
58
+ | `skipped` | BIMI lookup skipped (see skip reasons below) |
59
+ | `fail` | BIMI record found but invalid |
60
+ | `none` | No BIMI record found |
61
+ | `temperr` | Temporary error during DNS lookup |
62
+
63
+ ## Skip Reasons
64
+
65
+ The `status.comment` field explains why BIMI was skipped:
66
+
67
+ | Comment | Description |
68
+ |---------|-------------|
69
+ | `"DMARC not enabled"` | DMARC result was `none` |
70
+ | `"message failed DMARC"` | DMARC result was not `pass` |
71
+ | `"too lax DMARC policy"` | DMARC policy is `none` or `quarantine` with `pct < 100` |
72
+ | `"Aligned DKIM signature required"` | `bimiWithAlignedDkim` option set but no aligned DKIM |
73
+ | `"undersized DKIM signature"` | DKIM signature has unsigned body bytes (due to `l=` tag) |
74
+ | `"could not determine domain"` | Unable to extract domain from headers |
75
+
76
+ ## Fail Reasons
77
+
78
+ | Comment | Description |
79
+ |---------|-------------|
80
+ | `"multiple BIMI-Selector headers"` | Message has more than one BIMI-Selector header |
81
+ | `"missing bimi version in selector header"` | BIMI-Selector header missing `v=BIMI1` |
82
+ | `"missing bimi version in dns record"` | DNS record missing `v=BIMI1` |
83
+ | `"missing location value in dns record"` | Record has neither `l=` nor `a=` tag |
84
+ | `"invalid location value in dns record"` | `l=` value is not a valid HTTPS URL |
85
+ | `"invalid authority value in dns record"` | `a=` value is not a valid HTTPS URL |
86
+ | `"failed to resolve {domain}"` | DNS lookup error |
87
+ | `"invalid BIMI response for {domain}"` | DNS response format invalid |
88
+
89
+ ## VMC Validation Result
90
+
91
+ When using `validateVMC()` to validate the authority evidence document:
92
+
93
+ ```javascript
94
+ const { bimi, validateVMC } = require('mailauth/lib/bimi');
95
+
96
+ const bimiResult = await bimi(data);
97
+ const vmcResult = await validateVMC(bimiResult, options);
98
+ ```
99
+
100
+ ### VMC Result Object
101
+
102
+ | Field | Type | Description |
103
+ |-------|------|-------------|
104
+ | `location` | `object` | Logo file fetch result |
105
+ | `authority` | `object` | VMC/CMC fetch and validation result |
106
+ | `headers` | `object` | Ready-to-use email headers (only on validation success) |
107
+
108
+ ### location Object
109
+
110
+ | Field | Type | Description |
111
+ |-------|------|-------------|
112
+ | `url` | `string` | Logo URL |
113
+ | `success` | `boolean` | Whether fetch succeeded |
114
+ | `logoFile` | `string` | Base64-encoded logo SVG (on success) |
115
+ | `error` | `object` | Error details (on failure) |
116
+ | `hashAlgo` | `string` | Hash algorithm used for verification |
117
+ | `hashValue` | `string` | Calculated hash of the logo file |
118
+
119
+ ### authority Object
120
+
121
+ | Field | Type | Description |
122
+ |-------|------|-------------|
123
+ | `url` | `string` | VMC/CMC URL |
124
+ | `success` | `boolean` | Whether fetch and validation succeeded |
125
+ | `vmc` | `object` | Parsed VMC data (on success) |
126
+ | `domainVerified` | `boolean` | Whether domain matches certificate |
127
+ | `hashMatch` | `boolean` | Whether logo hash matches certificate |
128
+ | `error` | `object` | Error details (on failure) |
129
+
130
+ ### headers Object
131
+
132
+ Present only when VMC validation succeeds. Contains ready-to-use email headers.
133
+
134
+ | Field | Type | Presence | Description |
135
+ |-------|------|----------|-------------|
136
+ | `indicator` | `string` | Always | BIMI-Indicator header with base64-encoded SVG logo |
137
+ | `location` | `string` | Always | BIMI-Location header with logo URL |
138
+ | `preference` | `string` | `p=` tag present | BIMI-Logo-Preference header |
139
+
140
+ These headers should be added to messages after successful BIMI validation. The MTA should:
141
+ 1. Remove any existing BIMI-* headers from incoming messages
142
+ 2. Add these headers after successful validation
143
+
144
+ ### VMC Error Codes
145
+
146
+ | Code | Description |
147
+ |------|-------------|
148
+ | `HTTP_REQUEST_FAILED` | HTTP request failed |
149
+ | `MISSING_VMC_LOGO` | VMC does not contain a logo file |
150
+ | `INVALID_MEDIATYPE` | Logo media type is not `image/svg+xml` |
151
+ | `INVALID_LOGO_HASH` | Logo hash does not match certificate |
152
+ | `SVG_VALIDATION_FAILED` | SVG file failed validation |
153
+ | `VMC_DOMAIN_MISMATCH` | Domain not found in certificate SAN |
154
+
155
+ ## Example Output
156
+
157
+ ### BIMI Pass
158
+
159
+ ```json
160
+ {
161
+ "status": {
162
+ "result": "pass",
163
+ "header": {
164
+ "selector": "default",
165
+ "d": "example.com"
166
+ },
167
+ "policy": {
168
+ "authority": "none",
169
+ "authority-uri": "https://example.com/bimi/vmc.pem"
170
+ }
171
+ },
172
+ "location": "https://example.com/bimi/logo.svg",
173
+ "authority": "https://example.com/bimi/vmc.pem",
174
+ "preference": "self",
175
+ "rr": "v=BIMI1; l=https://example.com/bimi/logo.svg; a=https://example.com/bimi/vmc.pem; p=self",
176
+ "info": "bimi=pass header.selector=default header.d=example.com policy.authority=none policy.authority-uri=https://example.com/bimi/vmc.pem"
177
+ }
178
+ ```
179
+
180
+ ### BIMI Skipped (DMARC Failed)
181
+
182
+ ```json
183
+ {
184
+ "status": {
185
+ "result": "skipped",
186
+ "comment": "message failed DMARC",
187
+ "header": {}
188
+ },
189
+ "info": "bimi=skipped (message failed DMARC)"
190
+ }
191
+ ```
192
+
193
+ ### BIMI Skipped (Policy Too Lax)
194
+
195
+ ```json
196
+ {
197
+ "status": {
198
+ "result": "skipped",
199
+ "comment": "too lax DMARC policy",
200
+ "header": {}
201
+ },
202
+ "info": "bimi=skipped (too lax DMARC policy)"
203
+ }
204
+ ```
205
+
206
+ ### BIMI None (No Record)
207
+
208
+ ```json
209
+ {
210
+ "status": {
211
+ "result": "none",
212
+ "header": {
213
+ "selector": "default",
214
+ "d": "example.com"
215
+ }
216
+ },
217
+ "info": "bimi=none header.selector=default header.d=example.com"
218
+ }
219
+ ```
220
+
221
+ ### BIMI Fail (Invalid Record)
222
+
223
+ ```json
224
+ {
225
+ "status": {
226
+ "result": "fail",
227
+ "comment": "missing location value in dns record",
228
+ "header": {
229
+ "selector": "default",
230
+ "d": "example.com"
231
+ }
232
+ },
233
+ "rr": "v=BIMI1;",
234
+ "info": "bimi=fail (missing location value in dns record) header.selector=default header.d=example.com"
235
+ }
236
+ ```
237
+
238
+ ### VMC Validation Result
239
+
240
+ ```json
241
+ {
242
+ "location": {
243
+ "url": "https://example.com/bimi/logo.svg",
244
+ "success": true,
245
+ "logoFile": "PHN2ZyB4bWxucz0i...",
246
+ "hashAlgo": "sha256",
247
+ "hashValue": "abc123..."
248
+ },
249
+ "authority": {
250
+ "url": "https://example.com/bimi/vmc.pem",
251
+ "success": true,
252
+ "domainVerified": true,
253
+ "hashMatch": true,
254
+ "vmc": {
255
+ "type": "VMC",
256
+ "mediaType": "image/svg+xml",
257
+ "logoFile": "PHN2ZyB4bWxucz0i...",
258
+ "hashAlgo": "sha256",
259
+ "hashValue": "abc123...",
260
+ "validHash": true,
261
+ "certificate": {
262
+ "subjectAltName": ["example.com", "*.example.com"]
263
+ }
264
+ }
265
+ },
266
+ "headers": {
267
+ "indicator": "BIMI-Indicator: PHN2ZyB4bWxucz0i...",
268
+ "location": "BIMI-Location: v=BIMI1; l=https://example.com/bimi/logo.svg",
269
+ "preference": "BIMI-Logo-Preference: self"
270
+ }
271
+ }
272
+ ```