Admin

HMAC Request Signing (Agent API)

Technical reference for HMAC-SHA256 signing used by the Opterius Agent's internal API.

Last updated 1775606400

The Opterius Agent exposes an HTTP API on 127.0.0.1:7443. The Panel uses this API internally to perform server operations. If you are building custom tooling or automation that talks directly to the Agent — bypassing the Panel — you need to sign every request using HMAC-SHA256.

This is an advanced topic. Most integrations should use the Panel API instead.

Why HMAC, Not Bearer Tokens

The Agent API is intentionally low-level and stateless. Bearer tokens require a token store; HMAC requires only a shared secret. Because the Agent is a compiled binary running on the server, a shared secret in /etc/opterius/agent.conf is the simplest robust option.

The Secret Key

The secret is stored in /etc/opterius/agent.conf:

cat /etc/opterius/agent.conf

Look for the secret_key field. The Panel reads the same value from its database record for the server. The two must match — if they diverge (e.g. after manual edits), the Panel will receive 401 errors from the Agent and show it as offline.

Signature Formula

HMAC-SHA256(secret, timestamp + method + path + body)

Where:

  • secret — the raw value of secret_key from agent.conf, UTF-8 encoded
  • timestamp — current time in RFC 3339 format, e.g. 2026-04-08T14:32:00Z
  • method — HTTP method in uppercase: GET, POST, DELETE, PUT
  • path — the full request path including leading slash, e.g. /account/create
  • body — the raw JSON request body as a string; empty string "" for requests with no body

The components are concatenated directly with no separator.

String to sign example (POST /account/create):

2026-04-08T14:32:00ZPOST/account/create{"username":"alice","domain":"alice.example.com"}

The resulting HMAC is hex-encoded (lowercase).

Request Headers

Every request to the Agent must include:

Header Value
X-Signature Hex-encoded HMAC-SHA256 signature
X-Timestamp RFC 3339 timestamp used in the signature
Content-Type application/json (for POST/PUT requests)

The Agent rejects requests where the timestamp differs from server time by more than 5 minutes. This prevents replay attacks. Ensure your system clock is accurate (NTP sync recommended).

Python Example

import hmac
import hashlib
import json
import time
from datetime import datetime, timezone

import requests

SECRET_KEY = "your-secret-key-here"  # from /etc/opterius/agent.conf
AGENT_URL = "http://127.0.0.1:7443"


def sign_request(method: str, path: str, body: str = "") -> dict:
    """Return the headers needed to authenticate an Agent API request."""
    timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
    message = timestamp + method.upper() + path + body
    signature = hmac.new(
        SECRET_KEY.encode("utf-8"),
        message.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return {
        "X-Signature": signature,
        "X-Timestamp": timestamp,
        "Content-Type": "application/json",
    }


# Example: create an account
payload = {
    "username": "alice",
    "domain": "alice.example.com",
    "email": "alice@example.com",
    "password": "s3cur3p@ss",
    "package_id": 1,
}
body = json.dumps(payload)
headers = sign_request("POST", "/account/create", body)

response = requests.post(f"{AGENT_URL}/account/create", headers=headers, data=body)
print(response.status_code, response.json())

[!NOTE] hmac.new is used here for Python's standard library. If you are using Python 3.7+, hmac.new is equivalent to hmac.HMAC. The standard library hmac module is always preferred over third-party alternatives for correctness.

Error Responses

Status Meaning
401 Signature invalid or timestamp out of range
403 Valid signature but license check failed — see License Invalid

A 401 response means either the secret doesn't match, the timestamp is stale, or the string-to-sign was assembled incorrectly. Double-check the concatenation order: timestamp → method → path → body.

Related