Skip to main content
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:
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

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

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.
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.

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

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.

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:
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.
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.

Security Coverage Summary

  • Configuration — how to set NEXUS_SIGNING_KEY and the production checklist
  • Packet Structuresig, timestamp, and nonce field formats for both directions
  • Data Addon APIadditionalValidators(), registerHandler(), and the full DataAddon method reference
  • Request Lifecycle — where the security chain fits in the full request flow