Skip to content

Commit

Permalink
Updated docs
Browse files Browse the repository at this point in the history
  • Loading branch information
jclapis committed Dec 16, 2024
1 parent bc2a03e commit 95f37f3
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 37 deletions.
3 changes: 2 additions & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ fail() {
# Builds all of the CLI binaries
build_cli() {
echo -n "Building CLI binaries... "
docker buildx build --rm -f docker/cli.dockerfile --output build/$VERSION --target cli . || fail "Error building CLI binaries." echo "done!"
docker buildx build --rm -f docker/cli.dockerfile --output build/$VERSION --target cli . || fail "Error building CLI binaries."
echo "done!"
}


Expand Down
99 changes: 76 additions & 23 deletions specs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ A configuration metadata object consists of the following two properties, each o

A **Section** adheres to an individual section of the configuration (a grouping of parameters and subsections). It should have the following properties:

- `id` ([Identifier](./types.md#identifier), required): a unique identifier for the section. This is *not* presented to the user; it is only used internally as a way to reference the parameter.

- `name` ([Identifier](./types.md#identifier), required): a human-readable name for the section.

- `description` ([Dynamic string](./types.md#dynamic-properties) - [Description](./types.md#description), required): the human-readable description of the section. This will correspond to the value of the description box when the individual property is being configured in Hyperdrive's interactive configurator. It can be a string with any characters.
Expand Down Expand Up @@ -145,26 +147,77 @@ The following types of parameters are allowed:

## Instances

A **Configuration Instance** is an object that corresponds to a "filled out" version of a Configuration Metadata object. It has an identical Section and Parameter structure, but instead of Parameters it has **Parameter Instances**. which are simply key-value pairs. These indicate the value assigned to each parameter without any of the extraneous metadata. The type of value in each Parameter Instance is the same as the type of the corresponding Parameter.

A configuration instance object consists of the following properties, which must align exactly with the corresponding Metadata object:

- `parameters` ([Parameter Instance\[\]](#parameter-instance), required): an array of key-value pairs, where each one's name corresponds to the ID of a [Parameter Metadata](#parameters) object and the value is the value of that corresponding parameter.

- `sections` ([Instance Section\[\]](#section-1)): an array of groups of parameter instances used purely for organizational purposes. They can also have their own section children underneath them.


### Section

A **Section** in an instance is hierarchically identical to a [metadata section]()

- `name` ([Identifier](./types.md#identifier), required): the name of the corresponding section in the Configuration Metadata object.

- `parameters` ([Parameter Instance\[\]](#parameter-instance), required): an array of key-value pairs, where each one's name corresponds to the ID of a [Parameter Metadata](#parameters) object and the value is the value of that corresponding parameter.

- `sections` ([Instance Section\[\]](#section-1)): an array of groups of parameter instances used purely for organizational purposes. They can also have their own section children underneath them.


### Parameter Instance

Parameter instances are simple key-value pair properties. The key corresponds to the ID of the parameter metadata it represents. The value is the value assigned to the instance of that parameter metadata. The type of the value is dictated by the parameter metadata.
A **Configuration Instance** is an object that corresponds to a "filled out" version of a Configuration Metadata object. Instead of discrete parameters and section objects, each one becomes a simple key-value pair property. These indicate the value assigned to each parameter without any of the extraneous metadata. The type of value in each Parameter Instance is the same as the type of the corresponding metadata parameter.

Each top-level metadata parameter and section from the corresponding configuration metadata will become a top-level property of the configuration instance as follows:

- For each Parameter, the property will have the Parameter's `id` as the key and the parameter's `value` as the value.
- For each Section, the property will have the Section's `id` as the key. The value will be an object itself, which contains key-value pairs for all Parameters and Sections within that Section recursively.

For example, say the Configuration Metadata looked like this:

```json
{
"parameters": [
{
"id": "param1",
"name": "Parameter 1",
"description": {
"default": "This is the first parameter",
},
"type": "bool",
"default": false,
"value": true,
"overwriteOnUpgrade": false,
"affectedContainers": []
},{
"id": "param2",
"name": "Parameter 2",
"description": {
"default": "This is the second parameter",
},
"type": "int",
"default": 100,
"value": 12,
"overwriteOnUpgrade": false,
"affectedContainers": []
}
],
"sections": [
{
"id": "subsection",
"name": "Sub-section",
"description": {
"default": "This is a subsection of the main config"
},
"parameters": [
{
"id": "param3",
"name": "Parameter 3",
"description": {
"default": "This is the third parameter",
},
"type": "string",
"default": "",
"value": "hello world",
"overwriteOnUpgrade": false,
"affectedContainers": []
}
],
"sections": []
}
]
}
```

The corresponding Configuration Instance would then be this:

```json
{
"param1": false,
"param2": 12,
"subsection": {
"param3": "hello world"
}
}
```
6 changes: 3 additions & 3 deletions specs/examples/hello-world/adapter.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
# You can make yours as simple or as complex as you need, as long as you follow the adapter specification.

services:
{{.GetAdapterContainerName}}:
{{.GetProjectName}}_hw_adapter:
image: nodeset/hd-hello-world:v1.0.0
restart: unless-stopped
entrypoint: /usr/bin/hd-adapter # This is the path to the adapter binary
environment:
- HD_PROJECT_NAME={{.GetProjectName}}
volumes:
- {{.ModuleConfigDir}}:/hd/config
- {{.ModuleSecretFile}}:/hd/secrets/adapter.key
- {{.ModuleLogDir}}:/hd/logs
- {{.ModuleJwtKeyFile}}:/hd/secrets/jwt.key
cap_drop:
- all
cap_add:
Expand Down
11 changes: 6 additions & 5 deletions specs/examples/hello-world/templates/service.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ services:
ports:
- "127.0.0.1:{{.GetValue apiPort}}:{{.GetValue apiPort}}/tcp" # Restricted to localhost outside of Docker
volumes:
- {{.ModuleConfigDir}}:{{.ModuleConfigDir}}:ro # The module's config directory
- {{.ModuleConfigDir}}/native.cfg:{{.ModuleConfigDir}}/native.cfg:ro # The module's config directory
- {{.ModuleLogDir}}:{{.ModuleLogDir}} # The module's log directory
- {{.ModuleDataDir}}:{{.ModuleDataDir}} # The module's data directory
- {{.ModuleJwtKeyFile}}:{{.ModuleJwtKeyFile}}:ro
- {{.HyperdriveJwtKeyFile}}:{{.HyperdriveJwtKeyFile}}:ro
- {{.ModuleLogDir}}:{{.ModuleLogDir}} # The module's log directory
command:
- "--config-dir"
- "{{.ModuleConfigDir}}" # The module's config directory
- "--config-file"
- "{{.ModuleConfigDir}}/native.cfg" # The module's config directory
- "--data-dir"
- "{{.ModuleDataDir}}" # The module's data directory
- "--log-dir"
- "{{.ModuleLogDir}}" # The module's log directory
- --hyperdrive-url
- "{{.HyperdriveDaemonUrl}}" # HTTP URL for the Hyperdrive API
- --ip
Expand Down
56 changes: 56 additions & 0 deletions specs/hyperdrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Hyperdrive

Hyperdrive is a toolkit that bridges service maintainers who want to run software in a physically decentralized capacity with [NodeSet's](https://nodeset.io) cohort: a geographically distributed network of node operators (over 400 across more than 40 countries) that are willing and able to run it. For node operators, it provides a unified configuration and execution experience which lets them run that software in a familiar and comfortable setting. For service maintainers, it provides a flexible architecture that offers the freedom to build, distribute, configure, and execute services in whatever language or paradigm they like with no changes required for conformity.

Coupled with NodeSet's systems for presenting new software offerings to its node operators and providing blockchain-based operator payments, Hyperdrive is a powerful capability that enables service maintainers to rapidly bootstrap a distributed backbone.


## Components

The Hyperdrive system involves several key components - the **CLI**, the **service**, and the **modules**:

![](images/ov1.png)

The **CLI** is the terminal-based application that users use to interact with Hyperdrive services. It provides a way to quickly and easily install, manage, configure, and run them. It supports both interactive and non-interactive modes for manual execution and headless automation.

The **Service** (also known as the "daemon") is a persistent-running background application that maintains common state and provides an API endpoint for other services to query. It's the "central station" that other services can interact with to learn more about the active Hyperdrive installation. It also provides authenticated access to the nodeset.io API.

While the CLI and Service are powerful, they don't perform any duties or roles by themselves. That functionality is provided **modules** installed into the Hyperdrive system. Modules are custom third-party applications that contain description metadata, persistent services, and a "bridging application" (called the **adapter**) that translates between Hyperdrive functions and native module functions.
![alt text](image.png)

Hyperdrive itself is responsible for provisioning and running any modules the node operator wants to install. It is the responsibility of the module authors to provide applications that the users choose to install, which may or may not be incentivized by paying the node operators for their services. <<*NOTE: link to node operator payment info here*>>

Hyperdrive provides rigorous specifications and documentation for developers on how to create a Hyperdrive module. To learn more, visit the [modules developer guide](./module.md).


### Built-In Modules

Some modules have been authored by the Hyperdrive team which can serve as examples. They include:
- A BIP39 wallet for standard mnemonic-based private key creation and storage
- An Ethereum Node for running an Execution Client and Beacon Node for the Ethereum network
- An Ethereum Validator for running an Ethereum validator client, for staking or LST node operation
- A StakeWise module for supporting Ethereum validation on behalf of StakeWise vaults
- A module for the [NodeSet Constellation](https://docs.gravitaprotocol.com/gravita-docs/constellation/overview) protocol

<<*NOTE: repository links to be provided as they're built*>>


## Requirements

Hyperdrive is readily available for **Linux** systems on `x64` and `arm64` platforms, with MacOS support planned. Windows support is not currently planned at this time. Any Linux distribution that supports `glibc` 2.36 or higher is supported, but to give a common examples:
- Debian Bookworm (12)+
- Ubuntu Jammy (22.04)+
- Fedora 39+

Note that some modules may have more stringent requirements, or may have specific hardware requirements (such as CPU count, total system RAM, or free disk space); consult the module's own documentation for its details.

Hyperdrive leverages [Docker containerization](https://docs.docker.com/get-started/docker-overview/) extensively, so the ability to run Docker is required.

Hyperdrive is designed in such a way that all interaction (including configuration) is done within a terminal, so a remote shell such as SSH is recommended. A GUI / desktop environment is not required.


## Architecture

The Hyperdrive CLI is a standalone binary that is installed on the machine's local filesystem, along with several supporting service files. It runs natively as the logged-in user, and has access to anything that user has access to (notably it does *not* have access to module data, unless the user has root user privileges).

The Hyperdrive Service is an application run within a Docker container sandbox, as are all module components (including the adapter). They are all networked together and can thus communicate with one another, but they cannot access arbitrary files on the native filesystem beyond what the module author gives it access to.
Binary file added specs/images/ov1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 10 additions & 5 deletions specs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ Each of these use cases has a different way of utilizing templates, which are co
Templates can be used anywhere inside a Docker Compose file template for your adapter. The following methods are supported:


### GetProjectName

`{{.GetProjectName}}` provides the Docker Compose project name for the project your adapter belongs to. Use this in tandem with your service names to retrieve the name of any of your service containers.


### ModuleConfigDir

`{{.ModuleConfigDir}}` retrieves the full path to the directory that your adapter should save its configuration (and any other extraneous files) into. This will also be made available to your service Docker Compose templates so it can be mounted as a volume for file retrieval.
Expand Down Expand Up @@ -75,6 +80,11 @@ Any dynamic property templates will be run dynamically whenever one of the follo
Templates can be used anywhere inside a Docker Compose file template for service definitions. The following methods are supported:


### GetProjectName

`{{.GetProjectName}}` provides the Docker Compose project name for the project your adapter belongs to. Use this in tandem with your service names to specify the name of any of your service containers.


### GetValue

`{{.GetValue <FQPN>}}` retrieves the value of the provided parameter, which is specified by its [Fully Qualified Parameter Name](./types.md#fully-qualified-parameter-name).
Expand Down Expand Up @@ -109,11 +119,6 @@ Whenever Hyperdrive starts its services (including the modules), the template fi
`{{.HyperdriveDaemonUrl}}` returns the full URL, including scheme and port, for the Hyperdrive daemon's API endpoint. Your service can use this to send HTTP API requests to Hyperdrive.


### ModuleJwtKeyFile

`{{.ModuleJwtKeyFile}}` returns the path of the file on-disk that should be used as the JWT authentication secret key by your service.


### HyperdriveJwtKeyFile

`{{.HyperdriveJwtKeyFile}}` returns the path of the file on-disk that your daemon must use as the JWT authentication secret key when sending requests to the Hyperdrive API.

0 comments on commit 95f37f3

Please sign in to comment.