# `msg.verify(public_key=None)`

Validates message structure, canonical hash, and signature. Returns `True` on success and raises `MsgValidationError` on failure.

If you need the verified values as data, use `msg.verify_details(public_key=None)`. It performs the same validation and returns a `VerificationDetails` instance describing the fields and checks that passed.

`public_key` is optional. When omitted, `verify()` first validates the PollyWeb
branch at `pw.{From}` with DNSSEC, then fetches the key from DNS using:

```text
{Selector}._domainkey.pw.{From}
```

The TXT record must follow the DKIM wire format:

```text
v=DKIM1; k=<key-type>; p=<base64-encoded public key>
```

The message header may also contain `Algorithm`, which identifies the signature algorithm used for `Signature`. For domain messages, the sender DKIM record remains authoritative, and PollyWeb can infer the algorithm even when the header is absent. If the header is present, PollyWeb requires it to agree with DNS. PollyWeb currently supports:

- `Algorithm=ed25519-sha256` with `k=ed25519`
- `Algorithm=rsa-sha256` with `k=rsa`

If `Algorithm` is omitted, `verify()` keeps legacy compatibility by inferring the algorithm from the verified public key or the sender DKIM key type.

DNSSEC is required. The `pw.{From}` branch validation and the DKIM TXT response
must both have the `AD` flag set.

If `public_key` is passed explicitly, `Selector` is not required because DNS resolution is skipped. To validate only structure and the canonical hash without checking the signature, use [`msg.validate_unsigned()`](validate_unsigned.md).

## Validation order

1. `Schema` must match `pollyweb.org/MSG:1.0`
2. `To` must be a syntactically valid domain string
3. `Correlation` must be a UUID string
4. `Timestamp` must be a UTC timestamp ending in `Z`
5. Required header fields must be non-empty: `From`, `To`, `Subject`, `Correlation`, and `Timestamp`; `Selector` is also required when DNS lookup is needed
6. `Hash` must be present
7. `Signature` must be present
8. `SHA-256(canonical())` must match the stored `Hash`
9. When needed, `pw.{From}` is validated with DNSSEC first
10. The public key is resolved from DNS with DNSSEC enforcement
11. The signature algorithm must match the DKIM key type when DNS is used
12. The signature must verify against `canonical()`

```python
signed.verify(public_key)
signed.verify(pair.PublicKey)
received.verify(db_public_key)
signed.verify()
details = signed.verify_details(public_key)
```

To inspect the sender domain's DNS configuration separately from message validation, use [`DNS.check()`](../dns/check.md).
