Email Believability Index (EBI) Specification — v1.3

Status: Draft

Last Updated: 2026-01-11

Primary Goal: Provide a machine-verifiable, auditable, and actionable summary of email authentication and integrity signals (DKIM / SPF / DMARC / ARC), including security posture (vulnerabilities) — not just “pass/fail.”

EBI is designed to be:


1. Overview

1.1 What is EBI?

EBI (Email Believability Index) is a structured JSON response that summarizes the authenticity and integrity of an email message by analyzing:

EBI deliberately surfaces edge cases and vulnerabilities (e.g., weak DKIM canonicalization, partial body signing, relaxed alignment), allowing downstream systems to assess risk beyond basic “pass/fail.”

1.2 Analogy

EBI is analogous to OCSP for email authentication: a standardized response format that conveys a verdict and supporting evidence.

1.3 Non-goals

1.4 Static .eml Limitations

When analyzing static .eml files (as opposed to live SMTP sessions), certain authentication mechanisms cannot be verified directly:


2. Response Schema

2.1 Top-level Object

An EBI response MUST be a JSON object with the following structure.

{
  "ebi_version": "1.3",
  "request_id": "string",
  "timestamp": "RFC3339 string",
  "message_id": "string | null",

  "subject": "string | null",
  "from": "string | null",
  "to": ["string"],

  "verdict": { /* VerdictObject */ },
  "score": { /* ScoreObject */ },

  "dkim": { /* DKIMObject */ },
  "spf": { /* SPFObject */ },
  "dmarc": { /* DMARCObject */ },
  "arc": { /* ARCObject | null */ },

  "findings": [ /* FindingObject[] */ ],

  "metadata": { /* MetadataObject */ }
}

2.2 Field Reference

Field Type Required Description
ebi_version string Yes Spec version. MUST be "1.3" for this spec.
request_id string Yes A unique ID for correlation across systems.
timestamp string Yes RFC3339 timestamp when analysis was completed.
message_id string | null No Message-ID header from the evaluated email.
subject string | null No Subject line (optional).
from string | null No Header From (optional).
to string[] No Header To list (optional).
verdict object Yes High-level believability verdict and explanation.
score object Yes* Numeric score derived from verdict + findings. Required unless analysis is impossible.
dkim object Yes DKIM authentication results and signature details.
spf object Yes SPF authentication results and evaluation details.
dmarc object Yes DMARC evaluation results and alignment checks.
arc object | null No ARC chain validation (null if no ARC headers present).
findings FindingObject[] Yes List of vulnerabilities, anomalies, and noteworthy conditions.
metadata object Yes Context for the analysis environment and optional raw evidence references.

* score MAY be null only when the system cannot compute any reliable verdict (e.g., missing message headers, parser failure). In that case verdict.status SHOULD be INCONCLUSIVE.


3. Verdict Object

3.1 VerdictObject Schema

{
  "status": "AUTHENTIC" | "PARTIAL" | "FAILED" | "UNSAFE" | "INCONCLUSIVE",
  "confidence": "HIGH" | "MEDIUM" | "LOW",
  "code": "string",
  "summary": "string",
  "explanation": "string | null",
  "flags": ["string"]
}

3.2 status Semantics

Status Meaning
AUTHENTIC Strong authentication and integrity; no high-risk weaknesses detected.
PARTIAL Some authentication succeeded but with missing signals or weak alignment.
FAILED Authentication failed or indicates possible spoofing.
UNSAFE Authentication passed but vulnerabilities or configuration weaknesses allow tampering or misattribution.
INCONCLUSIVE Not enough information to determine authenticity.

3.3 confidence

Confidence reflects the reliability of the verdict given available evidence.

Confidence When Used
HIGH All expected signals present and consistent
MEDIUM Some signals present but not all, or weak crypto used
LOW Unable to make a confident determination

3.4 Verdict Codes

The code field provides machine-readable detail about why a particular verdict was reached.

Code Status Description
ALL_PASS AUTHENTIC DKIM, SPF, and DMARC all passed
DKIM_PARTIAL_BODY_SIGNED UNSAFE DKIM l= tag present; body can be extended
DKIM_SIGNATURE_EXPIRED FAILED DKIM signature x= timestamp has passed
NO_AUTH_MECHANISMS FAILED No DKIM signatures present and no authentication passed
DMARC_FAIL FAILED DMARC alignment check failed
ALL_AUTH_FAIL FAILED Had signatures but all authentication mechanisms failed
WEAK_CRYPTO PARTIAL DKIM passed but uses deprecated SHA-1
DKIM_ONLY PARTIAL Only DKIM passed; SPF/DMARC absent or failed
SPF_ONLY PARTIAL Only SPF passed; DKIM/DMARC absent or failed
UNKNOWN INCONCLUSIVE Unable to determine authenticity

3.5 Verdict Determination Priority

Implementations MUST evaluate verdict conditions in the following priority order (first match wins):

  1. UNSAFE — If any DKIM signature has body_length.limited = true (l= tag present)
  2. FAILED — If any DKIM signature has expired (finding DKIM_SIGNATURE_EXPIRED present) AND DKIM was NOT verified via ARC or Authentication-Results (see §3.6)
  3. FAILED — If no DKIM signatures AND neither DKIM nor SPF passed (NO_AUTH_MECHANISMS)
  4. FAILED — If DMARC result is FAIL
  5. FAILED — If neither DKIM nor SPF passed but signatures were present (ALL_AUTH_FAIL)
  6. PARTIAL — If DKIM passed but uses weak cryptography (SHA-1), confidence=MEDIUM
  7. AUTHENTIC — If DKIM AND SPF AND DMARC all passed
  8. PARTIAL — If DKIM OR SPF passed (but not all three), confidence=MEDIUM
  9. INCONCLUSIVE — Otherwise, confidence=LOW

3.6 Expired Signatures with ARC/Authentication-Results

When DKIM authentication is derived from a trusted source (ARC chain or Authentication-Results header), an expired signature SHOULD NOT force a FAILED verdict. The rationale:

In this case, the DKIM_SIGNATURE_EXPIRED finding remains (severity HIGH) as evidence, but verdict determination continues to the next priority level.


4. Score Object

4.1 Purpose

The score provides a single numeric projection of believability for UI display and policy thresholding.

Important: the score is not a replacement for the richer EBI structure. It is derived from the verdict and findings.

4.2 ScoreObject Schema

{
  "value": 0,
  "scale": { "min": 0, "max": 100 },
  "band": "EXCELLENT" | "GOOD" | "CAUTION" | "DANGEROUS" | "UNKNOWN",
  "method": "EBI_SCORE_V1",
  "components": {
    "base": 0,
    "finding_penalty": 0,
    "confidence_adjustment": 0
  },
  "notes": "string | null"
}

4.3 Bands

Band Range
EXCELLENT 90–100
GOOD 75–89
CAUTION 55–74
DANGEROUS 0–54
UNKNOWN Used only when score is null.

4.4 Scoring Method: EBI_SCORE_V1

Step A — Base score from verdict.status

verdict.status base
AUTHENTIC 95
UNSAFE 75
PARTIAL 65
INCONCLUSIVE 50
FAILED 10

Step B — Finding penalties

For each element in findings, subtract:

severity penalty
CRITICAL -30
HIGH -15
MEDIUM -7
LOW -3
INFO 0

Total finding penalty MUST be capped at -60.

Step C — Confidence adjustment

verdict.confidence adjustment
HIGH 0
MEDIUM -5
LOW -12

Step D — Clamp

score = clamp(base + finding_penalty + confidence_adjustment, 0, 100)

Step E — Band mapping

Band is determined from the ranges in §4.3.


5. Findings

5.1 FindingObject Schema

{
  "id": "string",
  "severity": "CRITICAL" | "HIGH" | "MEDIUM" | "LOW" | "INFO",
  "title": "string",
  "summary": "string",
  "details": "string | null",
  "evidence": {
    "type": "HEADER" | "DNS" | "DERIVED" | "BODY" | "OTHER",
    "key": "string | null",
    "value": "string | null"
  },
  "recommendation": "string | null"
}

5.2 Finding IDs

Finding IDs are stable strings suitable for rule-based policy. See §13 for the complete registry.


6. DKIM

6.1 DKIMObject Schema

{
  "result": "PASS" | "FAIL" | "NONE" | "TEMPERROR" | "PERMERROR",
  "from_domain_match": true,
  "domain": "string | null",
  "selector": "string | null",
  "signatures": [
    {
      "domain": "string",
      "selector": "string",
      "result": "PASS" | "FAIL" | "TEMPERROR" | "PERMERROR",
      "canonicalization": {
        "header": "simple" | "relaxed" | "unknown",
        "body": "simple" | "relaxed" | "unknown"
      },
      "body_length": {
        "limited": false,
        "value": null
      },
      "timestamp": "RFC3339 string | null",
      "hash_algo": "rsa-sha256" | "rsa-sha1" | "ed25519-sha256" | "unknown",
      "signed_headers": ["string"]
    }
  ]
}

6.2 Field Semantics

result

The effective DKIM result. This may be derived from (in priority order):

  1. Direct verification — Cryptographic verification of DKIM-Signature headers in the message
  2. Authentication-Results header — If direct verification fails but the message contains an Authentication-Results header showing dkim=pass (see §6.4)
  3. ARC chain — If no direct DKIM signatures but a valid ARC chain exists with dkim=pass at the origin (i=1)

When the result comes from Authentication-Results, metadata.raw.evidence_refs SHOULD include "Authentication-Results". When the result comes from ARC, metadata.raw.evidence_refs SHOULD include "ARC-Authentication-Results".

from_domain_match

Indicates whether the DKIM signing domain (d=) matches or is a parent of the From header domain. This is a DKIM-level observation independent of DMARC evaluation.

body_length.limited

Indicates whether the DKIM signature uses the l= tag to limit body signing.

6.3 Guidance

6.4 Authentication-Results Fallback (RFC 8601)

When analyzing forwarded emails (e.g., emails forwarded as attachments), DKIM signatures may fail re-verification due to transit modifications such as:

However, the original receiving server verified DKIM successfully at the time of receipt and recorded this in the Authentication-Results header (defined by RFC 8601).

Implementations SHOULD trust Authentication-Results when ALL of the following conditions are met:

  1. The message contains DKIM-Signature headers (signatures were present)
  2. Direct cryptographic verification fails or returns neutral
  3. An Authentication-Results header exists with dkim=pass
  4. The passing DKIM domain in Authentication-Results matches a signature’s d= domain

When Authentication-Results is used: - A finding with ID DKIM_VIA_AUTH_RESULTS (severity INFO) SHOULD be added - metadata.raw.evidence_refs SHOULD include "Authentication-Results"

Security note: The Authentication-Results header represents a trusted assertion from the original receiving mail server. This is comparable to trusting ARC chains — both rely on the integrity of headers from upstream servers.


7. SPF

7.1 SPFObject Schema

{
  "result": "PASS" | "FAIL" | "SOFTFAIL" | "NEUTRAL" | "NONE" | "TEMPERROR" | "PERMERROR",
  "domain": "string | null",
  "mail_from": "string | null",
  "helo": "string | null",
  "ip": "string | null",
  "explanation": "string | null",
  "dns_lookups": 0
}

7.2 Static .eml Limitations

For static .eml files, SPF cannot be verified directly because:

In this case: - result SHOULD be "NONE" unless inferred from a valid ARC chain - A finding with ID SPF_NOT_VERIFIABLE (severity INFO) SHOULD be added

7.3 SPF from ARC or Authentication-Results

If a valid ARC chain exists (see §9) and the ARC-Authentication-Results at instance i=1 contains SPF results, those MAY be used as the effective SPF result.

Alternatively, if the message contains an Authentication-Results header with SPF results (see §6.4 for trust model), those MAY be used.

The explanation field SHOULD note "From ARC chain" or "From Authentication-Results header" as appropriate.

7.4 Guidance


8. DMARC

8.1 DMARCObject Schema

{
  "result": "PASS" | "FAIL" | "NONE" | "TEMPERROR" | "PERMERROR",
  "policy": "none" | "quarantine" | "reject" | "unknown",
  "pct": 100,
  "alignment": {
    "dkim": true,
    "spf": true,
    "mode": "relaxed" | "strict" | "unknown"
  },
  "domain": "string | null",
  "rua": ["string"],
  "ruf": ["string"],
  "explanation": "string | null"
}

8.2 DMARC Result Derivation

The effective DMARC result may come from multiple sources (in priority order):

  1. Authentication-Results header — If message contains Authentication-Results with dmarc= result (see §6.4 for trust model)
  2. ARC chain — If valid ARC chain exists with dmarc=pass at origin (i=1)
  3. Direct DMARC check — Via DNS lookup of _dmarc.<from-domain>
  4. Implicit pass — If DKIM alignment passes (signing domain matches From domain) and DKIM passes, DMARC is considered to pass even without explicit DMARC record

When the result comes from Authentication-Results, the explanation field SHOULD note "Determined from Authentication-Results header".

8.3 Alignment for Static .eml

For static .eml files: - alignment.dkim — Can be determined (signing domain vs From domain) - alignment.spf — SHOULD be false or null (cannot verify SPF alignment without envelope)


9. ARC (Authenticated Received Chain)

9.1 Overview

ARC provides a mechanism to preserve authentication results across mail forwarding. When an email is forwarded, the original DKIM signature may break, but ARC headers record what authentication looked like at each hop.

EBI uses ARC to recover original authentication results when direct verification is not possible.

9.2 ARCObject Schema

{
  "result": "PASS" | "FAIL" | "NONE" | "TEMPERROR" | "PERMERROR",
  "chain_valid": true,
  "instances": [
    {
      "i": 1,
      "cv": "pass" | "fail" | "none" | "unknown",
      "auth_results": "string | null",
      "signing_domain": "string | null"
    }
  ]
}

9.3 Field Semantics

chain_valid

Indicates whether the ARC chain is trustworthy:

CRITICAL: Authentication results from ARC MUST NOT be trusted if chain_valid is false.

instances

Array of ARC set instances, sorted by i value (ascending). Each instance represents a hop in the forwarding chain.

9.4 Trusting ARC Results

When chain_valid is true, the authentication results from instance i=1 (the origin) MAY be used as effective results:

IF arc.chain_valid = true AND arc.instances[0].i = 1:
  effectiveDkim = parseResult(arc.instances[0].auth_results, "dkim")
  effectiveSpf = parseResult(arc.instances[0].auth_results, "spf")  
  effectiveDmarc = parseResult(arc.instances[0].auth_results, "dmarc")

9.5 ARC Parsing

ARC headers are parsed from:

  1. ARC-Seal — Contains i=, cv=, d= (signing domain)
  2. ARC-Authentication-Results — Contains original auth results at each hop

Example ARC-Authentication-Results:

ARC-Authentication-Results: i=1; mx.google.com;
       dkim=pass header.d=example.com;
       spf=pass smtp.mailfrom=example.com;
       dmarc=pass header.from=example.com

10. Metadata

10.1 MetadataObject Schema

{
  "source": {
    "system": "string",
    "version": "string | null"
  },
  "analysis": {
    "mode": "LIVE" | "BATCH" | "TEST",
    "elapsed_ms": 0
  },
  "raw": {
    "header_hash": "string | null",
    "body_hash": "string | null",
    "evidence_refs": ["string"]
  }
}

10.2 Guidance


11. Implementation Requirements

11.1 Determinism

Given identical inputs, an implementation MUST produce identical EBI outputs (except for timestamp and request_id).

11.2 Evidence-first

Every non-trivial verdict decision SHOULD be supported by either:

11.3 ARC Chain Validation

Implementations MUST:

11.4 Backward Compatibility


12. Example Response

This example demonstrates a forwarded email where original DKIM verification happens via ARC chain.

{
  "ebi_version": "1.3",
  "request_id": "req_a1b2c3d4",
  "timestamp": "2026-01-11T10:30:00Z",
  "message_id": "<forwarded-msg@example.com>",

  "subject": "Important Document",
  "from": "sender@example.com",
  "to": ["recipient@company.com"],

  "verdict": {
    "status": "AUTHENTIC",
    "confidence": "HIGH",
    "code": "ALL_PASS",
    "summary": "All authentication mechanisms passed.",
    "explanation": "Email appears genuine.",
    "flags": []
  },

  "score": {
    "value": 85,
    "scale": {"min": 0, "max": 100},
    "band": "GOOD",
    "method": "EBI_SCORE_V1",
    "components": {
      "base": 95,
      "finding_penalty": -10,
      "confidence_adjustment": 0
    },
    "notes": "AUTHENTIC + 2 findings"
  },

  "dkim": {
    "result": "PASS",
    "from_domain_match": true,
    "domain": "example.com",
    "selector": null,
    "signatures": []
  },

  "spf": {
    "result": "PASS",
    "domain": "example.com",
    "mail_from": null,
    "helo": null,
    "ip": null,
    "explanation": "From ARC chain",
    "dns_lookups": 3
  },

  "dmarc": {
    "result": "PASS",
    "policy": "quarantine",
    "pct": null,
    "alignment": {"dkim": true, "spf": false, "mode": "relaxed"},
    "domain": "example.com",
    "rua": [],
    "ruf": [],
    "explanation": "Determined from ARC authentication chain"
  },

  "arc": {
    "result": "PASS",
    "chain_valid": true,
    "instances": [
      {
        "i": 1,
        "cv": "none",
        "auth_results": "dkim=pass spf=pass dmarc=pass",
        "signing_domain": "forwarder.com"
      },
      {
        "i": 2,
        "cv": "pass",
        "auth_results": "dkim=none spf=none dmarc=none",
        "signing_domain": "mx.company.com"
      }
    ]
  },

  "findings": [
    {
      "id": "DKIM_VIA_ARC",
      "severity": "INFO",
      "title": "DKIM verified via ARC chain",
      "summary": "Original DKIM signature was verified by forwarder.com.",
      "details": "The email was forwarded and the original DKIM signature was replaced with an ARC chain.",
      "evidence": {"type": "HEADER", "key": "ARC-Authentication-Results", "value": "dkim=pass"},
      "recommendation": "ARC provides trustworthy authentication for forwarded emails."
    },
    {
      "id": "DKIM_RELAXED_BODY_CANON",
      "severity": "MEDIUM",
      "title": "Relaxed body canonicalization",
      "summary": "Relaxed canonicalization permits whitespace modifications without invalidating signature.",
      "details": null,
      "evidence": {"type": "DERIVED", "key": "dkim.canonicalization.body", "value": "relaxed"},
      "recommendation": "Consider simple canonicalization for high-integrity messages."
    }
  ],

  "metadata": {
    "source": {"system": "ebi-service", "version": "1.2.0"},
    "analysis": {"mode": "LIVE", "elapsed_ms": 156},
    "raw": {
      "header_hash": null,
      "body_hash": null,
      "evidence_refs": ["ARC-Authentication-Results"]
    }
  }
}

13. Finding ID Registry

This section is normative. Implementations SHOULD use these exact IDs.

13.1 DKIM Findings

ID Severity Condition
DKIM_PARTIAL_BODY_SIGNED CRITICAL DKIM l= tag present; body can be appended
DKIM_RELAXED_BODY_CANON MEDIUM DKIM uses c=.../relaxed for body
DKIM_RELAXED_HEADER_CANON LOW DKIM uses c=relaxed/... for headers
DKIM_WEAK_HASH_ALGO HIGH DKIM uses rsa-sha1
DKIM_MISSING_FROM_HEADER HIGH From header not in signed headers (h=)
DKIM_MISSING_SUBJECT_HEADER MEDIUM Subject header not in signed headers
DKIM_THIRD_PARTY_SIGNATURE INFO Signature domain differs from From domain
DKIM_SIGNATURE_EXPIRED HIGH Signature x= timestamp has passed
DKIM_VIA_ARC INFO DKIM result inferred from valid ARC chain
DKIM_VIA_AUTH_RESULTS INFO DKIM result inferred from Authentication-Results header (RFC 8601)

13.2 SPF Findings

ID Severity Condition
SPF_NEUTRAL LOW SPF returned neutral
SPF_SOFTFAIL MEDIUM SPF returned softfail
SPF_NOT_VERIFIABLE INFO Static .eml lacks envelope context for SPF

13.3 DMARC Findings

ID Severity Condition
DMARC_POLICY_NONE LOW DMARC policy is p=none
DMARC_FAIL HIGH DMARC evaluation failed

13.4 ARC Findings

ID Severity Condition
ARC_CHAIN_FAIL MEDIUM ARC chain validation failed (cv=fail)

14. Changelog

v1.3

v1.2

v1.1

v1.0