Creator documentation

Verifying a request

Two checks: a header comparison that is enough, and a signature that is worth doing.

Sections

Two checks, and you want both. The first is a header comparison you can write in a line. The second is not redundancy — it is what makes the first one survive a leak, and it is the only thing that stops a captured request being replayed at your endpoint or at somebody else’s.

The creator key#

Every request carries your key. Compare it in constant time against the value you stored.

Pythonpython
import hmac, os
EXPECTED = os.environ["NEXUS_CREATOR_KEY"].encode()

def authorized(headers) -> bool:
    presented = headers.get("X-Nexus-Creator-Key", "").encode()
    return hmac.compare_digest(presented, EXPECTED)

The Ed25519 signature#

The creator key is a shared secret sitting on your server, in an environment variable, possibly in a log. If it leaks, anyone can impersonate Nexus402 to you and take free tool calls. The signature is asymmetric: you hold only a public key, so there is nothing on your side worth stealing.

Three headers carry it:

Signature headerstext
X-Nexus-Signature: ed25519:<base64 of 64 bytes>
X-Nexus-Timestamp: <unix seconds>
X-Correlation-ID:  <uuid>

The signed message is exactly six fields joined by single newlines:

The canonical stringtext
<method>\n<host>\n<path and query>\n<timestamp>\n<correlation id>\n<lowercase hex sha256 of the raw body>

The host, and the mistake everyone makes#

Why is the host signed at all? Because every creator verifies with the same platform key today. Without the host, a request we signed for one creator would also verify at another creator's endpoint on the same path — letting one creator replay our signature at yours and collect your tool's output unpaid. Per-creator keys will make that structural; signing the host closes it now.

Three more things that silently weaken the check#

  • Hash the raw body bytes, before any JSON parse or re-serialise. Re-encoding changes the bytes and the hash will not match.
  • Enforce the 60-second window in both directions. Without an upper bound a future-dated request is valid forever; without a lower one a captured request replays forever.
  • Join with \\n and nothing else. No trailing newline, no spaces, no carriage returns.