> ## Documentation Index
> Fetch the complete documentation index at: https://nexus-core.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Packet Security and Replay Protection in Nexus Core

> Nexus Core signs both inbound and outbound Redis packets using HMAC-SHA256, a 5-minute timestamp window, and nonce-based replay protection via a composable MessageValidationChain.

Nexus Core implements **symmetric message authentication**: every packet received is validated, and every packet sent is signed using the same `NEXUS_SIGNING_KEY`. As of v1.6.1, the validation logic is structured as a composable `MessageValidationChain` that is shared globally for inbound messages and also available per-addon for custom rules.

## How It Works: MessageValidationChain

Inbound validation is handled by `MessageValidationChain`, a pipeline of `MessageValidator` instances. Each validator receives the parsed `NexusJsonDataContainer` and returns a `ValidationResult` indicating success or a rejection reason. Processing stops immediately if any validator rejects the message.

`NexusReceiver` builds a **global chain** applied to every inbound packet:

```java theme={null}
new MessageValidationChain(List.of(
    new SignatureValidator(),
    new TimestampValidator(),
    new NonceValidator()
))
```

Starting from v1.6.1, each `DataAddon` can also define an **additional per-addon chain** via `additionalValidators()`. This chain runs after the global chain passes, before any request handler executes.

### MessageValidator Interface

```java theme={null}
@FunctionalInterface
public interface MessageValidator {
    ValidationResult validate(NexusJsonDataContainer message);
}
```

`ValidationResult` is a record with two fields: `valid` (boolean) and `reason` (String). Use the static factories:

```java theme={null}
ValidationResult.ok()              // passes
ValidationResult.reject("reason")  // fails and logs the reason
```

## Global Validation: Three Stages

Every inbound packet passes through these three validators in order. A failure at any stage silently drops the packet and logs a warning. No response is sent.

### Stage 1: HMAC-SHA256 Signature (`SignatureValidator`)

Every packet includes a `sig` field: a Base64-encoded HMAC-SHA256 signature over all other fields in the packet (excluding `sig` itself), computed using the shared `NEXUS_SIGNING_KEY`.

When a packet arrives, `SignatureValidator`:

1. Reads all fields except `sig`.
2. Recomputes HMAC-SHA256 using `HmacSigner.sign(payloadWithoutSig, NEXUS_SIGNING_KEY)`.
3. Compares the result against the received `sig`.
4. Rejects the packet if they do not match.

### Stage 2: Timestamp (`TimestampValidator`)

Every packet must include a `timestamp` field (Unix milliseconds). `TimestampValidator` calculates `|now - timestamp|`. If the difference exceeds **5 minutes** (`TIMESTAMP_WINDOW_MILLIS`), the packet is rejected.

<Tip>
  Keep all server clocks synchronized with NTP. Clock drift beyond 5 minutes causes valid packets to be rejected even when the signature and nonce are correct.
</Tip>

### Stage 3: Nonce Replay Protection (`NonceValidator`)

Every packet must include a unique `nonce` string. `NonceValidator` records each nonce in a `ConcurrentHashMap` keyed by nonce value with the packet timestamp. A second packet with the same nonce is rejected immediately as a replay attempt.

Expired nonce entries are purged automatically every `TIMESTAMP_WINDOW_MILLIS` by a background daemon thread, keeping memory usage bounded.

## Validation Order

```text theme={null}
1. sig       — Exists? HMAC verification passes?
2. timestamp — Exists? Within 5-minute window?
3. nonce     — Exists? Not seen before?
               └──► Global chain passed. Per-addon chain runs next.
4. addon.additionalValidators() — All pass?
5. addon.handleRequest()        — Returns true?
               └──► dispatch() runs the registered RequestHandler.
```

## Per-Addon Validation (New in v1.6.1)

Override `additionalValidators()` in your `DataAddon` to enforce rules specific to your addon's data. The returned validators are assembled into an immutable `MessageValidationChain` once (lazily, thread-safely) and reused for every request that addon receives.

```java theme={null}
@Override
protected List<MessageValidator> additionalValidators() {
    return List.of(
        message -> message.containsKey("playerUuid")
            ? ValidationResult.ok()
            : ValidationResult.reject("playerUuid field is required"),

        message -> {
            String uuid = message.get("playerUuid", String.class);
            return uuid != null && uuid.length() == 36
                ? ValidationResult.ok()
                : ValidationResult.reject("playerUuid must be a valid UUID string");
        }
    );
}
```

## Outbound Signing

Starting from v1.5.1, the `MessageAuth` system signs every outgoing message from Nexus Core, adding `timestamp`, `nonce`, and `sig` fields to all responses. This applies to:

* All `DataAddon` publish operations (query responses, broadcasts)
* `RedisDataContainer` heartbeat messages

Spigot-side clients can verify the `sig` on incoming responses using the same `NEXUS_SIGNING_KEY`. Clients that do not yet verify outbound signatures continue to work — backward compatibility is maintained.

## Configuring the Signing Key

Set the environment variable before launching Nexus Core and on every Spigot server in the network:

```bash theme={null}
export NEXUS_SIGNING_KEY="a-long-and-unpredictable-secret-key"
```

<Warning>
  `NEXUS_SIGNING_KEY` must be identical on Nexus Core and every connected Spigot server. Never commit it to version control. Rotate it immediately if it is exposed.
</Warning>

<Note>
  If `NEXUS_SIGNING_KEY` is not defined, both inbound verification and outbound signing are disabled. A one-time warning is logged. This is acceptable only for local development.
</Note>

## Security Coverage Summary

| Direction                           | Since  | Mechanism                                                                              |
| ----------------------------------- | ------ | -------------------------------------------------------------------------------------- |
| Spigot → Nexus (inbound, global)    | v1.5   | `MessageValidationChain`: `SignatureValidator`, `TimestampValidator`, `NonceValidator` |
| Spigot → Nexus (inbound, per-addon) | v1.6.1 | `DataAddon.additionalValidators()`                                                     |
| Nexus → Spigot (outbound)           | v1.5.1 | `MessageAuth` with HMAC-SHA256, timestamp, nonce                                       |

## Related Topics

* [Configuration](/configuration) — how to set `NEXUS_SIGNING_KEY` and the production checklist
* [Packet Structure](/reference/packet-structure) — `sig`, `timestamp`, and `nonce` field formats for both directions
* [Data Addon API](/addons/data-addon-api) — `additionalValidators()`, `registerHandler()`, and the full `DataAddon` method reference
* [Request Lifecycle](/reference/request-lifecycle) — where the security chain fits in the full request flow


## Related topics

- [Nexus Core Changelog](/reference/changelog.md)
- [Request Lifecycle in Nexus Core](/reference/request-lifecycle.md)
- [System Architecture of Nexus Core](/concepts/architecture.md)
- [Configuring Nexus Core](/configuration.md)
- [Best Practices for Building Nexus Core Addons](/addons/best-practices.md)
