# Cambrian CLI

## Prerequisites

* node.js >= 22.0.0
* docker >= 20.0.0

## Installing Cambrian utility

```
npm i --global @cambrianone/camb-client@latest
```

## Initialization

{% @mermaid/diagram content="flowchart TB
subgraph Offchain
avs\[AVS]

```
    subgraph Operators
        operator1[Operator1]
        operator2[Operator2]
        operator3[Operator3]
    end

end

subgraph Onchain
    poa[Cambrian PoA program]

    oracle[Cambrian Oracle program]

    jito-restaking[Jito Restaking program]

    jito-vault[Jito Vault program]
end

cli["Cambrian CLI utility"]

cli --> |1.1 Scaffolds AVS| avs
cli --> |1.2 Scaffolds Operators| Operators
cli --> |1.3 Initializes PoA and operators onchain| Onchain" %}
```

### Scaffold AVS and initialize PoA onchain

```bash
camb init -t avs <AVS directory>
```

Explaining wizard:

* `Enter AVS IP address to bind to` - provide an IP address to bind to, it should be reachable from all the operators
* `Enter AVS HTTP port to bind to` - provide an HTTP port to bind to, it should be reachable from all the operators
* `Enter AVS WS port to bind to` - provide a WebSockers port to bind to, it should be reachable from all the operators
* `Enter admin private key or press enter to generate a new one` - admin private key / keypair as array of `uint8` or as base58-encoded string
* `Enter Solana API URL or press enter to use default` - Solana JSON RPC endpoint
* `Enter Solana API WS URL or press enter to use default` - Solana WebSockets endpoint
* `Enter Cambrian Consensus Program name or press enter to generate a new one` - string identifier (name) of the Cambrian Consensus Program (CCP) instance (aka PoA name)
* `Enter proposal storage key or press enter to generate a new one` - unique string key for the proposal storage
* `Enter storage space` - common oracle storage space, in bytes
* `Enter consensus threshold` - The minimum number of operators required to approve a proposal before execution
* `Enter stake threshold` - The **minimum stake** required for an operator to participate in CCP

### List installed AVS instances

```bash
camb avs list
```

### Start AVS:

```bash
camb avs run -u <AVS pubkey>
```

AVS is stated for:

* Distributing payload between the operators
* Checking vault state and it's updating when needed (once in an epoch)

### Scaffold operators and initialize them onchain

#### Scaffolding operators

Before scaffolding the operators make sure instance of AVS is already running.

```bash
camb init -t operator <operator 1 directory>
camb init -t operator <operator 2 directory>
camb init -t operator <operator 3 directory>
```

Explaining wizard:

* `Enter AVS HTTP URL` - AVS HTTP endpoint
* `Enter AVS WS URL` - AVS WebSockets endpoint

After initialization of AVS and operators you can optionally add an external service to run it alongside with AVS or operator instances:

```bash
camb manage add-external -n <service name> -p <path to AVS/operator directory> -i <external service container image> [-e <NAME1=VALUE1...>]
```

This command will create a boilerplate section for external service in AVS/operator docker-compose file. You can customize it later.

## Running components

{% @mermaid/diagram content="flowchart TB
subgraph Offchain
avs\[AVS]

```
    subgraph Operators
        operator1[Operator1]
        operator2[Operator2]
        operator3[Operator3]
    end

end

subgraph Onchain
    poa[Cambrian PoA program]

    oracle[Cambrian Oracle program]

    jito-restaking[Jito Restaking program]

    jito-vault[Jito Vault program]
end

cli["Cambrian CLI utility"]


cli --> |2.1 Starts AVS| avs
cli --> |2.2 Starts Operators| Operators
" %}
```

### List installed operator nodes (outputs voter public keys)

```bash
camb operator list -a <AVS public key>
```

### Start operators:

```bash
camb operator run -u <voter public key>
```

Each operator waits for the command from the AVS to store data to oracle storage and execute the proposal.

## Executing proposal

{% @mermaid/diagram content="flowchart TB
subgraph Offchain
avs\[AVS]

```
    subgraph Operators
        operator1[Operator1]
        operator2[Operator2]
        operator3[Operator3]
        payload-container1([Payload Container])
        payload-container2([Payload Container])
        payload-container3([Payload Container])
    end

end

subgraph Onchain
    poa[Cambrian PoA program]

    oracle[Cambrian Oracle program]

    jito-restaking[Jito Restaking program]

    jito-vault[Jito Vault program]
end

cli["Cambrian CLI utility"]


cli --> |3.1 Sends name of payload container image to AVS| avs
avs --> |3.2 Broadcasts name of payload container image to Operators| Operators

operator1 --> |3.3 Starts container| payload-container1
payload-container1 --> |3.4 Returns oracle data and proposal instructions| operator1

operator2 --> |3.3 Starts container| payload-container2
payload-container2 --> |3.4 Returns oracle data and proposal instructions| operator2

operator3 --> |3.3 Starts container| payload-container3
payload-container3 --> |3.4 Returns oracle data and proposal instructions| operator3

Operators --> |3.5 Store data to oracle storage| oracle
Operators --> |3.6 Send proposal instructions| poa" %}
```

### Running payload

Payload container holds data for oracle storage and execution instructions for the proposal.

Container receives a parameter (in `CAMB_MVP` environment variable) serialized as JSON-object.

It's type is:

```typescript
type TPayloadInput = {
  executorPDA?: string;
  apiUrl?: string;
  extraSigners?: Array<string>;
  poaName: string;
  proposalStorageKey: string;
}

```

`extraSigners` represents an optional array of serialized private keys used for signing transaction.

Container should write a JSON-stringified object.\
It's type is:

```typescript
type TPayloadOutput = {
  proposalInstructions: Array<{
    accounts: Array<{
      address: string;
      role: 0 | 1 | 2 | 3    
    }>,
    data: string;
    programAddress: string;
  }>;
  storagePayload:
  | { encoding: 'utf-8'; data: string }
  | { encoding: 'bytes'; data: number[] }
  | { encoding: 'base58'; data: string }
  | { encoding: 'base64'; data: string };
}
```

where proposal instructions `data` is base58-serialized data (or array of uint8) buffer and account `role` is the following enum:

```typescript
enum AccountRole {
    // Bitflag guide: is signer ⌄⌄ is writable
    WRITABLE_SIGNER = /* 3 */ 0b11,
    READONLY_SIGNER = /* 2 */ 0b10,
    WRITABLE =        /* 1 */ 0b01,
    READONLY =        /* 0 */ 0b00,
}
```

This type could be represented as JSON-schema:

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "TPayloadOutput",
  "type": "object",
  "properties": {
    "proposalInstructions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "accounts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "address": {
                  "type": "string",
                  "description": "Account address"
                },
                "role": {
                  "type": "integer",
                  "enum": [0, 1, 2, 3],
                  "description": "Account role as bitflag (is signer | is writable)\n\n0 (0b00): READONLY\n1 (0b01): WRITABLE\n2 (0b10): READONLY_SIGNER\n3 (0b11): WRITABLE_SIGNER"
                }
              },
              "required": ["address", "role"],
              "additionalProperties": false
            },
            "description": "Array of accounts involved in the instruction"
          },
          "data": {
            "type": "string",
            "description": "Instruction data as base58 encoded string"
          },
          "programAddress": {
            "type": "string",
            "description": "Program address for the instruction"
          }
        },
        "required": ["accounts", "data", "programAddress"],
        "additionalProperties": false
      },
      "description": "Array of proposal instructions"
    }
  },
  "storagePayload": {
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "encoding": {
          "const": "utf-8"
        },
        "data": {
          "type": "string"
        }
      },
      "required": ["encoding", "data"],
      "additionalProperties": false
    },
    {
      "type": "object",
      "properties": {
        "encoding": {
          "const": "bytes"
        },
        "data": {
          "type": "array",
          "items": {
            "type": "number"
          }
        }
      },
      "required": ["encoding", "data"],
      "additionalProperties": false
    },
    {
      "type": "object",
      "properties": {
        "encoding": {
          "const": "base58"
        },
        "data": {
          "type": "string"
        }
      },
      "required": ["encoding", "data"],
      "additionalProperties": false
    },
    {
      "type": "object",
      "properties": {
        "encoding": {
          "const": "base64"
        },
        "data": {
          "type": "string"
        }
      },
      "required": ["encoding", "data"],
      "additionalProperties": false
    }
  ]
},
  "required": ["proposalInstructions", "storagePayload"],
  "additionalProperties": false
}
```

Example of running payload container:

```bash
$ docker run --rm -e CAMB_INPUT='{"poaName": "test", "proposalStorageKey": "test"}' check-oracle | jq .
{
  "proposalInstructions": [
    {
      "programAddress": "ECb6jyKXDTE8NjVjsKgNpjSjcv4h2E7JQ42yKqWihBQE",
      "accounts": [
        {
          "address": "Qrszp1YM5zEW38JgHPMQVedeZ3A14HmaanB3z65d8Ps",
          "role": 1
        },
        {
          "address": "ExLXdCbiXhdmRw3MzmNFGPexCgSuoRpC3YTnGQ6dNekP",
          "role": 0
        },
        {
          "address": "Sysvar1nstructions1111111111111111111111111",
          "role": 0
        },
        {
          "address": "ECb6jyKXDTE8NjVjsKgNpjSjcv4h2E7JQ42yKqWihBQE",
          "role": 0
        }
      ],
      "data": "EmqjS8fy7HCvpUiTJqJxGB"
    }
  ],
  "storagePayload": {
    "encoding": "utf-8",
    "data": "Local time: 1746009154987"
  }
}
```

### Build payload container image

```bash
git clone https://github.com/cambrianone/payload-images
cd ./payload-images/check-oracle
docker build -t payload-check-oracle .
```

### Run payload

Send image name of payload container from the previous step (`payload-check-oracle`) to the AVS instance

```bash
camb payload run-container -a <AVS public key | AVS URL> -p [period in seconds] payload-check-oracle
```

* AVS broadcasts payload container image name to running operators
* Operators run payload containers
* Payload containers return data to store in oracle storage and proposal instructions
* Operators invoke `store_to_storage` instruction in PoA program to store data in oracle storage
* Operators invoke `handle_proposal` instruction in PoA program with proposal instructions


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.cambrian.one/cambrian-platform/cambrian-cli.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
