# Subnet Security

Securing a subnet starts with securing the Protocols, which is primarily where nodes communicate. It's important to implement security measures to avoid unwanted entries, record spam, sybil attacks, etc.

#### There are two main features to secure a subnet:

* [Authorizers](#authorizers)
* [Record Validators](#record-validators)

***

## [Authorizers](https://docs.hypertensor.org/subnet-template/authorizers)

Authorizers are middleware between peer-to-peer communication requests and responses. With Authorizers, custom logic can be deployed in protocols for all node communication. For example, an authorizer can include [rate limiting](https://docs.hypertensor.org/subnet-template/authorizers/rate-limiter) in an inference subnet, require a [proof-of-stake](https://docs.hypertensor.org/subnet-template/authorizers/pos) when joining a subnet, or require [signing requests and responses](https://docs.hypertensor.org/subnet-template/authorizers/signature-authorizer) for communication between nodes.

**Note**: The template comes with the above options, and developers can create their own custom Authorizers.

**At a minimum**, the DHT protocol (the main protocol for node communication and record storage) must implement the [PoS](https://docs.hypertensor.org/subnet-template/authorizers/pos) authorizer to ensure only nodes with a proof of stake can join the subnet and access the records, and communicate with nodes.

{% hint style="info" %}
The subnet template comes with PoS (proof-of-stake) built in. Authorizers only need to be added to the protocols that subnet teams build **if** a subnet requires peer-to-peer communication for its application logic. **If a subnet does not require peer-to-peer communication** for its application logic, then Authorizers are not needed anywhere other than the built-in PoS authorizer.
{% endhint %}

As well, **at a minimum**, each custom protocol should implement a [signature authorizer](https://docs.hypertensor.org/subnet-template/authorizers/signature-authorizer) (the signature authorization logic is built into the PoS authorizer).

**For example**, in a subnet for inference that utilizes the signature authorizer, one peer will call another peer to perform an inference task, the requesting peer will sign the data, and the responding peer will validate the signed data and respond with a signed response (signed tensor output), and finally, the requesting peer will then validate the signed response.

<figure><img src="https://743092870-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaoXQsAR3UWLLhndOMfU7%2Fuploads%2F9m6LPSKmKlSYA5MqF4ML%2Fauthorizer-graph.svg?alt=media&#x26;token=53f3f6dc-cbca-4298-979e-ba1030f442fb" alt=""><figcaption></figcaption></figure>

## [Record Validators](https://docs.hypertensor.org/subnet-template/dht-records/record-validator)

{% hint style="info" %}
Each DHT store request will first go through an Authorizer, if an Authorizer is implemented into the DHT Protocol (see example implementations below). For instance, records are stored through the DHT Protocol (see `/dht/protocol.py`). When you request another peer to store something, or another peer requests you to store something, both the request and response will go through the Authorizer first (see [`get_stub(p2p, peer)`](https://docs.hypertensor.org/protocols#get_stub-p2p-peer) for a more detailed explanation), then the Record Validator is called before the record is stored.
{% endhint %}

Record validators validate the records in the decentralized database (see [DHT Records](https://docs.hypertensor.org/subnet-template/dht-records)) for `POST` and `GET` requests.

**Record validators** enable use cases where the subnet can have database conditions, just like a standard key-value database. Such as key, subkey, value, expiration, Pydantic schema validation conditions, owned and protected records, a commit-reveal schema, or any use case the subnet needs to validate records on both store requests and get requests.

For example, if utilizing a commit-reveal, you will likely implement the [predicate validator](https://docs.hypertensor.org/subnet-template/dht-records/record-validator/predicate-validator) or even create a custom record validator. A **predicate validator** allows Python callables to be accessed when storing or getting data from the DHT records, such as accessing blockchain RPC methods. This can be used to have a commit-reveal scheme in-subnet to ensure commits and reveals can only be performed between specific blocks in specific epochs. For example, you can require `f"commit-{current_epoch}"` can only be stored up to the 50% mark of the epoch (the `current_epoch` and the 50% mark can be accessed and calculated from values from the callable function).

**At a minimum**, a subnet should **only allow keys that are required** to be stored in the DHT records, and **require a** **maximum expiration time** in the storage for as long as it's required.

Having owned records can also be important, depending on the use case, to ensure that records cannot be updated by just anyone (see [Signature Validators](https://docs.hypertensor.org/subnet-template/dht-records/record-validator/signature-validators)).

<figure><img src="https://743092870-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaoXQsAR3UWLLhndOMfU7%2Fuploads%2FIELXp9Fuah0QBbQBkOPw%2Frecordvalidator.svg?alt=media&#x26;token=37545d50-3bb0-496a-93fd-8a4db17d85c8" alt=""><figcaption></figcaption></figure>

***

## Working Together

Utilizing both an Authorizer and Record Validators together is a very powerful basis for security and expansiveness.

The DHT comes with its own built-in DHT protocol that utilizes the [PoS Authorizer](https://docs.hypertensor.org/subnet-template/authorizers/pos) (the PoS Authorizer also utilizes the signature authorizer by default). Each protocol built for application logic, such as an inference protocol, should also implement its own authorizer (see [Protocols](https://docs.hypertensor.org/build-a-subnet/protocols)). Whenever a peer wants to store data in the DHT, regardless of the application logic protocol, it first passes through the DHT's PoS Authorizer. Once the signatures and PoS are verified, the record or records are verified based on the Records Validators being used, and finally, after verification, the records are stored.

#### Example

**In the following example**, let's assume the Protocol is an inference protocol and each request for inference is verified to have been completed by the calling peer by storing a TRUE flag into the DHT records that can be later used for scoring.

{% hint style="info" %}
This is a simple example and not something expected to be used in production.
{% endhint %}

The requesting peer will request another peer to respond with an inference output. The request will be signed by the requester and verified by the responder. Once the inference task is verified to be complete, the requesting peer will then store that inference task 1 is complete.

The requesting peer will then call the DHT to store data in the DHT. The request will be signed by the requester and verified by the storing peer that is chosen to store the record. Once the responder verifies the request is valid, it will then verify the record itself based on the record validators used in the DHT. Once complete, the storing peer will return True or False depending on whether the record was stored or not.

{% hint style="info" %}
In practice, many peers store data. For brevity, the example will show only a single request to one of the peers to store the record. For more information on how peers are chosen to store data, see [Traverse (crawl) DHT,](https://docs.hypertensor.org/subnet-template/dht#traverse-crawl-dht) and [`async store_many`](https://docs.hypertensor.org/subnet-template/dht#async-store_many-keys-list-any-values-list-any-expiration_time-union-float-list-float-subkeys-option).
{% endhint %}

<figure><img src="https://743092870-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaoXQsAR3UWLLhndOMfU7%2Fuploads%2Fc9dTiXj2OczTJqhhfuEg%2Fprotocol-dht-flow.svg?alt=media&#x26;token=ede1b044-ba69-4534-b91e-bc20588d8027" alt=""><figcaption></figcaption></figure>

***

## Implementing Authorizers

As previously mentioned, security comes down to securing protocols, where most of the communication takes place. To add an authorizer to the DHT Protocol, we do so from the DHT initialization since the DHT creates the DHT Protocol.

### DHT

In this implementation, we start the DHT with a proof-of-stake authorizer that requires a proof of stake for all communications between all nodes.

{% hint style="info" %}
Authorizers can include any logic and a mix of use cases.
{% endhint %}

```python
# See server.py for full implementation
identity_path = "identity_path.id" # private key path
subnet_id = 1
pk = get_private_key(identity_path)
hypertensor = Hypertensor(rpc, PHRASE)

signature_authorizer = SignatureAuthorizer(pk)
pos = ProofOfStake(
    subnet_id,
    hypertensor,
    min_class=1,
)
pos_authorizer = ProofOfStakeAuthorizer(signature_authorizer, pos)

"""
Start DHT with PoS authorizer

- The authorizer is initialized into the DHTProtocol
"""
dht = DHT(
    initial_peers=initial_peers,
    start=True,
    num_workers=DEFAULT_NUM_WORKERS,
    use_relay=use_relay,
    use_auto_relay=use_auto_relay,
    client_mode=reachable_via_relay,
    record_validators=None,
    **dict(kwargs, authorizer=pos_authorizer) # implement PoS authorizer - only staked peers can join the subnet and communicate
)
```

### Custom Protocol

Start the protocol with an RSA signature protocol. Requiring signing between peers is important so each peer can know who is calling them for each request. Knowing who's requesting and responding to communications allows for more robust logic, such as rate limiting, blacklisting, etc..

```python
identity_path = "identity_path.id" # private key path
subnet_id = 1
hypertensor = Hypertensor(rpc, PHRASE)

pk = get_private_key(identity_path)
signature_authorizer = SignatureAuthorizer(pk)

mock_protocol = MockProtocol(
    dht=dht,
    subnet_id=subnet_id,
    hypertensor=hypertensor,
    authorizer=signature_authorizer, # implement signature authorizer - all communication between peers requires signing and validating
    start=True
)
```

## Implementing Record Validators

Unlike Authorizers, the DHT Protocol can initialize with multiple record validators that validate in order of priority. See [Record Validator](https://docs.hypertensor.org/subnet-template/dht-records/record-validator) for more information.

In the following, we are creating two validators, one that creates **signed and owned records** so we know who owns the record and only they can update it, and a [**Hypertensor Predicate Validator**](https://docs.hypertensor.org/subnet-template/dht-records/record-validator/predicate-validator#hypertensor-predicate-validator) for a commit-reveal scheme that ensures no record is stored for too long, when specific keys can be stored (tied to an epoch), validates key names, and validates values that ensure is abides from a Pydantic schema.

```python
identity_path = "identity_path.id" # private key path
pk = get_private_key(identity_path)

subnet_id = 1
hypertensor = Hypertensor(rpc, PHRASE)

# Create signed and owned records
signature_validator = SignatureValidator(pk)

# Create predicate validator for commit-reveal/key-value validations
consensus_predicate = HypertensorPredicateValidator.from_predicate_class(
    MockHypertensorCommitReveal, hypertensor=hypertensor, subnet_id=subnet_id
)

record_validators=[signature_validator, consensus_predicate]

dht = DHT(
    initial_peers=initial_peers,
    start=True,
    num_workers=DEFAULT_NUM_WORKERS,
    use_relay=use_relay,
    use_auto_relay=use_auto_relay,
    client_mode=reachable_via_relay,
    record_validators=record_validators,
    **kwargs,
)
```

* [See the Signature Validator](https://github.com/hayotensor/mesh-template/blob/77cdb8ef2bebc0bb6f03797a4ecb9d6d346e8f14/mesh/dht/crypto.py#L15) (`SignatureValidator(...)`)
* [See the Hypertensor Predicate Validator Class](https://github.com/hayotensor/mesh-template/blob/2448648c5eea5d8f77051a77e9dca8533e0c2937/mesh/dht/validation.py#L235) (`HypertensorPredicateValidator(...)`)
* [See the initialization of the Hypertensor Predicate Validator Class](https://github.com/hayotensor/mesh-template/blob/2448648c5eea5d8f77051a77e9dca8533e0c2937/mesh/subnet/utils/mock_commit_reveal.py#L49) (`hypertensor_consensus_predicate()`)
