# Developer guide

Implementing a client library for xrDebug is straightforward since it's based on standard HTTP APIs. Client libraries act as wrappers around these HTTP endpoints, providing a clean interface that simplifies configuration and usage for end-users.

Welcome contributions

If you have an idea for a client library in a language or technology not listed here, please reach out to us. Your contributions help make xrDebug better for everyone.

# Design considerations

  • Server doesn't enforce any specific message format and client software is responsible for formatting the debug information. It's recommended to use HTML for messages.
  • "ID" field is used to identify messages and pauses. It should be unique, and it's up to the client to generate it.

Client software only requires to handle the following endpoints:

  • POST /messages
  • POST /pauses
  • GET /pauses/{id}

# Message

To send messages use the POST /messages endpoint. In xrDebug is the client software which formats the debug information, so the server doesn't enforce any specific format. You can send any data you want, but it's recommended to use HTML.

# Pauses

Pauses allow you to temporarily halt program execution until a specific condition is met. They work in a two-state system:

  1. When a pause is created (POST /pauses), it starts with stop: false
  2. The program enters a polling loop, checking the pause status (GET /pauses/{id})
  3. The pause can be released in two ways:
    • Stopped (PATCH /pauses/{id}) - Sets stop: true, program should terminate
    • Deleted (DELETE /pauses/{id}) - Removes the pause, program should continue

# Example pause loop

class PauseException extends Exception {}

function waitForPause(string $id): void
{
    while (true) {
        $response = http_get("/pauses/{$id}");
        if (!$response) {
            // Pause was deleted, continue execution
            return;
        }
        if ($response->stop === true) {
            // Pause was stopped, terminate execution
            throw new PauseException("Execution stopped by debugger");
        }
        // Wait before next check
        sleep(1);
    }
}

try {
    // Create pause
    http_post("/pauses", ["id" => "debug-123"]);
    // Wait for pause to be released
    waitForPause("debug-123");
    // Continue execution if pause was deleted
    echo "Continuing...";
} catch (PauseException $e) {
    echo "Execution stopped";
    exit(1);
}

# Signed requests

Request signing using Ed25519 digital signatures to verify message origin authenticity.

To sign a request, the client must include the X-Signature header on requests made to the xrDebug server. The signature is a base64 encoded string generated by signing the serialized post fields with the private key. If there's no fields sign an empty string.

# Sign workflow

To sign a request server expect the following data workflow:

  1. Sort the post fields by key
  2. Concatenate the key-value pairs
  3. Sign the concatenated string
  4. Base64 encode the signature at X-Signature header

Example in PHP:

function serialize(array $data): string
{
    $result = '';
    ksort($data);
    foreach ($data as $key => $value) {
        $result .= $key . $value;
    }

    return $result;
}

$serialized = serialize($data);
$signature = $privateKey->sign($serialized);
$signHeader = base64_encode($signature);

Example in Python:

def serialize(data: dict) -> str:
    return ''.join(f'{k}{v}' for k, v in sorted(data.items()))

serialized = serialize(data)
signature = private_key.sign(serialized)
signHeader = base64.b64encode(signature).decode()

The X-Signature header should contain the base64 encoded signature generated by the client.

curl --fail -X POST \
    --data "body=My signed message" \
    --data "file_path=file" \
    --data "file_line=1" \
    -H "X-Signature: <signHeader>" \
    http://127.0.0.1:27420/messages