# Offline Message Push Test Vectors

[Normative test vectors](README.md) · [Relay–push gateway protocol](../relay-push/README.md)

## Coverage

File: [`push-notifications-v1.json`](../../test-vectors/push-notifications-v1.json). Fixed inputs cover certificate gateway declarations, device identity stability, dual signatures, send conditions, deduplication scope, relay signatures, unknown fields, cross-network isolation, address format, gateway confirmation, device authorization queries, certificate adoption and address updates, gateway deduplication, and HTTP handling boundaries.

These fixed inputs do not replace the network behavior and concurrency tests in the [Push conformance requirements](../relay-push/README.md#compatibility-and-conformance-requirements). Example addresses need not resolve or be accessed.

## Fixed Inputs and References

The root `network_context` is the trusted network context, following the [common execution rules](README.md#file-structure-and-trusted-context); private keys in `keys` are for testing only. `signing_algorithm` indicates that the ECDSA vectors use P-256, SHA-256, and RFC 6979, without additionally converting S to a low value. The protocol still accepts mathematically valid high-S or low-S signatures.

| Section | Fixed inputs and expected results |
|---|---|
| `keys` | Test private keys for the certificate account, device, and relay; relay public key and ID; identity derivation inputs and IDs for the recipient device and group |
| `device_certificates` | Complete certificates with no address, two different addresses, and an unknown signed property; device and account signing inputs, digests, and DeviceId |
| `certificate_tampering` | Independent tampering with complete certificates and expected verification results for both signatures |
| `notifications` | Complete notifications, including omitted associated-object information, a custom message type with a nongroup object, and an unknown signed property; canonical bytes, signing inputs, and request sizes |
| `deduplication_keys` | Whether a notification remains under the same deduplication key after changing notification fields or the trusted network; does not judge the validity of the complete modified notification |
| `signature_tampering` | Expected relay signature verification results after changing fields or the trusted network while retaining the original signature |
| `gateway_urls` | Address format acceptance and rejection cases, including queries and the 512-byte boundary; no DNS or network connections |
| `notification_eligibility` | Push conditions for the original message and target device |
| `gateway_confirmation` | `OPTIONS` request construction, confirmation response evaluation, and binding of confirmation results to complete URLs |
| `device_status` | Authorization status and certificate expiry to return for a given current certificate and authorization state; gateway declarations do not affect the result |
| `gateway_state` | Gateway addresses after certificate adoption, authentication failure, session termination, restart, and authorization changes |
| `deduplication` | Gateway acceptance, skipping, duplicate requests, and unavailable deduplication processing |
| `notification_responses` | Relay handling categories for `POST` notification responses |
| `connection_targets` | Allow or reject decisions for a given actual connection IP; passing address format checks does not replace this check |

Expand only the surrounding same-file JSON Pointers listed below. During expansion, remove the `_ref` suffix and obtain the corresponding complete object. Unknown properties inside referenced objects and signing inputs are not test instructions.

| Section | Reference field | Target |
|---|---|---|
| `certificate_tampering`, `device_status`, `gateway_state[].steps[]` | `certificate_ref` | Complete `object` in `device_certificates` |
| `deduplication_keys`, `signature_tampering` | `notification_ref` | Complete `object` in `notifications` |

`set` and `remove` are test operations: make a deep copy of the original object, first delete the root fields listed in `remove`, then add or replace the root fields given by `set`. Execute each case independently without changing the shared original value; these operations are not part of the wire protocol. When present, `candidate_network_context` replaces only the trusted context used for signature verification or deduplication in that case; it does not automatically rewrite the wire `network` field.

`gateway_confirmation.responses` and `notification_responses` use the same `response` structure: `http_status` is the status code, `headers` contains the response headers, and `body` is the raw body text (omission means an empty body); `response = null` means no response was obtained.

## Execution Steps and Assertions

### Identity, Certificates, and Status Queries

1. Independently generate the corresponding public keys from the private keys in `keys`; derive `relay_id` from its public key under the relay identity rules. Reconstruct the IDs from `recipient_device_identity` and `group_identity`, and check the results.
2. For each certificate, exclude root `device_signature` and `account_signature`, inject trusted `$context`, compare the Canonical JSON, `utf8_hex`, and `sha256` in `device_signing_input`, and verify the device signature. Retain the device signature and exclude only the root account signature, reconstruct and compare `account_signing_input`, and verify the account signature and account binding.
3. Independently construct `meshline.device.identity`; gateway addresses and unknown properties MUST NOT be copied into the identity input. Every certificate MUST produce the same given `device_id`.
4. Apply the test operations in `certificate_tampering`, retain the original signatures, and verify both signatures separately; match the expected failure results for each case. Format validity or an unchanged DeviceId cannot substitute for signature verification.
5. The `device_status` cases treat the specified certificate as the certificate in the current authoritative state. `active` directly supplies the device's current authorization decision; do not reinterpret these test preconditions using the executing machine's current time. Project `status` and conditional `expires_at`, comparing field values and presence against `expected`; gateway declarations in certificates do not enter the query result.

The existing certificate vectors without a gateway in `identity-auth-v1.json` remain unchanged and continue to be verified under their original execution guide.

### Certificate Adoption and Address Updates

Each `gateway_state` scenario starts independently, with the relay having no gateway address for the target device. All referenced certificates belong to the same device ID under the same account. Execute `steps` in order and compare the current record with `expected_gateway` after each step; `null` means no address is available.

| `event` | Input and processing |
|---|---|
| `certificate_adopted` | The certificate referenced by `certificate_ref` has been obtained and verified; this step adopts its gateway declaration, updating the address when `push_gateway` is present and clearing it when omitted |
| `device_authentication_failed` | Device authentication carrying `certificate_ref` failed, and that certificate was not adopted; the address record remains unchanged |
| `session_ended` | `reason` gives the reason the session ended; the address record remains unchanged |
| `relay_restarted` | `gateway_state_available` specifies whether the previous address remains available after restart; when unavailable, the address record is empty |
| `authorization_checked` | `status = inactive` clears the address; `active` or `unavailable` does not modify the record, with the latter meaning that no usable authorization result was obtained |
| `historical_certificate_read` | A historical certificate is read but has not been verified again and adopted; the address record remains unchanged |

Certificate adoption order is determined by step order, not by certificate age. Address availability in restart cases is a test precondition and does not prescribe a storage method. Authorization checks before sending are performed separately under [Send target validation](../relay-push/README.md#send-target-validation).

### Notification Fields and Relay Signatures

Every notification in `notifications` MUST pass field validation, including cases with a `message_type` that is not predefined and cases omitting `subject_id` and `subject_name`. `message_type` MUST be a nonempty string but MUST NOT be restricted to a known enumeration. Group message examples use the group as the associated object; the custom message example uses `example-subject-1` as `subject_id`. Associated-object identifiers MUST NOT all be validated as group IDs.

1. Exclude only root `relay_signature` from the complete notification, retain all other fields and unknown properties, inject the trusted network context, reconstruct `signing_input`, and compare the canonical bytes, digest, and relay signature. When generating signatures using the algorithm described in [Fixed Inputs and References](#fixed-inputs-and-references), signature bytes SHOULD be compared exactly; other implementations compare inputs and verify signatures under the common rules.
2. Generate Canonical JSON for the complete notification without adding whitespace. Its UTF-8 length MUST equal `request_utf8_bytes`. This value describes only that fixed request representation; the HTTP 4 KiB limit still includes whitespace in the actual transmitted representation.
3. `signature_tampering` tests only cryptographic coverage: after applying the operations, verify with the original public key and signature, matching `expected_signature_valid`. Replacing the candidate network MUST also cause verification to fail. The complete request's structure, network, and target binding checks MUST still be performed separately.

### Address Validation

Validate each address in `gateway_urls`, comparing `expected_format_valid`; input MUST NOT be rewritten before validation. Private or loopback IPs can satisfy URL syntax but cannot pass the actual connection security checks.

The `address` in `connection_targets` is the already determined actual destination IP. Apply the public-destination restrictions and compare `expected_allowed`. These static classifications do not test DNS rebinding, proxies, connection pools, TLS host verification, or redirect prevention; independent network behavior tests MUST cover those.

### Send Conditions and Gateway Confirmation

In `notification_eligibility`, `envelope_fields` projects fields from an accepted envelope; compare `expected_allows_push` under [Send target validation](../relay-push/README.md#send-target-validation). `other_requirements_met` indicates whether all other push conditions are met. `expected_eligible` is `true` only when both are `true`; it indicates push eligibility and does not require an actual send.

`gateway_confirmation` contains three independent groups for request construction, response evaluation, and address binding, executed under the [Gateway confirmation](../relay-push/README.md#gateway-confirmation) rules:

- `requests`: construct a request from `push_gateway` and compare its method, complete URL, and request body UTF-8 byte count with `expected_request`.
- `responses`: determine the confirmation result from `response` and compare it with `expected_confirmed`; when `expected_follow_redirect` is present, also verify that the redirect is not followed.
- `bindings`: `confirmed_url` is the complete URL that was successfully confirmed; `null` means no confirmation yet. Compare it character for character with the current `push_gateway`, matching `expected_applies`. This assertion determines only whether the confirmation result applies to the current address; it does not replace other send target validation.

Actual interaction tests MUST also verify that no notification is sent when confirmation fails, that a changed address is confirmed before use, and that both confirmation and notification requests follow callback connection security requirements.

### Gateway Deduplication and Responses

`deduplication_keys` verifies scope isolation under [Gateway acceptance and deduplication](../relay-push/README.md#gateway-acceptance-and-deduplication): after applying field operations and the candidate network context, compare the deduplication key with that of the original notification under the root `network_context`, matching `expected_same_key`. The comparison covers the network, `to`, `to_device_id`, `from`, and `message_id`; no hash derivation is needed. Some cases deliberately violate other field constraints and test only deduplication scope; they do not imply that the complete modified notification can be accepted.

Each `deduplication` scenario starts with empty deduplication state. Integers in `requests` are zero-based indexes into `notifications`; `deduplication_available` specifies whether deduplication processing is available.

The fixed notifications have passed authentication checks, and all requests in a scenario fall within the same deduplication validity period. Process `requests` in order: return `503` if deduplication processing is unavailable; return `duplicate` if a record with the same key already exists; otherwise use `initial_decision` and record the result. Only `accepted` triggers new processing.

Compare `expected_statuses` (strings are the `status` of successful results; integers are HTTP error codes), and compare the number of newly triggered processing operations with `expected_new_tasks`.

For `notification_responses`, process `response` under [Successful responses](../relay-push/README.md#successful-responses) and [Errors and retries](../relay-push/README.md#errors-and-retries), and compare `expected_action`: `stop` means stop retrying this notification; `unknown` means no success confirmation was obtained and retrying is allowed; `optional_retry` means retrying is allowed but not required; `do_not_follow` means sending to a redirect destination is prohibited. `minimum_delay_seconds` gives only the minimum wait required by `Retry-After`; actual retries MUST also satisfy send target validation.

The three successful statuses confirm only that the gateway has accepted or skipped the notification; they do not establish successful platform submission or actual device delivery.
