# Setup and Register a Rule Server

The first step in setting up **Policies** for the Vertex is to set up a **Rule Server**.

:::note[Security Note]
In order to maintain the distributed security guarantees of the system, it is recommended to have a dedicated instance of the Rule Server for each Vertex instance (running the same logic).
:::

## Securing the Connection

The Rule Server will receive POST requests from the Vertex to the URL provided when [defining](https://docs.sodot.dev/vertex-api-reference/#tag/admin/POST/admin/policies/add-external-rule) the Rule in the Vertex.
Each request will be a JWT that is signed by the Vertex.\
In order to verify the JWT, the Rule Server must be configured with the Vertex public key, that can be retrieved in PEM format using:

```bash
curl -X GET 'https://<YOUR_VERTEX>/identity-pubkey' > vertex_public_key.pem
```

The public key can then be used to verify the JWT signature using the `ES256` algorithm.

### Generating a JWT Keypair

The Vertex supports both the `ES256` (our recommendation) and `RS256` (RSA 2048-bit) algorithms for **Rule Server** JWTs.
To generate a keypair for the Rule Server, you can use the following commands:

<Tabs>
  <Tab title="ES256">
    ```bash
    # Generating the private key
    openssl ecparam -name prime256v1 -genkey -noout -out es256_private.pem
    # Generating the public key in PEM format
    openssl ec -in es256_private.pem -pubout -out es256_public.pem
    ```
  </Tab>

  <Tab title="RS256">
    ```bash
    # Generating the private key
    openssl genrsa -out rule_server_private.pem 2048
    # Generating the public key in PEM format
    openssl rsa -in rule_server_private.pem -outform PEM -pubout -out rule_server_public.pem
    ```
  </Tab>
</Tabs>

### Request Data Spec

The JWT will contain a JSON structure that always includes an `action` field, each `action` has a different data structure:

<Tabs>
  <Tab title="sign_ecdsa">
    ```json
    {
      "action": "sign_ecdsa",
      "request": {
        "hash_algo": "keccak256", // The hash algorithm used to hash the message: "keccak256", "sha256", "sha256d" or "none"
        "msg": "1a2b3c4d5e6f", // The message to be signed in hex format
        "derivation_path": [44, 66, 0, 0, 0], // The derivation path of the key to use for signing
        "derived_pubkey": "04bd59...99ec" // The uncompressed derived public key of the key to use for signing
      },
      "extra_data": "", // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42" // A unique identifier for the request, must be included in the response.
    }
    ```
  </Tab>

  <Tab title="sign_ed25519">
    ```json
    {
      "action": "sign_ed25519",
      "request": {
        "msg": "1a2b3c4d5e6f", // The message to be signed in hex format
        "derivation_path": [44, 66, 0, 0, 0], // The derivation path of the key to use for signing
        "derived_pubkey": "04bd59...99ec" // The derived public key of the key to use for signing
      },
      "extra_data": "", // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42" // A unique identifier for the request, must be included in the response.
    }
    ```
  </Tab>

  <Tab title="sign_exportable_ed25519">
    ```json
    {
      "action": "sign_exportable_ed25519",
      "request": {
        "msg": "1a2b3c4d5e6f", // The message to be signed in hex format
        "pubkey": "04bd59...99ec" // The public key of the key to use for signing
      },
      "extra_data": "", // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42" // A unique identifier for the request, must be included in the response.
    }
    ```
  </Tab>

  <Tab title="sign_bip340">
    ```json
    {
      "action": "sign_bip340",
      "request": {
        "msg": "1a2b3c4d5e6f", // The message to be signed in hex format
        "derivation_path": [44, 66, 0, 0, 0], // The derivation path of the key to use for signing
        "tweak": "6f5e4d3c2b1a", // The tweak to use for the BIP340 signature in hex format, or null if no tweak was provided
        "derived_pubkey": "04bd59...99ec" // The derived public key of the key to use for signing
      },
      "extra_data": "", // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42" // A unique identifier for the request, must be included in the response.
    }
    ```
  </Tab>

  <Tab title="sign_sr25519">
    ```json
    {
      "action": "sign_sr25519",
      "request": {
        "msg": "1a2b3c4d5e6f", // The message to be signed in hex format
        "derivation_path": ["this", "is", "a", "derivation", "path"], // The derivation path of the key to use for signing
        "derived_pubkey": "04bd59...99ec" // The derived public key of the key to use for signing
      },
      "extra_data": "", // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42" // A unique identifier for the request, must be included in the response.
    }
    ```
  </Tab>

  <Tab title="sign_hmac_sha256/384/512">
    ```json
    {
      "action": "sign_hmac_sha256", // Or "sign_hmac_sha384" / "sign_hmac_sha512", depending on the endpoint called
      "request": {
        "msg": "1a2b3c4d5e6f", // The message to be signed in hex format
        "key_name": "my-hmac-key" // The name of the key to use for signing, or null if the key was referenced by key_id
      },
      "extra_data": "", // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42" // A unique identifier for the request, must be included in the response.
    }
    ```
  </Tab>

  <Tab title="sign_rsa">
    ```json
    {
      "action": "sign_rsa",
      "request": {
        "msg": "1a2b3c4d5e6f", // The message to be signed in hex format
        "pubkey": "30820222300d0609...0203010001" // The public key of the key to use for signing, in hex format
      },
      "extra_data": "", // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42" // A unique identifier for the request, must be included in the response.
    }
    ```
  </Tab>
</Tabs>

### Response Data Spec

The Rule Server must respond with a JWT that is signed by the Rule Server's private key.\
The structure of the response JWT must be one of the following:

<Tabs>
  <Tab title="approved">
    ```json
    {
      "result": "approved", // The result of the request, must be 'approved' to sign the message
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42" // The request_id from the request
    }
    ```
  </Tab>

  <Tab title="rejected">
    ```json
    {
      "result": "rejected", // The result of the request, "rejected" will fail the signing operation
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42", // The request_id from the request
      "reason": "Invalid message" // The reason for rejecting the request, the Vertex will report this string to the User
    }
    ```
  </Tab>

  <Tab title="retry">
    ```json
    {
      "result": "retry", // The result of the request, "retry" will cause the Vertex to retry the request (up to a maximum of 3 retries)
      "request_id": "f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42", // The request_id from the request
      "reason": "Invalid message" // An optional reason for retrying the request, the Vertex will report this string to the User in case all retries fail.
    }
    ```
  </Tab>
</Tabs>

## Rule Server Examples

### Whitelisting Withdrawal Addresses Policy

An example Rule Server implementation for a policy that implements whitelisting for withdrawal addresses is found [here](https://repo.sodot.dev/artifactory/sodot-reference-implementations/transfers-whitelisting-policy-server.zip) (please contact the Sodot team in case you do not have access).\
For instructions on how to set up and run the example Rule Server, please refer to the `README.md` file in the repository.

### Minimal Rule Server Example

Below is a minimal example of a Rule Server implemented in Node.js using the Express framework and the `jsonwebtoken` library.
This example Rule Server **approves all signing requests**.

The flow is as follows:

1. The Rule Server receives a POST request from the Vertex.
2. The Rule Server validates the JWT signature using the Vertex public key.
3. The Rule Server parses the request data and validates the message or transaction that is being signed.
4. The Rule Server responds with a JWT indicating whether the request is approved.

```javascript [rule_server.js]
const express = require("express");
const jwt = require("jsonwebtoken");
const fs = require("fs");
// Load the Rule Server private key and the Vertex public key
const privateKey = fs.readFileSync("rule_server_private.pem");
const vertexPubkey = fs.readFileSync("vertex_public_key.pem");

const app = express();
const port = 80;

app.use(function (req) {
  req.decodedBody = "";
  req.setEncoding("utf8");
  req.on("data", function (chunk) {
    req.decodedBody += chunk;
  });
  req.on("end", function () {
    req.next();
  });
});

app.post("/validate_transaction", (req, res) => {
  let signRequest = "";
  try {
    signRequest = jwt.verify(req.decodedBody, vertexPubkey);
  } catch (error) {
    res.sendStatus(401); // Unauthorized
  }

  if (signRequest) {
    const { request_id } = signRequest; // request_id must be returned in the response
    console.log("Sign request data", signRequest); // For this example we only print the request
    let result = "approved"; // We will approve all requests in this example
    const jwtRes = jwt.sign(
      {
        result,
        request_id,
      },
      privateKey,
      { algorithm: "RS256" } // For this example we use RS256
    );
    res.send(jwtRes);
  }
});

app.listen(port, () => {
  console.log(`Server listening at http://0.0.0.0:${port}`);
});
```

## Next Steps

Once the Rule Server is set up, you can [define](https://docs.sodot.dev/vertex-api-reference/#tag/admin/POST/admin/policies/add-external-rule) the Rule in the Vertex and [create](https://docs.sodot.dev/vertex-api-reference/#tag/admin/POST/admin/policies/create-policy) a Policy that includes the Rule.
Full details on setting up Policies on the Vertex can be found [here](/mpc-infra/policies/setting_policies).
