# Open Agent Management Protocol

> Source: https://www.ymotongpoo.com/works/otel-specs-ja/opamp/specification/


ステータス: [Beta]

## はじめに

Open Agent Management Protocol（OpAMP）は、データ収集を行う大規模なAgent群をリモートで管理するためのネットワークプロトコルです。

OpAMPを使うと、AgentはServerにステータスを報告し、Serverから設定を受け取り、Serverからエージェントインストールパッケージの更新を受け取れます。このプロトコルはベンダーに依存しないため、ServerはOpAMPを実装する異なる種類のAgent群（異なるベンダーの混在したAgent群を含む）をリモートで監視・管理できます。

OpAMPは次の機能をサポートします。

* Agentのリモート設定。
* ステータスレポート。このプロトコルにより、Agentは自身の種類やバージョン、稼働しているOSの種類やバージョンといったプロパティを報告できます。ステータスレポートはまた、管理Serverが個々のAgentやAgentの種類に応じてリモート設定を調整することも可能にします。
* [OTLP](https://opentelemetry.io/docs/specs/otlp/)互換のバックエンドへのAgent自身のテレメトリーのレポート。CPUやRAM使用率といったAgentのプロセスメトリクスや、データ処理のレートといったAgent固有のメトリクスを監視するために使われます。
* Agentのハートビート。
* ダウンロード可能なAgent固有パッケージの管理。
* セキュアな自動更新機能（Agentのアップグレードとダウングレードの双方）。
* クライアント側のTLS証明書の失効やローテーションを含む、接続クレデンシャルの管理。

上記の機能によって、混在した大規模なAgent群（OpenTelemetry Collector、Fluentdなど）に対する「単一の管理画面」が実現します。

## 通信モデル

OpAMP Serverは、OpAMPプロトコルのクライアント側実装を提供するAgentを管理します。以降、このクライアント側実装をOpAMP Client、あるいは単にClientと呼びます。OpAMPは、AgentとClientの間に特定の関係を前提としません。Clientは、Agentとは異なるライフサイクルを持つ別プロセスとして動作することも、サイドカーやプラグインとして動作することも、Agentのコードに完全に統合されることもできます。

Agentは、OpAMP Serverから指示された場合にオプションで自身のテレメトリーをOTLPの宛先に送信できます。Agentはおそらく他の宛先にも接続し、収集したデータをそこに送信します。

```
        ┌────────────┬────────┐           ┌─────────┐
        │            │ OpAMP  │  OpAMP    │ OpAMP   │
        │            │        ├──────────►│         │
        │            │ Client │           │ Server  │
        │            └────────┤           └─────────┘
        │                     │
        │            ┌────────┤           ┌─────────┐
        │            │OTLP    │ OTLP/HTTP │ OTLP    │
        │  Agent     │        ├──────────►│         │
        │            │Exporter│           │ Receiver│
        │            └────────┤           └─────────┘
        │                     │
        │            ┌────────┤
        │            │Other   ├──────────► Other
        │            │Clients ├──────────► Destinations
        └────────────┴────────┘
```

この仕様書は、OpAMPネットワークプロトコルと、OpAMP AgentおよびServerに期待される振る舞いを定義します。OTLP/HTTPプロトコルの仕様については[OTLP](https://opentelemetry.io/docs/specs/otlp/)を参照してください。Agentが他の宛先に接続する際に使うプロトコルはAgentの種類ごとに異なり、この仕様書の対象外です。

OpAMPプロトコルは、プレーンHTTP接続とWebSocket接続という2つのサポート対象トランスポートのいずれかで動作します。Server実装はプレーンHTTP接続とWebSocket接続の両方を受け付けるべき（SHOULD）です。OpAMP Client実装は、それぞれの必要に応じてプレーンHTTPまたはWebSocketのいずれかのトランスポートのみをサポートすることを選んでもかまいません。

OpAMP ClientはAgentの代わりにOpAMP Serverに接続します。通常、1つのServerが多数のClientからの接続を受け付けます。Agentは、自己割り当てまたはServer割り当てのグローバルに一意なインスタンス識別子（instance_uid、以下同様）で識別されます。instance_uidは、Client経由でAgentからServerへ送られる各メッセージ、およびServerからAgentへ送られる各メッセージに記録されます。

接続のデフォルトのURLパスは/v1/opampです。URLパスはClient側・Server側で設定可能でもよい（MAY）ものとします。

OpAMPエンドポイントのデフォルトのポートは4320です。OpAMP Server実装は、デフォルトでこのポートをリスンするべき（SHOULD）です。

Agent側でOpAMPを実装する典型的な方法の一つは、Agentプロセスを制御する補助的なSupervisorプロセスを持つことです。Supervisorは、通常OpAMP Serverとの通信も担います。

```
      ┌───────────────────┐
      │ Supervisor        │
      │                   │
      │          ┌────────┤           ┌────────┐
      │          │ OpAMP  │  OpAMP    │ OpAMP  │
      │          │        ├──────────►│        │
      │          │ Client │           │ Server │
      └────┬─────┴────────┘           └────────┘
           │
           │
           ▼
      ┌──────────┐
      │          │
      │  Agent   │
      │          │
      └──────────┘
```

OpAMPの仕様書は、Supervisorの使用を必須とはしておらず、SupervisorとAgentがどのように通信するかも定義していません。

この仕様書の以降の記述では、_Agent_という語を、Supervisorがその一部を構成しているかどうかに関わらず、OpAMPのクライアント側を実装しているエンティティを指す語として使います。

### WebSocketトランスポート

OpAMPプロトコルがサポートするトランスポートの1つが[WebSocket](https://datatracker.ietf.org/doc/html/rfc6455)です。OpAMP ClientはWebSocketクライアントであり、ServerはWebSocket Serverです。ClientとServerは、バイナリデータのWebSocketメッセージを使って通信します。各WebSocketメッセージの内容は、エンコードされた`header`に続けて[バイナリエンコードされたProtobuf](https://developers.google.com/protocol-buffers/docs/encoding)の`data`メッセージが続く形になります（[WebSocketメッセージのフォーマット](#websocketメッセージのフォーマット)を参照）。

Agentの代わりに、ClientはAgentToServerメッセージデータを送信し、ServerはServerToAgentのProtobufメッセージデータを送信します。

```
        ┌────────────\ \────────┐                        ┌──────────────┐
        │            / /        │   Data:AgentToServer   │              │
        │            \ \ OpAmp  ├───────────────────────►│              │
        │     Agent  / /        │                        │    Server    │
        │            \ \ Client │   Data:ServerToAgent   │              │
        │            / /        │◄───────────────────────┤              │
        └────────────\ \────────┘                        └──────────────┘
```

#### WebSocketメッセージのフォーマット

各WebSocketメッセージのフォーマットは以下のとおりです。

```
        ┌────────────┬────────────────────────────────────────┬───────────────────┐
        │ header     │ Varint encoded unsigned 64 bit integer │ 1-10 bytes        │
        ├────────────┼────────────────────────────────────────┼───────────────────┤
        │ data       │ Encoded Protobuf message,              │ 0 or more bytes   │
        │            │ either AgentToServer or ServerToAgent  │                   │
        └────────────┴────────────────────────────────────────┴───────────────────┘
```

デコード前の`header`は64ビットの符号なし整数です。WebSocketメッセージの中では、この64ビットの`header`値は[Base 128 Varint](https://developers.google.com/protocol-buffers/docs/encoding#varints)形式でバイト列にエンコードされます。エンコードされた`header`が使うバイト数は、デコード前の`header`の値によって決まり、1バイトから10バイトの間になります。

この版の仕様書では、デコード前の`header`の値は0に固定されています。それ以外の`header`の値はすべて将来の使用のために予約されています。そのような値は将来のOpAMP仕様書のバージョンで定義されます。この仕様書に準拠したOpAMP WebSocketメッセージデコーダーは、`header`の値が0であることを確認するべき（SHOULD）であり、0でない場合はそのWebSocketメッセージが不正な形式であると見なすべき（SHOULD）です。

`data`フィールドには、[Protobufバイナリワイヤーフォーマット](https://developers.google.com/protocol-buffers/docs/encoding)でエンコードされたAgentToServerまたはServerToAgentメッセージを表すバイト列が含まれます。

`header`と`data`の両フィールドとも、可変長のバイト列を含むことに注意してください。`header`をデコードするBase 128 Varintアルゴリズムは、読み取ったバイト列から自分がいつ停止すべきかを判断できます。

`data`フィールドをProtobufのデコードロジックでデコードするには、実装は`data`フィールドのバイト数を知る必要があります。これを算出するために、実装はWebSocketメッセージ全体のバイト数から`header`のバイト数を差し引く必要があります（MUST）。

Protobufワイヤーフォーマットの設計上、エンコードされたAgentToServerまたはServerToAgentメッセージが空（すべてのフィールドが未設定）である場合、`data`のバイト数は0になることがあります。これは有効な状態です。

#### WebSocketメッセージの交換

OpAMP over WebSocketは、非同期・全二重のメッセージ交換プロトコルです。OpAMP ClientとServerが交換するメッセージの順序とシーケンスは、この仕様書内の各ケーパビリティに対応する節でそれぞれ定義されています。

シーケンスは通常、何らかの外部イベントによって発生する起点メッセージから開始されます。例えば、接続が確立された後にClientがAgentToServerメッセージを送信する場合、「接続の確立」が起点イベントであり、AgentToServerが起点メッセージとなります。

ClientとServerのどちらも、起点メッセージを送信してシーケンスを開始できます。

起点メッセージは、受信者に1つ以上のメッセージを返信させることがあり、それがさらに反対方向のメッセージを引き起こす、という形で続いていきます。この双方向のメッセージ交換は、交換の目標が達成されるか、シーケンスがエラーで失敗するまで続きます。

同一のメッセージが、あるケースではシーケンスの起点メッセージとなり、別のケースでは他のメッセージへの応答として発生することがある点に注意してください。OpAMPの他のプロトコルとは異なり、「リクエスト」と「レスポンス」というメッセージ種別の間に厳密な区別はありません。メッセージの役割は、そのシーケンスがどのように開始されたかに依存します。

例えばAgentToServerメッセージは、Clientが初めてServerに接続する際にClientが送信する起点メッセージとなることがあります。また、AgentToServerメッセージは、Serverがリモート設定のオファーをAgentに送り、Agentがその設定を受け入れたことを報告する際の応答として送信されることもあります。

Clientがハートビートを送信できる場合、ClientはReportsHeartbeatケーパビリティを設定するべき（SHOULD）です。ReportsHeartbeatケーパビリティが設定されている場合、Clientは定期的にハートビートを送信するべき（SHOULD）です。ハートビートの間隔は、Client側で別の値が設定されていない限り、あるいはServerが`OpAMPConnectionSettings.heartbeat_interval_seconds`フィールドを通じて別の間隔を提示していない限り、30秒であるべき（SHOULD）です。

メッセージシーケンスの詳細は、[動作](#動作)節以下の各節を参照してください。

WebSocketトランスポートは、HTTPトランスポート使用時のようにClientがServerをポーリングするのを待たずに、ServerからAgentへ即座に通信する能力が必要な場合に典型的に使われます（詳細は後述）。

### プレーンHTTPトランスポート

OpAMPプロトコルがサポートする2つ目のトランスポートは、プレーンHTTP接続です。OpAMP ClientはHTTPクライアントであり、ServerはHTTPサーバーです。ClientはServerに対してPOSTリクエストを送ります。POSTリクエストとレスポンスのボディは、[バイナリシリアライズされたProtobuf](https://developers.google.com/protocol-buffers/docs/encoding)メッセージです。ClientはリクエストボディにAgentToServerのProtobufメッセージを送信し、ServerはレスポンスボディにServerToAgentのProtobufメッセージを送信します。

OpAMP over HTTPは、同期的・半二重のメッセージ交換プロトコルです。Clientは、配送すべきAgentToServerメッセージがあるときにHTTPリクエストを開始します。ServerはAgentに配送したいServerToAgentメッセージを、各HTTPリクエストへの応答として返します。Agentが配送するものを何も持たない場合、Clientはinstance_uidフィールドのみを設定した[instance_uid](#agenttoserverinstance_uid)のAgentToServerメッセージを送信して、Serverを定期的にポーリングしなければなりません（MUST）。これにより、Serverが（新しいリモート設定などの）配送したいメッセージをレスポンスとして返す機会を得られます。

Agentが配送するものを持たない場合のデフォルトのポーリング間隔は30秒です。このポーリング間隔はClient側で設定可能であるべき（SHOULD）です。Clientが以前にOpAMP接続設定を受け取り、それを受け入れている場合は、`OpAMPConnectionSettings.heartbeat_interval_seconds`の値がポーリング間隔として使われるべき（SHOULD）です。

HTTPトランスポートを使う場合のメッセージのシーケンスは、WebSocketトランスポートを使う場合とまったく同じです。唯一の違いはタイミングにあります。

- Serverがメッセージをagentに送信したい場合、Serverは、Clientがサーバーをポーリングし、Serverのメッセージをレスポンスとして返せるHTTPリクエストを確立するまで待つ必要があります。
- Agentがメッセージをサーバーに送信したいが、すでに応答されていないリクエストをServerに送信済みの場合、Clientはそのレスポンスを受け取るまで新しいリクエストの送信を待たなければなりません（MUST）。なお、この場合の新しいリクエストは、直前のレスポンスを受け取った直後に送信できます。Clientはリクエストの間のポーリング期間を待つ必要はありません。

プレーンHTTPトランスポートを使う場合、Clientは「Content-Type: application/x-protobuf」というリクエストヘッダーを設定しなければなりません（MUST）。Serverはこのヘッダーが設定されたHTTPリクエストを受信した場合、それをプレーンHTTPトランスポートのリクエストと見なすべき（SHOULD）であり、そうでなければWebSocketトランスポートの開始要求と見なすべき（SHOULD）です。

Clientは、リクエストボディをgzipメソッドで圧縮してもよく（MAY）、その場合は「Content-Encoding: gzip」を指定しなければなりません（MUST）。Server実装は「Content-Encoding」ヘッダーを尊重し、gzip圧縮されたリクエストボディと非圧縮のリクエストボディの両方をサポートしなければなりません（MUST）。

Clientは、instance_uidフィールドの値を、UUIDの[標準的な文字列表現](https://datatracker.ietf.org/doc/html/rfc4122#section-3)にエンコードして「OpAMP-Instance-UID」リクエストヘッダーに設定するべき（SHOULD）です（例:「OpAMP-Instance-UID: f81d4fae-7dec-11d0-a765-00a0c91e6bf6」）。この値は、AgentToServerメッセージの[instance_uid](#agenttoserverinstance_uid)フィールドの値と等しくなります。

Clientが「Accept-Encoding」ヘッダーで圧縮されたレスポンスを受け入れられることを示した場合、Serverはレスポンスを圧縮するべき（SHOULD）です。

### AgentToServerとServerToAgentのメッセージ

#### AgentToServer Message

OpAMPのWebSocketメッセージの本体、またはHTTPリクエストのボディは、以下のように定義されるバイナリシリアライズされたProtobufメッセージAgentToServerです（この文書中のすべてのメッセージは[Protobuf 3言語](https://developers.google.com/protocol-buffers/docs/proto3)で定義されています）。

```protobuf
message AgentToServer {
    bytes instance_uid = 1;
    uint64 sequence_num = 2;
    AgentDescription agent_description = 3;
    uint64 capabilities = 4;
    ComponentHealth health = 5;
    EffectiveConfig effective_config = 6;
    RemoteConfigStatus remote_config_status = 7;
    PackageStatuses package_statuses = 8;
    AgentDisconnect agent_disconnect = 9;
    uint64 flags = 10;
    ConnectionSettingsRequest connection_settings_request = 11; // Status: [Development]
    CustomCapabilities custom_capabilities = 12; // Status: [Development]
    CustomMessage custom_message = 13; // Status: [Development]
    AvailableComponents available_components = 14; // Status: [Development]
    ConnectionSettingsStatus connection_settings_status = 15; // Status: [Development]
}
```

Serverは、[動作](#動作)節で対応する記述に従って各フィールドを処理するべきです。

##### AgentToServer.instance_uid

instance_uidフィールドは、稼働中のAgentインスタンスをグローバルに一意に識別する識別子です。Agentはこの識別子を自己生成するべき（SHOULD）であり、他のAgentが生成する識別子と競合しないよう最善を尽くすべきです。instance_uidは、Agentプロセスの生存期間を通じて変わらないままであるべき（SHOULD）です。instance_uidは16バイトでなければならず（MUST）、[UUID v7仕様](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-14.html#name-uuid-version-7)を使って生成されるべき（SHOULD）です。

Agentが、Serverによって生成された識別子を使いたい場合、このフィールドには一時的な値を設定するべき（SHOULD）で、かつRequestInstanceUidフラグを設定しなければなりません（MUST）。

##### AgentToServer.sequence_num

シーケンス番号は、Clientが送信するAgentToServerメッセージごとに1ずつ増加します。これにより、sequence_numが直前に受信した値より正確に1大きくなっていないことにServerが気付いた場合、Serverはメッセージを取り損なったことを検出できます。詳細は[エージェントステータスの圧縮](#エージェントステータスの圧縮)を参照してください。

##### AgentToServer.agent_description

Agentの種類や稼働場所などを記述するデータです。詳細は[AgentDescription](#agentdescription-message)メッセージを参照してください。このフィールドは、直前のAgentToServerメッセージ以降この情報が変化していない場合は未設定であるべき（SHOULD）です。

##### AgentToServer.capabilities

AgentCapabilities列挙型で定義されたフラグのビットマスクです。AgentCapabilities列挙型に定義されていないビットはすべて、Clientによって0に設定されなければなりません（MUST）。これにより、将来AgentCapabilities列挙型を拡張してプロトコルを拡張しても、古いAgentは自動的に新しいケーパビリティをサポートしていないと報告することになります。このフィールドは常に設定されなければなりません（MUST）。

Agentは、最初のメッセージ以降いつでも自身のケーパビリティを更新してもよい（MAY）ものとします。Serverは、以降のメッセージが正しく送信されるように、Agentの変更を尊重しなければなりません（MUST）。Serverが、Agentがサポートしないケーパビリティに対応するメッセージの一部を送信した場合、Agentはそれを無視するべき（SHOULD）です。

```protobuf
enum AgentCapabilities {
    // The capabilities field is unspecified.
    UnspecifiedAgentCapability = 0;
    // The Agent can report status. This bit MUST be set, since all Agents MUST
    // report status.
    ReportsStatus                   = 0x00000001;
    // The Agent can accept remote configuration from the Server.
    AcceptsRemoteConfig             = 0x00000002;
    // The Agent will report EffectiveConfig in AgentToServer.
    ReportsEffectiveConfig          = 0x00000004;
    // The Agent can accept package offers.
    // Status: [Beta]
    AcceptsPackages                 = 0x00000008;
    // The Agent can report package status.
    // Status: [Beta]
    ReportsPackageStatuses          = 0x00000010;
    // The Agent can report own trace to the destination specified by
    // the Server via ConnectionSettingsOffers.own_traces field.
    // Status: [Beta]
    ReportsOwnTraces                = 0x00000020;
    // The Agent can report own metrics to the destination specified by
    // the Server via ConnectionSettingsOffers.own_metrics field.
    // Status: [Beta]
    ReportsOwnMetrics               = 0x00000040;
    // The Agent can report own logs to the destination specified by
    // the Server via ConnectionSettingsOffers.own_logs field.
    // Status: [Beta]
    ReportsOwnLogs                  = 0x00000080;
    // The can accept connections settings for OpAMP via
    // ConnectionSettingsOffers.opamp field.
    // Status: [Beta]
    AcceptsOpAMPConnectionSettings  = 0x00000100;
    // The can accept connections settings for other destinations via
    // ConnectionSettingsOffers.other_connections field.
    // Status: [Beta]
    AcceptsOtherConnectionSettings  = 0x00000200;
    // The Agent can accept restart requests.
    // Status: [Beta]
    AcceptsRestartCommand           = 0x00000400;
    // The Agent will report Health via AgentToServer.health field.
    ReportsHealth                   = 0x00000800;
    // The Agent will report RemoteConfig status via AgentToServer.remote_config_status field.
    ReportsRemoteConfig             = 0x00001000;
    // The Agent can report heartbeats.
    // This is specified by the ServerToAgent.OpAMPConnectionSettings.heartbeat_interval_seconds field.
    // If this capability is true, but the Server does not set a heartbeat_interval_seconds field, the
    // Agent should use its own configured interval, which by default will be 30s. The Server may not
    // know the configured interval and should not make assumptions about it.
    // Status: [Development]
    ReportsHeartbeat                = 0x00002000;
    // The agent will report AvailableComponents via the AgentToServer.available_components field.
    // Status: [Development]
    ReportsAvailableComponents      = 0x00004000;
    // The agent will report ConnectionSettingsOffers status via AgentToServer.connection_settings_status field.
    // Status: [Development]
    ReportsConnectionSettingsStatus = 0x00008000;

    // Add new capabilities here, continuing with the least significant unused bit.
}
```

##### AgentToServer.health

ステータス: [Beta]

Agentおよびサブコンポーネントの現在のヘルスです。トップレベルのComponentHealthは、Agent全体のヘルスを表します。直前のAgentToServerメッセージ以降変化がない場合は省略してもよい（MAY）ものとします。詳細は[ComponentHealth message](#componenthealth-message)を参照してください。

##### AgentToServer.effective_config

Agentの現在の実効設定です。実効設定とは、Agentが現在実際に使用している設定です。実効設定は、Agentがローカル設定を代わりに（または加えて）使用しているなどの理由で、以前にServerから受け取ったリモート設定とは異なる場合があります。詳細は[EffectiveConfig](#effectiveconfig-message)メッセージを参照してください。このフィールドは、直前のAgentToServerメッセージ以降この情報が変化していない場合は未設定であるべき（SHOULD）です。

##### AgentToServer.remote_config_status

以前にServerから受け取ったリモート設定のステータスです。詳細は[RemoteConfigStatus](#remoteconfigstatus-message)メッセージを参照してください。このフィールドは、直前のAgentToServerメッセージ以降この情報が変化していない場合は未設定であるべき（SHOULD）です。

##### AgentToServer.package_statuses

ステータス: [Beta]

パッケージステータスを含む、Agentのパッケージの一覧です。このフィールドは、直前のAgentToServerメッセージ以降この情報が変化していない場合は未設定であるべき（SHOULD）です。

##### AgentToServer.agent_disconnect

AgentDisconnectは、ClientからServerへ送信される最後のAgentToServerメッセージで設定されなければなりません（MUST）。

##### AgentToServer.flags

AgentToServerFlagsのビットマスクで定義されるビットフラグです。

```protobuf
enum AgentToServerFlags {
    FlagsUnspecified = 0;

    // Flags is a bit mask. Values below define individual bits.

    // The Agent requests Server go generate a new instance_uid, which will
    // be sent back in ServerToAgent message
    RequestInstanceUid     = 0x00000001;
}
```

##### AgentToServer.connection_settings_request

ステータス: [Development]

接続設定の作成を要求するフィールドです。Agentが接続設定の作成を開始するフローにおいて、このフィールドが設定されます。

詳細は[ConnectionSettingsRequest](#connectionsettingsrequest-message)メッセージを参照してください。

##### AgentToServer.custom_capabilities

ステータス: [Development]

Agentがサポートするカスタムケーパビリティを示すメッセージです。

詳細は[CustomCapabilities](#customcapabilities)メッセージを参照してください。

##### AgentToServer.custom_message

ステータス: [Development]

Agentからserverへ送信されるカスタムメッセージです。

詳細は[CustomMessage](#custommessage)メッセージを参照してください。

##### AgentToServer.available_components

ステータス: [Development]

Agent内で利用可能なコンポーネントを列挙するメッセージです。このフィールドは、ReportsAvailableComponentsケーパビリティが設定されている場合に限り報告されるべき（SHOULD）です。

詳細は[AvailableComponents](#availablecomponents-message)メッセージを参照してください。

##### AgentToServer.connection_settings_status

ステータス: [Development]

以前にServerから受信した接続設定のステータスです。詳細は[ConnectionSettingsStatus](#connectionsettingsstatus-message)メッセージを参照してください。このフィールドは、直前のAgentToServerメッセージ以降この情報が変化していない場合は未設定であるべき（SHOULD）です。このフィールドは[接続設定の管理](#接続設定の管理)ワークフローの一部です。

#### ServerToAgent Message

WebSocketメッセージの本体、またはHTTPレスポンスのボディは、バイナリシリアライズされたProtobufメッセージServerToAgentです。

ServerToAgentメッセージは、AgentToServerメッセージへの応答として、あるいはServerがAgentに配送すべきデータを持っているときに、ServerからAgentへ送信されます。

ServerがAgentToServerメッセージを受信し、Agentに送り返すデータを何も持っていない場合でも、ServerToAgentメッセージは送信されますが、instance_uid以外のフィールドはすべて未設定になります（この場合、ServerToAgentは単純に受信確認として機能します）。

ServerToAgentメッセージを受信すると、Agentはそれを処理しなければなりません（MUST）。必要な処理は、メッセージ内のどのフィールドが設定されているかによって決まります。詳細は、以下のフィールドの説明からこの仕様書の対応する節へのリンクを参照してください。

この処理の結果として、AgentはServerにステータスレポートを送信する必要が生じることがあります。Agentは、ServerToAgentメッセージの処理を完全に終えてから1つのステータスレポートを送信してもよく、あるいはServerToAgentメッセージの各部分を処理する都度、複数のステータスレポートを送信して進行状況を示してもかまいません（例えば[パッケージのダウンロード](#パッケージのダウンロード)を参照）。処理に長い時間がかかる場合、複数回のステータスレポートによってServerが状況を把握し続けられるようにするのが望ましいことがあります。

Serverは各ステータスレポートに対して、ServerToAgentメッセージ（または何か問題が起きた場合はServerErrorResponse）で応答することに注意してください。これらのServerToAgentメッセージは、以前に受信したものと同じ内容の場合もあれば、Server側の状況が変化していれば異なる内容になる場合もあります。Agentは、これらの追加のServerToAgentメッセージが届いたときにそれを処理する準備をしておくべき（SHOULD）です。

Clientは、Agentのステータスが処理の結果として変化しなかった場合、ステータスレポートを一切送信するべきではありません（SHOULD NOT）。

ServerToAgentメッセージの構造は以下のとおりです。

```protobuf
message ServerToAgent {
    bytes instance_uid = 1;
    ServerErrorResponse error_response = 2;
    AgentRemoteConfig remote_config = 3;
    ConnectionSettingsOffers connection_settings = 4; // Status: [Beta]
    PackagesAvailable packages_available = 5; // Status: [Beta]
    uint64 flags = 6;
    uint64 capabilities = 7;
    AgentIdentification agent_identification = 8;
    ServerToAgentCommand command = 9; // Status: [Beta]
    CustomCapabilities custom_capabilities = 10; // Status: [Development]
    CustomMessage custom_message = 11; // Status: [Development]
}
```

##### ServerToAgent.instance_uid

Agentのインスタンス識別子です。直前にAgentToServerメッセージで受信したinstance_uidフィールドと一致しなければなりません（MUST）。（終端プロキシが使われている場合など）複数のAgentとの通信が1つのWebSocket接続に多重化されている場合、instance_uidフィールドによって、そのServerToAgentメッセージがどのAgentに宛てられているかを区別できます。

注: この値は、AgentIdentificationフィールドで新しい値を送信することでServerによって上書きされることがあります。これが発生した場合、Agentは自身のinstance_uidを提供された値に更新し、以降のすべての通信でその値を使わなければなりません（MUST）。

##### ServerToAgent.error_response

error_responseは、AgentToServerメッセージの処理中に何か問題が起きたことをServerが示したい場合に設定されます。error_responseが設定されている場合、以下の他のフィールドはすべて未設定でなければならず、逆に以下のフィールドのいずれかが設定されている場合、error_responseは未設定でなければなりません。

##### ServerToAgent.remote_config

このフィールドは、Serverが該当Agentに対するリモート設定オファーを持っている場合に設定されます。詳細は[設定](#設定)を参照してください。

##### ServerToAgent.connection_settings

ステータス: [Beta]

このフィールドは、Serverが（宛先、ヘッダー、証明書などの）クライアント接続設定の1つ以上をAgentに変更してほしい場合に設定されます。詳細は[接続設定の管理](#接続設定の管理)を参照してください。

##### ServerToAgent.packages_available

ステータス: [Beta]

このフィールドは、ServerがAgentに提供するパッケージを持っている場合に設定されます。詳細は[パッケージ](#パッケージ)を参照してください。

##### ServerToAgent.flags

ServerToAgentFlagsのビットマスクで定義されるビットフラグです。

`Report*`フラグは、Clientが直前のAgentToServerメッセージに該当データの一部を含めなかった（これは許容される圧縮のやり方です）にもかかわらず、Serverがそのデータを持っていない場合（例えばServerが再起動してAgentのステータスを失った場合、[この節](#エージェントステータスの圧縮)を参照）に、Serverが使用できます。

```protobuf
enum Flags {
    FlagsUnspecified = 0;

    // Flags is a bit mask. Values below define individual bits.

    // ReportFullState flag can be used by the Server if the Client did not include
    // some sub-message in the last AgentToServer message (which is an allowed
    // optimization) but the Server detects that it does not have it (e.g. was
    // restarted and lost state). The detection happens using
    // AgentToServer.sequence_num values.
    // The Server asks the Agent to report the full status again by sending
    // a new, full AgentToServer message.
    ReportFullState = 0x00000001;
    // ReportAvailableComponents flag can be used by the server if the Agent did
    // not include the full AvailableComponents message, but only the hash.
    // If this flag is specified, the agent will populate available_components.components
    // with a full description of the agent's components.
    // Status: [Development]
    ServerToAgentFlags_ReportAvailableComponents = 0x00000002;
}
```

##### ServerToAgent.capabilities

ServerCapabilities列挙型で定義されたフラグのビットマスクです。ServerCapabilities列挙型に定義されていないビットはすべて、Serverによって0に設定されなければなりません（MUST）。これにより、将来ServerCapabilities列挙型を拡張してプロトコルを拡張しても、古いServerは自動的に新しいケーパビリティをサポートしていないと報告することになります。このフィールドは、Serverが送信する最初のServerToAgentメッセージで設定されなければならず（MUST）、以降のServerToAgentメッセージではUnspecifiedServerCapability値を設定することで省略してもよい（MAY）ものとします。

```protobuf
enum ServerCapabilities {
    // The capabilities field is unspecified.
    UnspecifiedServerCapability = 0;
    // The Server can accept status reports. This bit MUST be set, since all Server
    // MUST be able to accept status reports.
    AcceptsStatus                  = 0x00000001;
    // The Server can offer remote configuration to the Agent.
    OffersRemoteConfig             = 0x00000002;
    // The Server can accept EffectiveConfig in AgentToServer.
    AcceptsEffectiveConfig         = 0x00000004;
    // The Server can offer Packages.
    OffersPackages                 = 0x00000008;
    // The Server can accept Packages status.
    // Status: [Beta]
    AcceptsPackagesStatus          = 0x00000010;
    // The Server can offer connection settings.
    // Status: [Beta]
    OffersConnectionSettings       = 0x00000020;
    // The Server can accept ConnectionSettingsRequest and respond with an offer.
    // Status: [Development]
    AcceptsConnectionSettingsRequest = 0x00000040;

    // Add new capabilities here, continuing with the least significant unused bit.
}
```

##### ServerToAgent.agent_identification

必要に応じてServerが上書きできる、Agentの識別に関するプロパティです。new_instance_uidが設定されている場合、Agentは自身のinstance_uidを提供された値に更新し、以降のすべての通信でその値を使わなければなりません（MUST）。new_instance_uidは16バイトでなければならず（MUST）、[UUID v7仕様](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-14.html#name-uuid-version-7)を使って生成されるべき（SHOULD）です。

```protobuf
message AgentIdentification {
  bytes new_instance_uid = 1;
}
```

##### ServerToAgent.command

ステータス: [Beta]

このフィールドは、ServerがAgentに再起動を行わせたい場合に設定されます。このフィールドは、instance_uidまたはcapabilities以外のフィールドと同時に設定してはなりません。それ以外のフィールドはすべて無視され、Agentはコマンドを実行します。詳細は[ServerToAgentCommand Message](#servertoagentcommand-message)を参照してください。

##### ServerToAgent.custom_capabilities

ステータス: [Development]

Serverがサポートするカスタムケーパビリティを示すメッセージです。

詳細は[CustomCapabilities](#customcapabilities)メッセージを参照してください。

##### ServerToAgent.custom_message

ステータス: [Development]

ServerからAgentへ送信されるカスタムメッセージです。

詳細は[CustomMessage](#custommessage)メッセージを参照してください。

#### ServerErrorResponse Message

このメッセージの構造は以下のとおりです。

```protobuf
message ServerErrorResponse {
    enum Type {
        UNKNOWN = 0;
        BAD_REQUEST = 1;
        UNAVAILABLE = 2
    }
    Type type = 1;
    string error_message = 2;
    oneof Details {
        RetryInfo retry_info = 3;
    }
}
```

##### ServerErrorResponse.type

このフィールドは、Agentのリクエストを処理しようとした際にServerが遭遇したエラーの種類を定義します。取り得る値は以下です。

UNKNOWN: 不明なエラー。何か問題が発生しましたが、それが正確に何であるかは分かりません。error_messageフィールドに問題の説明が含まれることがあります。

BAD_REQUEST: 直前に受信したAgentToServerメッセージへの応答としてのみ送信され、そのAgentToServerメッセージが不正な形式であったことを示します。[不正なリクエスト](#不正なリクエスト)の処理を参照してください。

UNAVAILABLE: Serverが過負荷状態にあり、リクエストを処理できません。[スロットリング](#スロットリング)を参照してください。

##### ServerErrorResponse.error_message

エラーメッセージです。通常は人間が読める形式です。

##### ServerErrorResponse.retry_info

type==UNAVAILABLEの場合の、再試行に関する追加の[RetryInfo](#スロットリング)メッセージです。

### ServerToAgentCommand Message

ステータス: [Beta]

このメッセージの構造は以下のとおりです。

```protobuf
// ServerToAgentCommand is sent from the Server to the Agent to request that the Agent
// perform a command.
message ServerToAgentCommand {
    enum CommandType {
        // The Agent should restart. This request will be ignored if the Agent does not
        // support restart.
        Restart = 0;
    }
    CommandType type = 1;
}
```

ServerToAgentCommandメッセージは、ServerがAgentに再起動してほしいときに送信されます。このメッセージには、command、instance_uid、capabilitiesフィールドのみを含めなければなりません。それ以外のフィールドはすべて無視されます。

## 動作

### ステータスレポート

Clientは以下の場合にステータスレポートを送信しなければなりません（MUST）。

* Serverへの接続直後の初回。このステータスレポートは、Clientが送信する最初のメッセージでなければなりません（MUST）。WebSocket Clientが以前の接続が閉じられた後に再接続する場合、そのステータスレポートは、前回の接続で送信されたメッセージ以降変化していないステータスフィールドを省略してもよい（MAY）ものとします。
* その後は、Agentのステータスが変化するたびに。

ステータスレポートは、[AgentToServer](#agenttoserver-message)メッセージとして送信されます。このメッセージ内の次のフィールドは、対応するステータスの部分を反映するために設定できます。agent_description、capabilities、health、effective_config、remote_config_status、package_statusesです。

Serverは、[ServerToAgent](#servertoagent-message)メッセージを送信することでAgentToServerメッセージに応答しなければなりません（MUST）。

ステータスレポートの処理が失敗した場合、[error_response](#servertoagenterror_response)フィールドがServerErrorResponseメッセージに設定されなければなりません（MUST）。

ステータスレポートがServerによって正常に処理された場合、[error_response](#servertoagenterror_response)フィールドは未設定でなければならず（MUST）、他のフィールドは必要に応じて設定できます。

以下は、（サーバー側の処理が成功したと仮定した場合の）ステータスレポートの動作を示すシーケンス図です。

```
        Client                                  Server

          │                                       │
          │                                       │
          │          WebSocket Connect            │
          ├──────────────────────────────────────►│
          │                                       │
          │           AgentToServer               │   ┌─────────┐
          ├──────────────────────────────────────►├──►│         │
          │                                       │   │ Process │
          │           ServerToAgent               │   │ Status  │
          │◄──────────────────────────────────────┤◄──┤         │
          │                                       │   └─────────┘
          .                 ...                   .

          │           AgentToServer               │   ┌─────────┐
          ├──────────────────────────────────────►├──►│         │
          │                                       │   │ Process │
          │           ServerToAgent               │   │ Status  │
          │◄──────────────────────────────────────┤◄──┤         │
          │                                       │   └─────────┘
          │                                       │
```

Agentのステータスは、Serverからのメッセージを受信した結果として変化することがある点に注意してください。例えば、ServerがAgentにリモート設定を送信したとします。Agentがそのようなリクエストを処理すると、Agentのステータスが変化します（例えば、Agentの実効設定が変わります）。そのようなステータス変化があれば、Clientはそれに応じてServerにステータスレポートを送信するべきです。

したがって、このような場合のメッセージのシーケンスは、基本的に以下のようになります。

```
          Agent   Client                                  Server

            │       │         ServerToAgent                 │
            ┌───────┤◄──────────────────────────────────────┤
            │       │                                       │
            ▼       │                                       │
        ┌────────┐  │                                       │
        │Process │  │                                       │
        │Received│  │                                       │
        │Data    │  │                                       │
        └───┬────┘  │                                       │
            │       │                                       │
            │Status │                                       │
            │Changed│         AgentToServer                 │   ┌─────────┐
            └──────►├──────────────────────────────────────►├──►│         │
                    │                                       │   │ Process │
                    │         ServerToAgent                 │   │ Status  │
                    │◄──────────────────────────────────────┤◄──┤         │
                    │                                       │   └─────────┘
```

Clientは、ServerToAgentメッセージを受信した際、Serverから受信したメッセージの処理がAgentのステータスの実際の変化（例えばAgentの設定変更）をもたらさない限り、ステータスレポートを送信してはなりません（MUST NOT）。この場合のシーケンス図は以下のようになります。

```
              Agent  Client                                  Server

                │      │         ServerToAgent                 │
                ┌──────┤◄──────────────────────────────────────┤
                │      │                                       │
                ▼      │                                       │
            ┌────────┐ │                                       │
            │Process │ │                                       │
            │Received│ │                                       │
            │Data    │ │                                       │
            └───┬────┘ │                                       │
                │      │                                       │
                ▼      │                                       │
             No Status │                                       │
              Changes  │                                       │
                       │                                       │
                       │                                       │
```

重要: Clientがこれらの規則に従わない場合、ClientとServerの間で無限にメッセージが往復するループが発生する可能性があります。

#### エージェントステータスの圧縮

Clientは、AgentToServerメッセージを送信することでAgentのステータスをServerに通知します。ステータスには、例えばAgentの記述、実効設定、Serverから受信したリモート設定のステータス、パッケージのステータスなどが含まれます。Serverは、AgentToServerメッセージから参照されるサブメッセージ内のデータを使って、Agentのステータスを追跡します。

Clientは、その特定のデータが前回報告されて以降変化していないサブメッセージを省略することで、AgentToServerメッセージを圧縮してもよい（MAY）ものとします。この圧縮の対象となり得るサブメッセージは、[AgentDescription](#agentdescription-message)、[ComponentHealth](#componenthealth-message)、[EffectiveConfig](#effectiveconfig-message)、[RemoteConfigStatus](#remoteconfigstatus-message)、[PackageStatuses](#packagestatuses-message)、[CustomCapabilities](#customcapabilities)、[AvailableComponents](#availablecomponents-message)、[ConnectionSettingsStatus](#connectionsettingsstatus-message)です。

圧縮は、AgentToServerメッセージ内でサブメッセージを省略することによって行われます。サブメッセージ内のいずれかのフィールドが変化した場合、その特定のサブメッセージには圧縮を使用できず、関連するすべてのフィールドを含むサブメッセージが存在しなければなりません（MUST）。

すべてのAgentToServerメッセージがServerに確実に配送され、Serverがそれらを正しく処理する場合、この圧縮は安全であり、Serverは常にAgentの正しい最新ステータスを保持しているはずです。

しかし、ClientとServerが同期を失い、Clientは自分が送信したデータをServerが持っていると思い込んでいるが実際にはServerが持っていないという状況が発生することがあります。これは例えば、Clientは動作を継続しつつServerが再起動され、その間にClientが送信したAgentToServerメッセージをServerが一時的にダウンしていて受信できなかった場合に発生し得ます。

この状況を検出して復旧するために、AgentToServerメッセージにはsequence_numフィールドが含まれます。このフィールドは、Clientが新しく送信するAgentToServerメッセージができるたびに増加する整数です。

Serverが受信したAgentToServerメッセージのsequence_numフィールドの値が、直前に受信したsequence_numの値より正確に1大きくない場合、Serverはそれによって、AgentToServerメッセージの完全なステータスを持っていないことを認識します。

この状況が発生した場合、失われたステータスを復旧するために、Serverは省略されたデータをAgentに報告させるよう要求しなければなりません（MUST）。この要求を行うために、Serverは[ServerToAgentメッセージ](#servertoagent-message)の[flags](#servertoagentflags)フィールドに`ReportFullState`ビットを設定して、AgentにServerToAgentメッセージを送信しなければなりません（MUST）。

#### AgentDescription Message

AgentDescriptionメッセージの構造は以下のとおりです。

```protobuf
message AgentDescription {
    repeated KeyValue identifying_attributes = 1;
    repeated KeyValue non_identifying_attributes = 2;
}
```

##### AgentDescription.identifying_attributes

Agentを識別する属性です。

キーと値は、OpenTelemetryの[resourceセマンティック規約](https://opentelemetry.io/docs/specs/semconv/resource/)に従います。

Agentは、これらの属性を自身のテレメトリーのResourceにも含めるべき（SHOULD）です。識別属性の組み合わせは、Agentが自身のテレメトリーを送信する宛先システムにおいて、そのAgent自身のテレメトリーを一意に識別できるだけの十分な情報を持つべき（SHOULD）です。

##### AgentDescription.non_identifying_attributes

Agentを必ずしも識別するわけではないが、Agentがどこで動いているかを説明する助けとなる属性です。

以下は、非識別属性として適した属性の例です。

- Agentがどこで動作しているかを表すos.\*。
- Agentが動作しているホストを表すhost.\*。
- ホストが配置されているクラウドを表すcloud.\*。
- このAgentとその実行環境を記述するその他の関連するResource属性。
- エンドユーザーがこのAgentに関連付けたいと考える任意のユーザー定義属性。

#### ComponentHealth Message

ステータス: [Beta]

ComponentHealthメッセージの構造は以下のとおりです。

```protobuf
message ComponentHealth {
    bool healthy = 1;
    fixed64 start_time_unix_nano = 2;
    string last_error = 3;
    string status = 4;
    fixed64 status_time_unix_nano = 5;
    map<string, ComponentHealth> component_health_map = 6;
}
```

##### ComponentHealth.healthy

Agentが起動しており正常であればtrueに設定します。

##### ComponentHealth.start_time_unix_nano

Agentが起動している間、すなわちAgentが起動した時点からのタイムスタンプです。値は1970年1月1日00:00:00 UTCからのUNIXエポック時間（ナノ秒）です。Agentが動作していない場合は0に設定されなければなりません（MUST）。

##### ComponentHealth.last_error

Agentがエラー状態にある場合の、人間が読めるエラーメッセージです。healthy==falseの場合に設定されるべき（SHOULD）です。

##### ComponentHealth.status

文字列として表現されたコンポーネントのステータスです。ステータス値は、プロトコルレベルではなく、Agent固有のセマンティクスによって定義されます。

##### ComponentHealth.status_time_unix_nano

コンポーネントのステータスが観測された時刻です。値は1970年1月1日00:00:00 UTCからのUNIXエポック時間（ナノ秒）です。

##### ComponentHealth.component_health_map

より粒度の細かいサブコンポーネントのヘルスを格納するマップです。基盤となるシステムを記述するために必要な深さでネストできます。

#### EffectiveConfig Message

EffectiveConfigメッセージの構造は以下のとおりです。

```protobuf
message EffectiveConfig {
    AgentConfigMap config_map = 1;
}
```

##### EffectiveConfig.config_map

Agentの実効設定です。

AgentConfigMapメッセージの定義については、[設定](#設定)節を参照してください。

#### RemoteConfigStatus Message

RemoteConfigStatusメッセージの構造は以下のとおりです。

```protobuf
message RemoteConfigStatus {
    bytes last_remote_config_hash = 1;
    enum Status {
        // The value of status field is not set.
        UNSET = 0;

        // Remote config was successfully applied by the Agent.
        APPLIED = 1;

        // Agent is currently applying the remote config that it received earlier.
        APPLYING = 2;

        // Agent tried to apply the config received earlier, but it failed.
        // See error_message for more details.
        FAILED = 3;
    }
    Status status = 2;
    string error_message = 3;
}
```

##### RemoteConfigStatus.last_remote_config_hash

このAgentがAgentRemoteConfig.config_hashフィールドで最後に受信したリモート設定のハッシュです。Serverは、このハッシュを自身が保持するそのAgent向けの設定ハッシュと比較するべき（SHOULD）であり、ハッシュが異なる場合はServerはServerToAgentメッセージのレスポンスにremote_configフィールドを含めなければなりません（MUST）。

##### RemoteConfigStatus.status

以前に受信したリモート設定を適用しようとしたAgentの試みのステータスです。

##### RemoteConfigStatus.error_message

status==FAILEDの場合の任意のエラーメッセージです。

#### ConnectionSettingsStatus Message

ConnectionSettingsStatusメッセージの構造は以下のとおりです。

```protobuf
message ConnectionSettingsStatus {
    bytes last_connection_settings_hash = 1;
    enum Status {
        // The value of status field is not set.
        UNSET = 0;

        // offered connection settings were successfully applied by the Agent.
        APPLIED = 1;

        // Agent is currently applying the offered connection settings that it received earlier.
        APPLYING = 2;

        // Agent tried to apply the offered connection settings recieved earlier, but it failed.
        // See error_message for more details.
        FAILED = 3;
    }
    Status status = 2;
    string error_message = 3;
}
```

##### ConnectionSettingsStatus.last_connection_settings_hash

このAgentがconnection_settings.hashフィールドで最後に受信した、提示された接続設定のハッシュです。Serverは、このハッシュを自身が保持するそのAgent向けの設定ハッシュと比較するべき（SHOULD）であり、ハッシュが異なる場合はServerはServerToAgentメッセージのレスポンスにconnection_settingsフィールドを含めなければなりません（MUST）。

##### ConnectionSettingsStatus.status

以前に受信した接続設定を適用しようとしたAgentの試みのステータスです。

##### ConnectionSettingsStatus.error_message

status==FAILEDの場合の任意のエラーメッセージです。

#### PackageStatuses Message

ステータス: [Beta]

PackageStatusesメッセージは、Agentが持っている、または提示されたすべてのパッケージのステータスを記述します。このメッセージの構造は以下のとおりです。

```protobuf
message PackageStatuses {
    map<string, PackageStatus> packages = 1;
    bytes server_provided_all_packages_hash = 2;
    string error_message = 3;
}
```

##### PackageStatuses.packages

PackageStatusメッセージのマップです。キーはパッケージ名です。キーは[PackageStatus](#packagestatus-message)メッセージのnameフィールドと一致しなければなりません（MUST）。

##### PackageStatuses.server_provided_all_packages_hash

このAgentが以前にPackagesAvailableメッセージを通じてServerから受信した全パッケージの集約ハッシュです。

Serverは、このハッシュを、このAgentについて自身が保持する全パッケージの集約ハッシュと比較するべき（SHOULD）であり、ハッシュが異なる場合はServerはAgentにPackagesAvailableメッセージを送信するべき（SHOULD）です。

##### PackageStatuses.error_message

このフィールドは、Agentが[PackagesAvailableメッセージ](#packagesavailable-message)を処理する際にエラーに遭遇し、そのエラーが特定の単一のパッケージに関連しない場合に設定されます。

処理エラーがなかった場合、このフィールドは未設定でなければなりません（MUST）。

#### PackageStatus Message

ステータス: [Beta]

PackageStatusの構造は以下のとおりです。

```protobuf
message PackageStatus {
    string name = 1;
    string agent_has_version = 2;
    bytes agent_has_hash = 3;
    string server_offered_version = 4;
    bytes server_offered_hash = 5;
    enum Status {
        INSTALLED = 0;
        INSTALL_PENDING = 1;
        INSTALLING = 2;
        INSTALL_FAILED = 3;
        DOWNLOADING = 4;
    }
    Status status = 6;
    string error_message = 7;
    PackageDownloadDetails download_details = 8;
}
```

##### PackageStatus.name

パッケージ名です。常に設定されなければならず（MUST）、PackageStatusesメッセージのpackagesフィールド内のキーと一致しなければなりません（MUST）。

##### PackageStatus.agent_has_version

Agentが持っているパッケージのバージョンです。

Agentがこのパッケージを持っている場合は設定されなければなりません（MUST）。

Agentがこのパッケージを持っていない場合は空でなければなりません（MUST）。これは例えば、パッケージがServerによって提示されたがインストールに失敗し、Agentが以前このパッケージを持っていなかった場合に該当します。

##### PackageStatus.agent_has_hash

Agentが持っているパッケージのハッシュです。

Agentがこのパッケージを持っている場合は設定されなければなりません（MUST）。

Agentがこのパッケージを持っていない場合は空でなければなりません（MUST）。これは例えば、パッケージがServerによって提示されたがインストールに失敗し、Agentが以前このパッケージを持っていなかった場合に該当します。

##### PackageStatus.server_offered_version

Serverからagentに提示されたパッケージのバージョンです。

このパッケージのインストールが、以前にServerから受けたインストールのオファーによって開始されたものである場合は設定されなければなりません（MUST）。

Agentがこのパッケージを持っているが、それがローカルにインストールされたものでServerから提示されたものではない場合は空でなければなりません（MUST）。

agent_has_versionとserver_offered_versionの両方のフィールドが設定され、かつ異なる値を持つことがある点に注意してください。これは例えば、Agentがすでにパッケージのあるバージョンを正常にインストール済みで、Serverが別のバージョンを提示したが、Agentがそのバージョンのインストールに失敗した場合に発生し得ます。

##### PackageStatus.server_offered_hash

Serverからagentに提示されたパッケージのハッシュです。

このパッケージのインストールが、以前にServerから受けたインストールのオファーによって開始されたものである場合は設定されなければなりません（MUST）。

Agentがこのパッケージを持っているが、それがローカルにインストールされたものでServerから提示されたものではない場合は空でなければなりません（MUST）。

agent_has_hashとserver_offered_hashの両方のフィールドが設定され、かつ異なる値を持つことがある点に注意してください。これは例えば、Agentがすでにパッケージのあるバージョンを正常にインストール済みで、Serverが別のバージョンを提示したが、Agentがそのバージョンのインストールに失敗した場合に発生し得ます。

##### PackageStatus.status

このパッケージのステータスです。取り得る値は以下です。

INSTALLED: パッケージがAgentによって正常にインストールされています。error_messageフィールドは設定してはなりません（MUST NOT）。

INSTALLING: Agentが現在パッケージをダウンロードおよびインストール中です。Agentがインストール中のバージョンを示すため、server_offered_hashフィールドが設定されなければなりません（MUST）。error_messageフィールドは設定してはなりません（MUST NOT）。

INSTALL_FAILED: Agentがパッケージのインストールを試みましたが失敗しました。Agentがインストールを試みたバージョンを示すため、server_offered_hashフィールドが設定されなければなりません（MUST）。error_messageには、失敗に関する詳細が含まれることもあります。

##### PackageStatus.error_message

ステータスがエラーの場合のエラーメッセージです。

##### PackageStatus.download_details

ステータス: [Development]

download_detailsには、パッケージのダウンロードを記述する追加の詳細情報が含まれます。statusが`DOWNLOADING`の場合にのみ設定されるべきです。

```protobuf
message PackageDownloadDetails {
  double download_percent = 1;
  double download_bytes_per_second = 2;
}
```

### 接続設定の管理

ステータス: [Beta]

OpAMPには、Agentが接続するすべての宛先の接続設定、およびOpAMP Client自身の接続設定をServerが管理できるようにする機能が含まれています。

以下の図は、OpAMP Serverによって管理され、自身のテレメトリーをOTLPバックエンドに送信し、さらに自身の作業のために他の宛先にも接続する典型的なAgentを示しています。

```
            ┌────────────┬────────┐           ┌─────────┐
            │            │ OpAMP  │  OpAMP    │ OpAMP   │
            │            │        ├──────────►│         │
            │            │ Client │           │ Server  │
            │            └────────┤           └─────────┘
            │                     │
            │            ┌────────┤           ┌─────────┐
            │            │OTLP    │ OTLP/HTTP │OTLP     │
            │  Agent     │        ├──────────►│Telemetry│
            │            │Client  │           │Backend  │
            │            └────────┤           └─────────┘
            │                     │
            │            ┌────────┤
            │            │Other   ├──────────► Other
            │            │        ├──────────►
            │            │Clients ├──────────► Destinations
            └────────────┴────────┘
```

OpAMP Serverや他の宛先に接続する際、Agent（またはAgentの代わりに接続するOpAMP Client）は、通常、ヘッダーベースの認可メカニズム（例えば「Authorization」HTTPヘッダーやカスタムヘッダー内のアクセストークン）を使い、オプションでTLS接続のためのクライアント側証明書（相互TLSとも呼ばれます）も使うことが期待されます。

OpAMPプロトコルでは、Serverはこれらの各接続についての設定を提示でき、Agentはそのオファーを受け入れるか拒否できます。このメカニズムは、Agentを特定の宛先に誘導すること、およびアクセストークンやTLS証明書の登録・失効・ローテーションのために使えます。

Serverは、以下の3つのクラスの宛先について接続設定を提示できます。

1. **OpAMP Server**自身。これは通常、TLS証明書や認可に使うリクエストヘッダーといったクレデンシャルを管理するために使われます。Serverは、OpAMP Clientに別のOpAMP Serverへ接続するよう指示するために、異なる宛先エンドポイントを提示してもよい（MAY）ものとします。
2. Agentが**自身のテレメトリー**（メトリクス、トレース、ログ）をOTLP/HTTPプロトコルで送信するための宛先。
3. 文字列名がそれぞれに関連付けられた、**その他の追加接続**設定の集合。Agentの種類がこれらをどう使うかはAgent固有です。通常、名前はAgentが認識している接続先の名前を表します。例えばOpenTelemetry Collectorは、それぞれ対応する名前を持つエクスポーターに対して、名前付き接続設定を1つずつ使えます。

Serverは、特定の接続クラスに対応するケーパビリティが、AgentToServer.capabilitiesフィールドを通じてAgentから報告されている場合に限り、その接続クラスに対するオファーを提示できます。

- ReportsOwnTracesケーパビリティビットが設定されている場合、Serverはown_tracesフィールドを使ってトレース用の接続設定を提示してもよい（MAY）ものとします。
- ReportsOwnMetricsケーパビリティビットが設定されている場合、Serverはown_metricsフィールドを使ってメトリクス用の接続設定を提示してもよい（MAY）ものとします。
- ReportsOwnLogsケーパビリティビットが設定されている場合、Serverはown_logsフィールドを使ってログ用の接続設定を提示してもよい（MAY）ものとします。
- AcceptsOpAMPConnectionSettingsケーパビリティビットが設定されている場合、Serverはopampフィールドを使ってOpAMP接続用の接続設定を提示してもよい（MAY）ものとします。
- AcceptsOtherConnectionSettingsケーパビリティビットが設定されている場合、Serverはother_connectionsフィールドを使ってその他の宛先向けの接続設定を提示してもよい（MAY）ものとします。

さらに、ReportsConnectionSettingsStatusケーパビリティは、提示された接続設定が期待どおりに適用されたかをAgentがServerに報告できることを示すために使われます。

どの接続設定が提示されるかによって、操作のシーケンスはわずかに異なります。自身のテレメトリー用の接続設定の扱いは[自身のテレメトリーのレポート](#自身のテレメトリーのレポート)で説明されています。「その他」の宛先向けの接続設定の扱いは[その他の宛先向け接続設定](#その他の宛先向け接続設定)で説明されています。OpAMP接続設定の扱いは以下で説明します。

また、Agentから接続設定の作成を開始するようServerにリクエストすることも可能です。このプロセスは[エージェント起動のCSRフロー](#エージェント起動のcsrフロー)節で説明されています。

#### OpAMP接続設定オファーのフロー

Server起動によるOpAMP接続設定の変更は、以下のように行われます。

```
                   Client                                 Server

                     │                                       │    Initiate
                     │    Connect                            │    Settings
                     ├──────────────────────────────────────►│     Change
                     │                 ...                   │        │
                     │                                       │◄───────┘
                     │                                       │          ┌───────────┐
                     │                                       ├─────────►│           │
                     │                                       │ Generate │Credentials│
┌───────────┐        │ServerToAgent{ConnectionSettingsOffers}│ and Save │   Store   │
│           │◄───────┤◄──────────────────────────────────────┤◄─────────┤           │
│Credentials│ Save   │                                       │          └───────────┘
│   Store   │        │             Disconnect                │
│           ├───────►├──────────────────────────────────────►│
└───────────┘        │                                       │
                     │    Connect, New settings              │          ┌───────────┐
                     ├──────────────────────────────────────►├─────────►│           │
                     │                                       │ Delete   │Credentials│
┌───────────┐        │    Connection established             │ old      │   Store   │
│           │◄───────┤◄─────────────────────────────────────►│◄─────────┤           │
│Credentials│Delete  │                                       │          └───────────┘
│   Store   │old     │                                       │
│           ├───────►│                                       │
└───────────┘        │                                       │

```

1. Serverは新しい接続設定を生成し、Agentインスタンスの UIDと関連付けてServerのクレデンシャルストアに保存します。
2. Serverは、[ConnectionSettingsOffers](#connectionsettingsoffers-message)メッセージを含むServerToAgentメッセージを送信します。[opamp](#connectionsettingsoffersopamp)フィールドには、提示される新しい[OpAMPConnectionSettings](#opampconnectionsettings)が含まれます。
3. Clientは設定オファーを受信し、更新された接続設定をローカルストアに保存し、「候補」としてマークします（Clientがクラッシュした場合、手順5〜9の候補検証を再試行します）。
4. Clientは Serverから切断します。ClientがReportsConnectionSettingsStatusケーパビリティを持っている場合、Clientは新しい接続設定を適用中であることを示すべき（SHOULD）です。
5. Clientは新しい設定を使ってServerに接続します。
6. 接続が正常に確立され、必要なTLS検証がすべて通過し、Serverが認可の成功を示します。
7. Serverは、（Agentインスタンス UIDを使って）このAgentの古い接続設定をクレデンシャルストアから削除します。
8. Clientは古い設定をクレデンシャルストアから削除し、新しい接続設定を「有効」としてマークします。ClientがReportsConnectionSettingsStatusケーパビリティを持っている場合、接続設定が適用されたことを報告しなければなりません（MUST）。
9. 手順6が失敗した場合、Clientは新しい設定を削除し、古い設定に戻して再接続します。ClientがReportsConnectionSettingsStatusケーパビリティを持っている場合、接続設定が失敗したことを報告しなければなりません（MUST）。

注: 新しい接続設定を永続化できず、一時的なストレージしか使えないClientは、証明書オファーを拒否するべき（SHOULD）です。そうしないと、再起動後にアクセスを失い、提示された証明書も失う可能性があります。

#### Trust On First Use

TLSをクライアント証明書とともに使いたいが、最初は証明書を持っていないOpAMP Clientは、Trust On First Use（TOFU）フローを使えます。このシーケンスは以下のとおりです。

* Clientは、クライアント証明書なしで（Serverの識別情報を検証する）通常のTLSを使ってServerに接続します。Clientは、自身を識別できるようにAgentのステータスレポートを送信します。
* Serverは接続とステータスを受け入れ、OpAMP Client用のクライアント証明書を生成する承認を待ちます。
* Serverは人間による手動の承認を待つか、あるいはServerがそのように設定されている場合はTOFUリクエストをすべて自動的に承認します（Server側のオプションにできます）。
* 承認されると、以降のフローは[OpAMP接続設定オファーのフロー](#opamp接続設定オファーのフロー)の手順と基本的に同一ですが、削除すべき古いクライアント証明書が存在しない点が異なります。

TOFUフローによって、証明書のクライアント側インストールを行う必要なく、セキュアな環境をブートストラップできます。

まったく同じTOFUの手法は、Serverにアクセスするための認可ヘッダーを持たないOpAMP Clientにも使えます。Serverはそのようなアクセスを検出し、承認後にClientへ認可ヘッダーを送信できます。

#### Registration On First Use

一部のユースケースでは、新しくインストールされたAgentに初回使用に適した初期の接続設定を装備させ、最初の接続が確立された後に新しい接続クレデンシャルの組を生成することが望ましい場合があります。

これは、TOFUフローの仕組みと非常に似た方法で実現できます。唯一の違いは、最初の接続は適切に認証されますが、Serverは接続直後に新しい接続設定を生成してAgentに提示することです。Clientはその設定を永続化し、以降のすべての操作でそれを使います。

これにより、事前に定義した1組の接続クレデンシャル（認可ヘッダー、証明書など）を使って多数のAgentをデプロイしつつ、接続に成功した直後に各Agentが独自の一意な接続クレデンシャルを取得できます。これにより、他のすべてのAgentへのアクセスを妨げることなく、個々のAgentのクレデンシャルを失効させられます。

#### エージェント起動のCSRフロー

ステータス: [Development]

これは、Certificate Signing Request（CSR）をServerに送信し、以降のOpAMP接続で使うクライアント証明書（自己署名または CA署名）を取得できる、Agent起動のフローです。

このフローは現在、OpAMP接続に対してのみサポートされています。Agentが自身のテレメトリー用の接続や他の接続種別に対してCSRリクエストを送ることはできません。

```
                   Client                                 Server

                     │ (1)           Connect                 │
                     ├──────────────────────────────────────►│
                     │                 ...                   │
┌───────────┐        │                                       │          ┌───────────┐
│ Generate  │  (2)   │ (3)     AgentToServer{CSR}            │(4)       │           │
│ Keypair   ├───────►├──────────────────────────────────────►├─────────►│  Approve  │
│ and CSR   │        │                 ...                   │          │           │
└───────────┘        │                                       │          └─────┬─────┘
                     │                                       │                │(5)
                     │                                       │                │
                     │                                       │                ▼
                     │                                       │          ┌───────────┐
┌───────────┐        │ServerToAgent{ConnectionSettingsOffers}│ (7)      │Create     │
│           │◄───────┤◄──────────────────────────────────────┤◄─────────┤Certificate│
│Credentials│ Save   │                                       │          │    (6)    │
│   Store   │        │             Disconnect                │          └───────────┘
│           ├───────►├──────────────────────────────────────►│
└───────────┘        │                                       │
                     │    Connect, New settings              │          ┌───────────┐
                     ├──────────────────────────────────────►├─────────►│           │
                     │                                       │ Delete   │Credentials│
┌───────────┐        │    Connection established             │ old      │   Store   │
│           │◄───────┤◄─────────────────────────────────────►│◄─────────┤           │
│Credentials│Delete  │                                       │          └───────────┘
│   Store   │old (8) │                                       │
│           ├───────►│                                       │
└───────────┘        │                                       │
```

このシーケンスは以下のとおりです。

- (1) Clientは Serverに接続します。Clientは通常のTLSを使い、Serverの識別情報を検証するべき（SHOULD）です。Agentは、Serverによってすでに信頼されているブートストラップ用のクライアント証明書を使うこともできます（注: このブートストラップ証明書の配布とインストール方法は、この仕様書の対象外です）。
- (2) Agentは鍵ペアとCertificate Signing Request（CSR）を生成します。CSRには、Clientが以降の認証に使いたい情報が含まれます。
- (3) ClientはCSRを含むAgentToServerメッセージを送信します。
- (4) ブートストラップ証明書が提供されている場合、Serverはそれを（信頼されたCAなどで）検証し、Agent用のクライアント証明書を生成する承認を待ちます。
- (5) Serverは人間による手動の承認を待つか、あるいはServerがそのように設定されている場合はTOFUリクエストをすべて自動的に承認します（Server側のオプションにできます）。
- (6) 承認されると、Serverはクライアント証明書を作成します。Serverはこれを、自己署名証明書を発行（ローカルCAとして動作）するか、CSRをCAにプロキシしてCAからクライアント証明書を取得するかのいずれかで行います。
- (7) CAからクライアント証明書を取得した後のフローは、[OpAMP接続設定オファーのフロー](#opamp接続設定オファーのフロー)の手順と基本的に同一で、作成されたクライアント証明書を運ぶ接続設定の提示から始まります。OpAMPConnectionSettings.certificateメッセージには、certフィールドにクライアント証明書が設定されます。CAが使われる場合はca_certフィールドにCAの証明書が設定されます。private_keyフィールドは設定されません。このフローでは、Agentが秘密鍵を保持しており、Serverはそれを保持していないためです。
- (8) 提示された新しいクライアント証明書の検証に成功すると、Agentは（使われていた場合）ブートストラップ証明書を削除し、以降の接続には新しい証明書を使います。

ServerはAgentにOpAMPConnectionSettingsを送信する際、`certificate`以外のフィールドも含めてもよい（MAY）ものとし、これによりServerはAgentの証明書、接続ヘッダー、その他の設定を一度に置き換えられます。

手順4〜6のいずれかが失敗した場合、Serverは`type`フィールドを`ServerErrorResponseType_BadRequest`に設定した[ServerErrorResponse](#servererrorresponse-message)でAgentに応答しなければなりません（MUST）。

まったく同じフローは、Agentがいつでも新しい証明書を再リクエストするために使えます。例えば、現在の証明書の有効期限が近づいたときにAgentがこれを行うことがあります。

##### CSRにおけるinstance_uidの利用

ステータス: [Development]

実装は、AgentのinstanceUidをCSRフィールドの1つ（またはその一部）として使うことを選んでもよく、そのような実装では、接続してきたAgentのペイロード内のinstance_uidが証明書の内容と一致することをServerが検証してもよい（MAY）ものとします。これにより、Agentが他のAgentになりすますことを防げます。

CSRフィールドにinstance_uidを含むCSRを受信した場合、ServerはAgentToServerメッセージ内の[instance_uid](#agenttoserverinstance_uid)フィールドが、CSRフィールド内のinstance_uidと一致することを検証しなければなりません（MUST）。これにより、Agentが自分自身の証明書しかリクエストできないことが強制されます。

instance_uidがCSRおよび発行されたクライアント証明書の一部である場合、instance_uidの変更があればクライアント証明書の再生成が必要になります。そのような変更は、例えばServerが[new_instance_uid](#servertoagentagent_identification)フィールドを通じてAgentに新しいinstance_uidを使うよう指示した場合に発生し得ます。

Serverからinstance_uidの変更を指示されたAgentは、今回は新しいinstance_uidをCSRフィールドの1つとして使って、[エージェント起動のCSRフロー](#エージェント起動のcsrフロー)を再度実行しなければなりません。Serverは、Agentが古いinstance_uidを含む古い証明書をまだ使用している間にCSRを受信する準備をしておかなければなりません。

言い換えると、受信したCSRが、その同じ着信接続で使われているクライアント証明書内で参照されているinstance_uidとは異なるinstance_uidに対する新しい証明書をリクエストすることがあります。これは有効な状況であり、Serverによって拒否されてはなりません（MUST NOT）。この状況では、Serverは、接続してきたクライアント証明書内のinstance_uidを見て、それを既知の古いinstance_uidと比較し、Agentが実際にServerの指示どおりinstance_uidの変更を行っていることを確認してもよい（MAY）ものとします。

#### アクセスの取り消し

ServerはClientが使うアクセスヘッダーとクライアント証明書を知っているため、対応する接続設定を「失効」としてマークしてClientを切断することで、個々のAgentへのアクセスを取り消せます。失効したクレデンシャルを使った以降の接続は、Serverによって拒否され、実質的にそのClientによるServerへのアクセスが禁止されます。

Serverは、Agentの3種類の宛先すべての接続設定を管理できるため（接続設定を提示できるため）、この取り消しは、Serverが以前提示し、Agentが受け入れた宛先種別であれば、3種類のいずれについても実行できます。

自身のテレメトリーと「その他」の宛先については、Serverは、失効したクレデンシャルを使う接続を拒否できるように、失効した事実を対応する宛先にも伝達しなければなりません（MUST）。

#### 証明書の生成

Serverが生成するクライアント証明書は、自己署名、プライベートなCertificate Authorityによる署名、あるいはパブリックなCertificate Authorityによる署名のいずれでもかまいません。Serverは、証明書が意図した宛先によって信頼されるようにクライアント証明書を生成する責任を負います。これには、宛先が個々の自己署名クライアント証明書を直接記憶して信頼するか、あるいは信頼チェーンを検証できるように、クライアント証明書の署名に使われたCertificate Authorityを信頼している必要があります。

クライアント証明書がどのように生成されるかは、OpAMP仕様書の対象外です。

#### その他の宛先向け接続設定

未定。

#### ConnectionSettingsRequest Message

ステータス: [Development]

ConnectionSettingsRequestは、Agentの接続設定のオファーを作成して応答するようServerに求める、Agentからのリクエストです。

```protobuf
message ConnectionSettingsRequest {
    OpAMPConnectionSettingsRequest opamp = 1;
}
```

`opamp`フィールドは、OpAMP接続設定のリクエストを示すために設定されます。このフィールドが未設定の場合、ConnectionSettingsRequestメッセージは空であり、Serverにとって対応不要です。

#### OpAMPConnectionSettingsRequest Message

ステータス: [Development]

OpAMPConnectionSettingsRequestは、レスポンス内にOpAMPConnectionSettingsを生成するようServerに求めるリクエストです。

[エージェント起動のCSRフロー](#エージェント起動のcsrフロー)で使われます。

```protobuf
message OpAMPConnectionSettingsRequest {
    CertificateRequest certificate_request = 1;
}
```

certificate_requestは、Agentがクライアント証明書の作成をServerにリクエストする場合に設定されます。このフィールドは必須です。

#### CertificateRequest Message

ステータス: [Development]

```protobuf
message CertificateRequest {
    bytes csr = 1;
}
```

`csr`フィールドは、クライアントの秘密鍵で署名された、PEMエンコードされたClient Certificate Signing Request（CSR）です。

Serverはリクエストを検証するべき（SHOULD）であり、certificate.certに発行された証明書を含むOpAMPConnectionSettingsで応答するべき（SHOULD）です。

#### ConnectionSettingsOffers Message

ConnectionSettingsOffersメッセージは、Agentが使うべき接続設定を記述します。

```protobuf
message ConnectionSettingsOffers {
    bytes hash = 1;
    OpAMPConnectionSettings opamp = 2;
    TelemetryConnectionSettings own_metrics = 3;
    TelemetryConnectionSettings own_traces = 4;
    TelemetryConnectionSettings own_logs = 5;
    map<string,OtherConnectionSettings> other_connections = 6;
}
```

##### ConnectionSettingsOffers.hash

このメッセージから変化がないために省略されている設定も含めた、すべての設定のハッシュです。

##### ConnectionSettingsOffers.opamp

OpAMP Serverに接続するための設定です。このフィールドが設定されていない場合、Clientは設定が変化していないと見なし、既存の設定を使い続けるべきです。Clientは、無効な設定によってOpAMP Serverへのアクセスを失わないよう、実際に接続してみることで提示された接続設定を検証しなければなりません（MUST）。

##### ConnectionSettingsOffers.own_metrics

Agent自身のメトリクスを送信するOTLPメトリクスバックエンドに接続するための設定です。このフィールドが設定されていない場合、Agentは設定が変化していないと見なすべきです。

##### ConnectionSettingsOffers.own_traces

Agent自身のトレースを送信するOTLPトレースバックエンドに接続するための設定です。このフィールドが設定されていない場合、Agentは設定が変化していないと見なすべきです。

##### ConnectionSettingsOffers.own_logs

Agent自身のログを送信するOTLPログバックエンドに接続するための設定です。このフィールドが設定されていない場合、Agentは設定が変化していないと見なすべきです。

##### ConnectionSettingsOffers.other_connections

文字列名がそれぞれに関連付けられた、もう1つの接続設定の集合です。Agentがこれらをどう使うかはAgent固有です。通常、名前はAgentが認識している接続先の名前を表します。このフィールドが設定されていない場合、Agentはother_connections設定が変化していないと見なすべきです。

#### OpAMPConnectionSettings

OpAMPConnectionSettingsメッセージは、OpAMP接続に指定された設定を使うようServerからOpAMP Clientへのオファーを構成するフィールドの集合です。

```protobuf
message OpAMPConnectionSettings {
    string destination_endpoint = 1;
    Headers headers = 2;
    TLSCertificate certificate = 3;
    uint64 heartbeat_interval_seconds = 4;
    TLSConnectionSettings tls = 5;
    ProxyConnectionSettings proxy = 6;
}
```

##### OpAMPConnectionSettings.destination_endpoint

OpAMP ServerのURLです。これはWebSocketまたはHTTPのURLでなければならず（MUST）、空であってはなりません（MUST）。例: `wss://example.com:4320/v1/opamp`。

##### OpAMPConnectionSettings.headers

接続時に使う任意のヘッダーです。通常、アクセストークンや他の認可ヘッダーを設定するために使われます。HTTPベースのプロトコルでは、Clientはこれらをリクエストヘッダーに設定するべきです。例: key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l"。

##### OpAMPConnectionSettings.certificate

Clientは、以降その宛先に接続するために提示された証明書を使うべきです。Clientが提示された証明書を検証して接続できた場合、この接続についての以前のクライアント証明書はすべて忘れるべき（SHOULD）です。このフィールドは任意です。省略された場合、Clientはクライアント側証明書を使うべきではありません（SHOULD NOT）。このフィールドは、クライアント証明書の失効やローテーションを行うために使えます。

##### OpAMPConnectionSettings.heartbeat_interval_seconds

ステータス: [Development]

ReportsHeartbeatケーパビリティがtrueの場合、Clientは提示されたハートビート間隔を使ってAgentToServerメッセージを定期的に送信しなければなりません（MUST）。このケーパビリティがtrueで、Serverがheartbeat_interval_secondsを0に設定した場合、Agentのハートビートは無効化されるべきです。少なくとも`AgentToServer.instance_uid`フィールドはハートビートに設定されなければなりません（MUST）。HTTPベースのクライアントは、ハートビート間隔をポーリング間隔として使わなければなりません（MUST）。

instance_uidフィールドが設定されているAgentToServerメッセージは、有効なハートビートとみなされます。他のデータを含む別のAgentToServerメッセージがちょうど送信された場合、ハートビートのためだけに別途AgentToServerメッセージを送信する必要はないことに注意してください。Agentは、直前に送信したAgentToServerメッセージからハートビート間隔を数えなければなりません。

ハートビートは、接続をアクティブに保ち、Agentがまだ生存かつアクティブであることをServerに知らせるために使われます。Serverはハートビートを使って、接続中のAgentの生存に関する判断を行うことができます。

ハートビートの調整のフローは以下のように説明されます。

```
┌──────────┐                       ┌──────────┐
│          │ (1) Connect           │          │
│          ├──────────────────────►│          │
│          │                       │          │
│          │ (2) Set Heartbeat     │          │
│          │◄──────────────────────┤          │
│          │     Interval          │          │
│          │                       │          │
│  Agent   │ (3) Send Heartbeat    │  Server  │
│          ├──────────────────────►│          │
│          │                       │          │
│          │  ... heartbeat        │          │
│          │      interval         │          │
│          │                       │          │
│          │ (4) Send Heartbeat    │          │
│          ├──────────────────────►│          │
│          │                       │          │
└──────────┘                       └──────────┘
```

1. Agentはserverに接続し、オプションでReportsHeartbeatケーパビリティを設定します。Agentがこのケーパビリティを設定しない場合、Serverはハートビートを受信することを期待しないべきです。
2. AgentがReportsHeartbeatケーパビリティを設定した場合、ServerはOpAMPConnectionSettingsメッセージ内のheartbeat_interval_secondsフィールドに間隔を設定して応答してもよい（MAY）ものとします。この値は、希望する間隔か、`0`（Clientがハートビートを送信すべきでないことを示す）のいずれかです。推奨されるデフォルトの間隔は30秒です。
3. AgentがReportsHeartbeatケーパビリティを設定しており、かつServerがハートビートを無効化していない場合、Agentは、Serverが設定した間隔、またはAgent自身の設定されたハートビート間隔で指定される周期ごとにハートビートメッセージを送信しなければなりません（MUST）。
4. Agentは、生存している間、設定された間隔でハートビートを送信し続けます。

Agentは、ReportsHeartbeatケーパビリティを設定しないことで、ハートビートを送信しないことを選べます。Serverは、OpAMPConnectionSettings.heartbeat_interval_secondsフィールドに`0`秒の値を設定して応答することで、ハートビートを受信しないことを選べます。

##### OpAMPConnectionSettings.tls

ステータス: [Development]

任意のOpAMP固有のTLS設定です。

##### OpAMPConnectionSettings.proxy

ステータス: [Development]

任意のOpAMP固有のプロキシ設定です。

#### TelemetryConnectionSettings

TelemetryConnectionSettingsメッセージは、自身のテレメトリーを報告するためのネットワーク接続に指定された設定を使うようServerからAgentへのオファーを構成するフィールドの集合です。

```protobuf
message TelemetryConnectionSettings {
    string destination_endpoint = 1;
    Headers headers = 2;
    TLSCertificate certificate = 3;
    TLSConnectionSettings tls = 4;
    ProxyConnectionSettings proxy = 5;
}
```

##### TelemetryConnectionSettings.destination_endpoint

この値は、パスを含むOTLP/HTTP/ProtobufレシーバーへのフルURLでなければなりません（MUST）。スキーマは`https://`で始まるべき（SHOULD）です。例: `https://example.com:4318/v1/metrics`。URLが`http://`で始まる場合、Agentはテレメトリーの送信を拒否してもよい（MAY）ものとします。

##### TelemetryConnectionSettings.headers

接続時に使う任意のヘッダーです。通常、アクセストークンや他の認可ヘッダーを設定するために使われます。HTTPベースのプロトコルでは、Agentはこれらをリクエストヘッダーに設定するべきです。例: key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l"。

##### TelemetryConnectionSettings.certificate

Agentは、以降その宛先に接続するために提示された証明書を使うべきです。Agentが提示された証明書を検証して接続できた場合、この接続についての以前のクライアント証明書はすべて忘れるべき（SHOULD）です。このフィールドは任意です。省略された場合、クライアントはクライアント側証明書を使うべきではありません（SHOULD NOT）。このフィールドは、クライアント証明書の失効やローテーションを行うために使えます。

##### TelemetryConnectionSettings.tls

ステータス: [Development]

任意のテレメトリー固有のTLS設定です。

##### TelemetryConnectionSettings.proxy

ステータス: [Development]

任意のテレメトリー固有のプロキシ設定です。

#### OtherConnectionSettings

OtherConnectionSettingsメッセージは、ネットワーク接続に指定された設定を使うようServerからAgentへのオファーを構成するフィールドの集合です。このメッセージのすべてのフィールドが指定されている必要はありません。Serverは一部のフィールドのみを指定でき、その場合はServerがそれらのフィールドのみを変更するようAgentに提示し、残りのフィールドは変更しないままにすることを意味します。

例えば、Serverはcertificateフィールドのみが設定され、他のすべてのフィールドが未設定のConnectionSettingsメッセージを送信することがあります。これは、Serverが Agentに新しい証明書を使ってほしいが、現在のヘッダーやその他の設定を使って現在送信中の宛先への送信を継続してほしいことを意味します。

他のメッセージを参照するフィールドについては、その参照が未設定の場合にそのフィールドは未設定とみなされます。

プリミティブなフィールド（string）については、フィールドが未設定であることを示すために「flags」に頼ります（これは、古い`protoc`コンパイラがフィールドの存在を確認できるメソッドを生成しないという制約を克服するためです）。

```protobuf
message OtherConnectionSettings {
    string destination_endpoint = 1;
    Headers headers = 2;
    TLSCertificate certificate = 3;
    map<string, string> other_settings = 4;
    TLSConnectionSettings tls = 5;
    ProxyConnectionSettings proxy = 6;
}
```

##### OtherConnectionSettings.destination_endpoint

URL、host:port、またはその他の宛先指定子です。

##### OtherConnectionSettings.headers

接続時に使う任意のヘッダーです。通常、アクセストークンや他の認可ヘッダーを設定するために使われます。HTTPベースのプロトコルでは、Agentはこれらをリクエストヘッダーに設定するべきです。例: key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l"。

##### OtherConnectionSettings.certificate

Agentは、以降その宛先に接続するために提示された証明書を使うべきです。Agentが提示された証明書を検証して接続できた場合、この接続についての以前のクライアント証明書はすべて忘れるべき（SHOULD）です。このフィールドは任意です。省略された場合、クライアントはクライアント側証明書を使うべきではありません（SHOULD NOT）。このフィールドは、クライアント証明書の失効やローテーションを行うために使えます。

##### OtherConnectionSettings.other_settings

その他の接続設定です。これらはAgent固有であり、Agentが解釈します。

##### OtherConnectionSettings.tls

ステータス: [Development]

任意の接続固有のTLS設定です。

##### OtherConnectionSettings.proxy

ステータス: [Development]

任意の接続固有のプロキシ設定です。

#### TLSConnectionSettings Message

ステータス: [Development]

このメッセージは、クライアントの接続を構成するために使われる任意のTLS設定を運びます。Agentが接続設定を検証できた場合、Agentは以前のTLS設定をすべて忘れるべき（SHOULD）です。このメッセージが含まれていない場合、クライアントは設定が変化していないと見なして既存の設定を使い続けるべき（SHOULD）です。

```protobuf
message TLSConnectionSettings {
  string ca_pem_contents = 1;
  bool include_system_ca_certs_pool = 2;
  bool insecure_skip_verify = 3;
  string min_version = 4;
  string max_version = 5;
  repeated string cipher_suites = 6;
}
```

##### TLSConnectionSettings.ca_pem_contents

ca_pem_contents属性は、TLS設定の一部としてCAの公開証明書を提供するために使うことができます。

##### TLSConnectionSettings.include_system_ca_pool

include_system_ca_poolは、TLS設定を構築する際にシステムのデフォルトCAプールを使うようAgentに求めます。

##### TLSConnectionSettings.insecure_skip_verify

この設定は、接続のすべてのTLS検証を無効にします。

##### TLSConnectionSettings.min_version

これは、クライアントが使う最小サポートTLSバージョンを設定します。例: `1.2`、`TLSv1.2`。

##### TLSConnectionSettings.max_version

これは、クライアントが使う最大サポートTLSバージョンを設定します。例: `1.2`、`TLSv1.2`。

##### TLSConnectionSettings.ciper_suites

これは、接続で使用できるサポート対象の暗号スイートを設定します。例: `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`。

#### ProxyConnectionSettings Message

ステータス: [Development]

このメッセージは、クライアントの接続を構成するために使われる任意のプロキシ設定を運びます。Agentが接続設定を検証できた場合、Agentは以前のプロキシ設定をすべて忘れるべき（SHOULD）です。このメッセージが含まれていない場合、クライアントは設定が変化していないと見なして既存の設定を使い続けるべき（SHOULD）です。

プロキシがmTLS証明書やその他のTLS設定を必要とする場合、Clientは関連する接続の設定を使うべき（SHOULD）です。

```protobuf
message ProxyConnectionSettings {
    string url = 1;
    Headers connect_headers = 2;
}
```

##### ProxyConnectionSettings.url

urlは空であってはならず（MUST）、例えば`https://example.com:8443`や`127.0.0.1:8443`のようになります。

##### ProxyConnectionSettings.connect_headers

HTTPベースのプロキシの最初のCONNECTリクエストに対してClientが設定すべき任意のヘッダーです。SOCKS5などの他のプロキシ種別は、これらのヘッダーを無視することがあります。例: key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l"。

#### Headers Message

```protobuf
message Headers {
    repeated Header headers = 1;
}
message Header {
    string key = 1;
    string value = 2;
}
```

#### TLSCertificate Message

このメッセージは、クライアント側証明書として使えるTLS証明書を運びます。

（cert、private_key）の組は、宛先Serverが認識するCertificate Authority（CA）によって発行・署名されるべきです。

あるいは、Serverが証明書を検証できる限り、証明書は自己署名でもかまいません。この場合、ca_certフィールドは省略できます。

```protobuf
message TLSCertificate {
    bytes cert = 1;
    bytes private_key = 2;
    bytes ca_cert = 3;
}
```

##### TLSCertificate.cert

PEMエンコードされた証明書です。必須です。

##### TLSCertificate.private_key

証明書のPEMエンコードされた秘密鍵です。必須です。

##### TLSCertificate.ca_cert

署名を行ったCAのPEMエンコードされた証明書です。任意ですが、証明書がCA署名の場合は指定されなければなりません（MUST）。将来、接続してきたクライアントの証明書を検証できるように、中間のTLS終端プロキシによって保存されることがあります。

### 自身のテレメトリーのレポート

ステータス: [Beta]

自身のテレメトリーのレポートは、OpAMPプロトコルの任意のケーパビリティです。Serverは、Agentが自身のテレメトリー（メトリクス、トレース、ログ）を送信できる宛先をAgentに提示できます。Agentがテレメトリーを生成でき、かつそうしたい場合、Agentは自身のテレメトリーをOTLP/HTTPプロトコルで提示された宛先に送信すべきです。

```
            ┌────────────┬────────┐           ┌─────────┐
            │            │ OpAMP  │  OpAMP    │ OpAMP   │
            │            │        ├──────────►│         │
            │            │ Client │           │ Server  │
            │            └────────┤           └─────────┘
            │   Agent             │
            │            ┌────────┤           ┌─────────┐
            │            │OTLP    │ OTLP/HTTP │ OTLP    │
            │            │        ├──────────►│ Metric  │
            │            │Exporter│           │ Backend │
            └────────────┴────────┘           └─────────┘
```

Serverは、own_metrics、own_traces、own_logsのいずれか1つ以上のフィールドが設定された[connection_settings](#servertoagentconnection_settings)フィールドを持つ[ServerToAgent](#servertoagent-message)メッセージを送信することでオファーを行います。これらのフィールドはそれぞれ、OTLPプロトコルでテレメトリーを受信できる宛先を記述します。

Serverは、使用できるOTLPバックエンドが存在しない場合を除き、特定のAgentに最初のServerToAgentメッセージを送信する際（通常はClientからの最初のステータスレポートへの応答として）に[connection_settings](#servertoagentconnection_settings)フィールドを設定すべき（SHOULD）です。Serverは、宛先が変化した場合、以降のServerToAgentでもこのフィールドを設定するべき（SHOULD）です。宛先が変化していない場合、connection_settingsフィールドは設定されるべきではありません（SHOULD NOT）。connection_settingsフィールドが未設定のServerToAgentを受信した場合、Agentは以前に提示された宛先へのテレメトリー送信を続けるべき（SHOULD）です。AgentがReportsConnectionSettingsStatusケーパビリティを持つ場合、新しい設定を受信した際に[connection_settings_status](#connectionsettingsstatus-message)を適宜設定するべき（SHOULD）です。

Agentは、[own_metrics](#connectionsettingsoffersown_metrics)フィールドで提示された宛先に定期的にメトリクスを報告するべき（SHOULD）です。推奨されるレポート間隔は10秒です。以下は、操作のシーケンスを示す図です。

```
       Agent    Client                                Server
                                                            Metric
        │         │                                      │  Backend
        │         │ServerToAgent{ConnectionSettingsOffer}│
        ┌─────────│◄─────────────────────────────────────┤    │
        │                                                │    │
        ▼                                                     │
    ┌────────┐                                                │
    │Collect │                     OTLP Metrics               │ ──┐
    │Own     ├───────────────────────────────────────────────►│   │
    │Metrics │                                                │   │
    └────────┘                         ...                    .   │ Repeats
        │                                                         │
    ┌────────┐                                                │   │ Periodically
    │Collect │                     OTLP Metrics               │   │
    │Own     ├───────────────────────────────────────────────►│   │
    │Metrics │                                                │ ──┘
    └────────┘                                                │
```

Agentは、Agentプロセス（または複数プロセス）のメトリクスと、Agentの状態を記述する任意のカスタムメトリクスを報告すべき（SHOULD）です。報告されるプロセスメトリクスは、OpenTelemetryの[プロセスに関する規約](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/system/process-metrics.md)に従わなければなりません（MUST）。

同様に、Agentは、[own_traces](#connectionsettingsoffersown_traces)フィールドで提示された宛先にトレースを、[own_logs](#connectionsettingsoffersown_logs)フィールドで提示された宛先にログを報告すべき（SHOULD）です。

AgentDescriptionメッセージの[identifying_attributes](#agentdescriptionidentifying_attributes)フィールドで指定されたすべての属性は、報告されるOTLPテレメトリーのResourceにも指定されるべき（SHOULD）です。

AgentDescriptionメッセージの[non_identifying_attributes](#agentdescriptionnon_identifying_attributes)フィールドで指定された属性は、報告されるOTLPテレメトリーのResourceにも指定してもよい（MAY）ものとし、その場合はまったく同じ値を持つべき（SHOULD）です。

### 設定

Agentの設定は、OpAMPプロトコルの任意のケーパビリティです。リモート設定のケーパビリティは、必要に応じて（例えばKubernetesのようなオーケストレーションシステムの既存の設定機能を使う場合など）無効化できます。

Serverは、ServerToAgentメッセージ内の[remote_config](#servertoagentremote_config)フィールドを設定することで、Agentにリモート設定を提示できます。ServerToAgentメッセージは通常ステータスレポートへの応答として送信されるため、ServerはAgentの記述を把握しており、必要に応じて提示する設定を特定のAgentに合わせて調整できます。

OpAMP Clientは、AgentがリモートConfigurationを受け入れられる場合、AgentToServer.capabilitiesのAcceptsRemoteConfigビットを設定しなければなりません（MUST）。このビットが設定されていない場合、Serverはリモート設定をAgentに提示してはなりません（MUST NOT）。

Agentが実際の稼働に使う設定は、Serverから提示されるリモート設定とは異なることがあります。この実際の設定はAgentの実効設定（Effective Configuration）と呼ばれます。実効設定は通常、リモート設定とAgentが利用可能な他の入力（例えばローカルに利用可能な設定）をマージした後にAgentによって形成されます。

実効設定が形成されると、Agentはそれを自身の動作に使い、Clientはステータスレポートの[effective_config](#agenttoservereffective_config)フィールドを通じて実効設定もOpAMP Serverに報告します。Serverは通常、Clientから報告されるステータス内の他のデータと並んで、実効設定をエンドユーザーが確認できるようにします。

Clientは、Agentが実効設定を報告できる場合、AgentToServer.capabilitiesのReportsEffectiveConfigビットを設定しなければなりません（MUST）。このビットが設定されていない場合、ServerはAgentToServer.effective_configフィールドが設定されることを期待するべきではありません。

以下は典型的な設定シーケンスの図です。

```
     Agent       Client                             Server

       │           │ AgentToServer{}                   │   ┌─────────┐
       │           ├──────────────────────────────────►├──►│ Process │
       │           │                                   │   │ Status  │
Local  │    Remote │                                   │   │ and     │
Config │    Config │ ServerToAgent{AgentRemoteConfig}  │   │ Fetch   │
  │    │  ┌────────┤◄──────────────────────────────────┤◄──┤ Config  │
  ▼    │  ▼        │                                   │   └─────────┘
 ┌─────────┐       │                                   │
 │ Config  │       │                                   │
 │ Merger  │       │                                   │
 └─────┬───┘       │                                   │
       │           │                                   │
       │Effective  │                                   │
       │Config     │ AgentToServer{}                   │
       └──────────►├──────────────────────────────────►│
                   │                                   │
                   │                                   │
```

EffectiveConfigとRemoteConfigStatusのフィールドは、変化があった場合にAgentToServerメッセージに含まれます。

注: AgentToServerメッセージで報告される実効設定やその他のフィールドが変化していない場合、Clientはそのメッセージを送信するべきではありません（SHOULD NOT）。Clientがこの規則に従わない場合、ClientとServerの間で無限にメッセージが往復するループが発生する可能性があります。

Serverは、Clientからのステータスレポートを待たずに、自身の判断でリモート設定の送信を開始することもできます。これは、接続中だが報告すべき新しい情報がないAgentを再設定するために使えます。この場合のシーケンス図は以下のようになります。

```
    Agent      Client                             Server

       │           │                                   │
       │           │                                   │
       │           │                                   │   ┌────────┐
Local  │    Remote │                                   │   │Initiate│
Config │    Config │  ServerToAgent{AgentRemoteConfig} │   │and     │
    │  │  ┌────────┤◄──────────────────────────────────┤◄──┤Send    │
    ▼  │  ▼        │                                   │   │Config  │
  ┌─────────┐      │                                   │   └────────┘
  │ Config  │      │                                   │
  │ Merger  │      │                                   │
  └────┬────┘      │                                   │
       │           │                                   │
       │Effective  │                                   │
       │Config     │ AgentToServer{}                   │
       └──────────►├──────────────────────────────────►│
                   │                                   │
                   │                                   │
```

Agentは、自身の設定をServerによってリモートで制御させたくない場合、リモート設定のオファーを無視してもよい（MAY）ものとします。

#### Configuration Files

Agentの設定は、名前付きの設定ファイル群の集合です（これはリモート設定と実効設定の両方に当てはまります）。

ファイル名は、その集合の中で一意でなければなりません（MUST）。リモート設定とローカル設定に同じ名前だが異なる内容のファイルが含まれる可能性があります。これらのファイルがどのようにマージされて実効設定を形成するかはAgentの種類ごとに異なり、OpAMPプロトコルの一部ではありません。

集合内に設定ファイルが1つしかない場合、ファイル名は空でもよい（MAY）ものとします。

設定ファイルの集合は、AgentConfigMapメッセージを使って表現されます。

```protobuf
message AgentConfigMap {
  map<string, AgentConfigFile> config_map = 1;
}
```

AgentConfigSetメッセージのconfig_mapフィールドは、キーがファイル名である設定ファイルのマップです。

単一の設定ファイルを使うAgentの場合、config_mapフィールドは単一のエントリを含むべき（SHOULD）で、キーは空文字列でもよい（MAY）ものとします。

AgentConfigFileメッセージは、1つの設定ファイルを表し、以下の構造を持ちます。

```protobuf
message AgentConfigFile {
  bytes body = 1;
  string content_type = 2;
}
```

bodyフィールドには、設定ファイルの生バイト列が含まれます。生バイト列の内容、フォーマット、エンコーディングはAgentの種類ごとに異なり、OpAMPプロトコルの関心の外にあります。

content_typeは任意のフィールドです。bodyフィールドに含まれるものを記述するMIME Content-Type（例:「text/yaml」）です。Agentのステータスレポート内の実効設定で報告されるcontent_typeは、例えばServerが報告された設定をUIで適切に可視化するために使えます。

#### セキュリティに関する考慮事項

リモート設定は、悪意のある攻撃者に悪用される可能性のある、潜在的に危険な機能です。例えばAgentがローカルファイルを収集してネットワーク経由で送信できる場合、侵害されたOpAMP Serverが悪意のあるリモート設定をAgentに提示し、機密性の高いローカルファイルを収集して特定のネットワーク宛先に送信するようAgentに強制する可能性があります。

一般的な推奨事項については[一般的な推奨事項](#一般的な推奨事項)を、特にリモート再設定のケーパビリティに関する推奨事項については[設定の制限](#設定の制限)のセキュリティ節を参照してください。

#### AgentRemoteConfig Message

このメッセージの構造は以下のとおりです。

```protobuf
message AgentRemoteConfig {
  AgentConfigMap config = 1;
  bytes config_hash = 2;
}
```

### パッケージ

ステータス: [Beta]

各Agentは1つ以上のパッケージから構成されます。パッケージには名前があり、内容はファイルに格納されます。ファイルの内容、パッケージが提供する機能、それらがどのように保存・使用されるかはAgentの種類ごとに異なり、OpAMPプロトコルの関心の外にあります。

パッケージには、トップレベルパッケージとサブパッケージの2種類があります。

通常、トップレベルパッケージはAgentの主要機能を実装する1つだけです。トップレベルパッケージが1つだけの場合、名前は空でもかまいません。

サブパッケージはアドオンやプラグインとも呼ばれます。サブパッケージは、機能追加のためにAgentにインストールできます（そのためアドオンという名前がついています）。

Agentは1つ以上のパッケージをインストールしていることがあります。各パッケージには名前があります。Agentは、同じ名前のパッケージを2つ以上持つことはできません。

異なるパッケージは同じ名前のファイルを持つことがあります。ファイル名はグローバルに一意ではなく、特定のパッケージのスコープ内でのみ一意です。

パッケージは、（ローカルユーザーなどによって）ローカルに提供・インストールされることもあれば、Serverによってリモートで提示され、その場合はAgentがそのパッケージをダウンロードしてインストールすることもあります。

Serverは、Agentからのステータスレポートへの応答として、あるいはパッケージをAgentにプッシュしたいというServer側の判断によって送信されるServerToAgentメッセージ内の[packages_available](#servertoagentpackages_available)フィールドを設定することで、Agentにパッケージを提示します。

[PackagesAvailable](#packagesavailable-message)メッセージは、このAgent向けにServer上で利用可能なパッケージを記述します。各パッケージについて、このメッセージは、パッケージの内容を持つファイルを記述し、HTTP GETリクエストを使ってそのファイルをダウンロードできるURLを提供します。これらのURLは、（OpAMP Serverと同じホスト上、または別のホスト上にあり得る）ダウンロードServer上のパッケージファイルを指します。

このプロトコルは、パッケージごとに1つのダウンロード可能なファイルのみをサポートします。Agentのパッケージが概念的に複数のファイルから構成されている場合、Agentとサーバーは、zipやtarファイルのように、複数のファイルを1つのファイルに格納できる任意のファイル形式で保存することに同意できます。単一のパッケージファイルをダウンロードした後、Agentはその中に含まれるファイルを展開してもよい（MAY）ものとします。これがどのように行われるかはAgent固有であり、プロトコルの範囲外です。

OpAMP Clientが、AgentToServer.capabilitiesのAcceptsPackagesビットを通じてAgentがパッケージを受け入れられることを示した場合に限り、Serverはパッケージのオファーを提示できます。

#### パッケージのダウンロード

[PackagesAvailable](#packagesavailable-message)メッセージを受信した後、Agentは以下のダウンロード手順に従うべき（SHOULD）です。

##### ステップ1

自分が持っている全パッケージの集約ハッシュと、Serverがall_packages_hashフィールドで提示した集約ハッシュを比較します。

集約ハッシュが同一であれば、Agent上のすべてのパッケージがServerによって提示されたものと同一であることを意味するため、ダウンロード手順は完了したものと見なします。そうでない場合はステップ2に進みます。

##### ステップ2

Serverが提示した各パッケージについて、Agentはその特定のパッケージをダウンロードすべきかどうかを確認すべき（SHOULD）です。

* Agentが指定された名前のパッケージを持っていない場合、そのパッケージをダウンロードすべき（SHOULD）です。各パッケージファイルのダウンロード方法についてはステップ3を参照してください。
* Agentがそのパッケージを持っている場合、Agentは、自分が持っているパッケージのハッシュを、[PackageAvailable](#packageavailable-message)メッセージ内の[hash](#packageavailablehash)フィールドで提示されたパッケージのハッシュと比較すべき（SHOULD）です。ハッシュが同一であればパッケージは同一であり、そのパッケージの処理は完了したものとして次のパッケージに進みます。ハッシュが異なる場合は、ステップ3で説明されるようにパッケージファイルを確認します。

最後に、Agentが持っているパッケージの中にServerから提示されなかったものがある場合、そのパッケージはAgentによって削除されるべき（SHOULD）です。

##### ステップ3

Serverから提示されたパッケージのファイルについて、Agentはそのファイルをダウンロードすべきかどうかを確認すべき（SHOULD）です。

* Agentが指定された名前のファイルを持っていない場合、そのファイルをダウンロードすべき（SHOULD）です。
* Agentがそのファイルを持っている場合、Agentは、ローカルに持っているファイルのハッシュを、[DownloadableFile](#downloadablefile-message)メッセージ内の[hash](#downloadablefilecontent_hash)フィールドと比較すべき（SHOULD）です。ハッシュが同一であれば、このファイルの処理は完了です。そうでなければ、提示されたファイルは異なるものであり、[DownloadableFile](#downloadablefile-message)メッセージの[download_url](#downloadablefiledownload_url)フィールドで指定された場所からファイルをダウンロードすべき（SHOULD）です。Agentは、ファイルをダウンロードするためにHTTP GETメッセージを使うべき（SHOULD）です。Agentは、GETリクエストに[headers](#downloadablefileheaders)フィールドで提供されたHTTPヘッダーを含めるべき（SHOULD）です。

上記の手順によって、Agentは新規または変更されたパッケージのみを効率的にダウンロードし、かつ新規または変更されたファイルのみをダウンロードできます。

パッケージをダウンロードした後、Agentは（例えばパッケージを何らかの形で「インストール」または「有効化」するといった）Agentの種類ごとの追加処理を実行できます。

#### Package Status Reporting

ダウンロードとインストールの過程で、Agentはその処理のステータスを定期的に報告してもよい（MAY）ものとします。これを行うために、OpAMP Clientは[AgentToServer](#agenttoserver-message)メッセージを送信し、[package_statuses](#agenttoserverpackage_statuses)フィールドを適宜設定するべき（SHOULD）です。

すべてのパッケージのダウンロードとインストールが（成功または失敗にかかわらず）完了したら、ClientはすべてのパッケージのステータスをServerに報告するべき（SHOULD）です。

以下は、パッケージのダウンロードとステータスレポートの典型的なシーケンス図です。

```
    Download        Agent/Client                          OpAMP
     Server                                              Server
       │                 │                                  │
       │                 │  ServerToAgent{PackagesAvailable}│
       │                 │◄─────────────────────────────────┤
       │   HTTP GET      │                                  │
       │◄────────────────┤                                  │
       │ Download file #1│                                  │
       ├────────────────►│                                  │
       │                 │ AgentToServer{PackageStatuses}   │
       │                 ├─────────────────────────────────►│
       │   HTTP GET      │                                  │
       │◄────────────────┤                                  │
       │ Download file #2│                                  │
       ├────────────────►│                                  │
       │                 │ AgentToServer{PackageStatuses}   │
       │                 ├─────────────────────────────────►│
       │                 │                                  │
       .                 .                                  .

       │   HTTP GET      │                                  │
       │◄────────────────┤                                  │
       │ Download file #N│                                  │
       ├────────────────►│                                  │
       │                 │ AgentToServer{PackageStatuses}   │
       │                 ├─────────────────────────────────►│
       │                 │                                  │
```

Clientは、（ダウンロードまたはインストール中の）Agentが持っている、または処理中のすべてのパッケージを、PackageStatusesメッセージに常に含めなければなりません（MUST）。

Clientは、Serverから提示されてダウンロードしたパッケージだけでなく、Agentがローカルにインストールしたパッケージのステータスも報告してもよい（MAY）ものとします。

#### ハッシュの算出

Agentとサーバーは、Agentがどのファイルとパッケージをダウンロードする必要があるかを判断できるように、ハッシュを使ってファイルとパッケージの内容を識別します。

ハッシュの算出はServerによって行われます。Serverは衝突確率が最小になる強力なハッシュ算出方式を選ばなければならず（MUST）（実装がそのような保証を必要とする場合、ハッシュの一意性を保証するために乱数値を算出にシードしてもよい（MAY）ものとします）。

ハッシュはAgentにとって不透明であり、Agentはハッシュを算出することはなく、保存と比較のみを行います。

ハッシュには3つのレベルがあります。

##### File Hash

パッケージファイルの内容のハッシュです。これは[DownloadableFile](#downloadablefile-message)メッセージの[content_hash](#downloadablefilecontent_hash)フィールドに格納されます。この値は、Agentが持っている特定のファイルがServer上のものと異なり、再ダウンロードが必要かどうかを判断するために使われるべき（SHOULD）です。

##### Package Hash

パッケージ全体（パッケージ名とファイル内容）を識別するパッケージハッシュです。このハッシュは[PackageAvailable](#packageavailable-message)メッセージの[hash](#packageavailablehash)フィールドに格納されます。

この値は、Agentが持っている特定のパッケージがServer上のものと異なり、再ダウンロードが必要かどうかを判断するために使われるべき（SHOULD）です。

##### All Packages Hash

全パッケージハッシュは、特定のAgentのすべてのパッケージの集約ハッシュです。このハッシュは、すべてのパッケージ名と内容の集約として算出されます。このハッシュは、[PackagesAvailable](#packagesavailable-message)メッセージの[all_packages_hash](#packagesavailableall_packages_hash)フィールドに格納されます。

この値は、Agentが持っているパッケージのいずれかがServer上で利用可能なものと異なり、再ダウンロードが必要かどうかを判断するために使われるべき（SHOULD）です。

集約ハッシュには、Agent上でローカルに利用可能で、ダウンロードServerからダウンロードされなかったパッケージは含まれない点に注意してください。

#### セキュリティに関する考慮事項

パッケージをリモートでダウンロードすることは、悪意のある攻撃者に悪用される可能性のある、潜在的に危険な機能です。パッケージに実行可能コードが含まれる場合、侵害されたOpAMP Serverが悪意のあるパッケージをAgentに提示し、任意のコードを実行するようAgentに強制する可能性があります。

一般的な推奨事項については[一般的な推奨事項](#一般的な推奨事項)を、特に[コード署名](#コード署名)のケーパビリティに関する推奨事項についてはセキュリティ節を参照してください。

#### PackagesAvailable Message

このメッセージの構造は以下のとおりです。

```
message PackagesAvailable {
    map<string, PackageAvailable> packages = 1;
    bytes all_packages_hash = 2;
}
```

##### PackagesAvailable.packages

パッケージのマップです。キーはパッケージ名です。

##### PackagesAvailable.all_packages_hash

リモートでインストールされたすべてのパッケージの集約ハッシュです。

Clientはこの値を、以降の[PackageStatuses](#packagestatuses-message)メッセージに含めるべき（SHOULD）です。これにより、Agentに利用可能なパッケージの集合が変化したことをServerが識別し、次のServerToAgentメッセージで利用可能なパッケージを指定できます。

Serverがパッケージのagentへの送信をサポートし、かつAgentがパッケージを受け入れられることを示している場合、このフィールドは常に設定されなければなりません（MUST）。

#### PackageAvailable Message

このメッセージは、Agentがすでに持っているパッケージの新規インストール、あるいはアップグレードまたはダウングレードを開始するための、Serverからのオファーです。このメッセージの構造は以下のとおりです。

```protobuf
message PackageAvailable {
    PackageType type = 1;
    string version = 2;
    DownloadableFile file = 3;
    bytes hash = 4;
}
```

##### PackageAvailable.type

パッケージの種類です。アドオンまたはトップレベルパッケージのいずれかです。

```protobuf
enum PackageType {
    TopLevelPackage = 0;
    AddonPackage    = 1;
}
```

##### PackageAvailable.version

Server側で利用可能なパッケージのバージョンです。Agentは例えば、以前にダウンロード済みでインストールに失敗したパッケージの再ダウンロードを避けるためにこの情報を使うことができます。

##### PackageAvailable.file

パッケージのダウンロード可能なファイルです。

##### PackageAvailable.hash

パッケージのハッシュです。PackageAvailableメッセージの他のすべてのフィールドとパッケージのファイル内容に基づいて算出されるべき（SHOULD）です。このハッシュは、Agentが持っているパッケージがServerが提示しているパッケージと異なるかどうかをAgentが判断するために使われます。

#### DownloadableFile Message

このメッセージの構造は以下のとおりです。

```protobuf
message DownloadableFile {
    string download_url = 1;
    bytes content_hash = 2;
    bytes signature = 3;
    Headers headers = 4; // Status: [Development]
}
```

##### DownloadableFile.download_url

HTTP GETリクエストを使ってファイルをダウンロードできるURLです。指定されたURLのServerは、ダウンロードの再開ができるよう、範囲リクエストをサポートするべき（SHOULD）です。

##### DownloadableFile.content_hash

ファイル内容のSHA256ハッシュです。ファイルが正しくダウンロードされたことをAgentが検証するために使えます。

##### DownloadableFile.signature

ファイル内容の任意の署名です。ダウンロードされたファイルの真正性をAgentが検証するために使えます。例えば[detached GPG signature](https://www.gnupg.org/gph/en/manual/x135.html#AEN160)にできます。正確な署名方式と検証方式はAgent固有です。推奨事項については[コード署名](#コード署名)を参照してください。

##### DownloadableFile.headers

ステータス: [Development]

HTTP GETリクエストで使う任意のヘッダーです。通常、アクセストークンや他の認可ヘッダーを設定するために使われます。HTTPベースのプロトコルでは、Agentはこれらをリクエストヘッダーに設定するべきです。例: key="Authorization", Value="Basic YWxhZGRpbjpvcGVuc2VzYW1l"。

### カスタムメッセージ

ステータス: [Development]

#### 目的

OpAMPプロトコルは、Agentのリモート管理と設定のあらゆる側面をカバーすることを意図しています。このコア機能の中でさらに要件が識別された場合、プロトコルは将来のリリースでこの追加の振る舞いをサポートするために拡張されることが期待されます。しかし、OpAMPプロトコルではサポートされない、Agentとの通信が必要なユースケースも一部存在します。これらは、Agentのリモート管理の範囲外であるか、1つのAgentに高度に固有で他のAgentの種類に一般化できないためです。CustomCapabilitiesとCustomMessageは、別の接続を開いたり、まったく新しいプロトコルを定義したりせずに、ServerとAgentの間でカスタムの振る舞いを実装できるようにします。

多くのAgentとAgent Management Serverの間の相互運用性はOpAMPの目標ですが、追加のカスタム機能は特定のAgentとServerのみでサポートされる場合があります。適切な場合、実装者はCustomMessageの使用方法を文書化し、追加のプラットフォームへのサポート追加を促すことが推奨されます。適切な場合、ServerとAgentの実装で広くサポートされているカスタムケーパビリティは、将来標準のOpAMPケーパビリティとして採用されることがあります。

#### CustomCapabilities

AgentとServerはどちらも、このメッセージを使って特定のカスタムケーパビリティをサポートすることを示すべきです。これはServerToAgentとAgentToServerの両方でサポートされます。このメッセージは、サポートされるカスタムケーパビリティの一覧が変化したときにのみ送信されるべきです。このメッセージが送信されたとき、サポートされるカスタムケーパビリティの一覧はこの一覧に一致するよう更新されるべきです。このメッセージが一度も送信されない場合、カスタムケーパビリティはサポートされていないと見なされます。

ケーパビリティは、オプションのバージョン情報を伴う逆順FQDNで識別されなければなりません。これにより、異なる組織によって導入されるケーパビリティ間の衝突を避けられます。ケーパビリティ名は、そのカスタムケーパビリティを作成した組織の逆順FQDN、ピリオド、そしてケーパビリティを識別する短い名前で構成されます。バージョンは任意であり、ケーパビリティ名とはスラッシュで区切られます。

例えば、「com.company.capability/v2」は、「company.com」によって作成された「capability」のバージョン2を識別します。

サポートされていないケーパビリティを持つCustomMessageを受信した場合、そのメッセージは無視できます。あるケーパビリティのサポートは、そのケーパビリティに対応するすべてのCustomMessageがサポートされることを意味します。正確な振る舞いはカスタムケーパビリティごとに固有です。

```protobuf
message CustomCapabilities {
  repeated string capabilities = 1; // Status: [Development]
}
```

##### CustomCapabilities.capabilities

サポートされているカスタムケーパビリティの一覧です。各ケーパビリティは、オプションのバージョン情報を伴う逆順FQDNであり、そのカスタムケーパビリティを一意に識別し、サポートされているCustomMessage内で指定されたケーパビリティと一致するべきです。

#### CustomMessage

CustomMessageは、AgentとServerの間でカスタムメッセージを送信できるようにします。双方向のカスタム通信を可能にするため、ServerToAgentとAgentToServerの両方でサポートされます。これには、AgentとServerの両方がメッセージの内容とエンコーディングについて合意していることが必要です。ケーパビリティがサポートされていない場合、またはメッセージの種類が認識されない場合、そのメッセージは無視できます。

CustomMessageは、メッセージの種類を識別し、メッセージの内容を持つバイナリデータを含みます。データのフォーマットはメッセージの種類によって決まり、OpAMPプロトコルの範囲外です。

```protobuf
message CustomMessage {
    string capability = 1;
    string type = 2;
    bytes data = 3;
}
```

##### CustomMessage.capability

ケーパビリティを一意に識別し、CustomCapabilitiesメッセージ内のいずれかのケーパビリティと一致する逆順FQDNです。

##### CustomMessage.type

ケーパビリティ内でのメッセージの種類です。ケーパビリティは、そのケーパビリティを実装するために使われるカスタムメッセージの種類を定義します。typeは、そのケーパビリティ内でのみ一意である必要があります。

##### CustomMessage.data

メッセージのバイナリデータです。ケーパビリティは、それが定義する各カスタムメッセージ種別について、dataの内容のフォーマットを指定しなければなりません。

#### 例

以下の例は、CustomCapabilitiesとCustomMessageの可能な使用方法を説明していますが、仕様書の一部となることを意図したものではありません。これらの例では、メッセージ種別のFQDNを作るために「io.opentelemetry.」が使われていますが、各ベンダーは自身のFQDNを使うことが期待されます。

##### 一時停止/再開の例

Agentがテレメトリー収集の一時停止と再開をサポートしている場合を考えます。一時停止中は、テレメトリーデータの収集も送信も行われません。この振る舞いをServerが制御できるようにするために、CustomMessageを使うCustomCapabilityを導入できます。

###### エージェントの接続

接続時、Agentは「io.opentelemetry.pause」を含むCustomCapabilitiesを含むAgentToServerメッセージを送信します。それに応じて、Serverは「io.opentelemetry.pause」を含むCustomCapabilitiesメッセージを含むServerToAgentメッセージを送信します。

```
CustomCapabilities {
  capabilities: ["io.opentelemetry.pause"]
}
```

###### Pause

Serverは、type「pause」の以下のCustomMessageを含むServerToAgentメッセージを送信します。これは追加情報を持たない単純なコマンドであるため、データは送信されません。

```
CustomMessage {
  capability: "io.opentelemetry.pause"
  type: "pause"
}
```

Agentがこのメッセージをサポートしており、一時停止に成功した場合、type「status」のCustomMessageを含み、新しい一時停止状態を示すバイナリのJSONエンコードされたレスポンスをdataに含むAgentToServerメッセージを返します。

```
CustomMessage {
  capability: "io.opentelemetry.pause"
  type: "status"
  data: {
    "paused": true
  }
}
```

Agentがこのメッセージをサポートしているが一時停止しようとしてエラーに遭遇した場合、type「error」のCustomMessageを含み、エラーメッセージを含むバイナリのJSONエンコードされたレスポンスをdataに含むAgentToServerメッセージを返します。

```
CustomMessage {
  capability: "io.opentelemetry.pause"
  type: "error"
  data: {
    "error": "Unable to pause"
  }
}
```

###### Resume

Pauseと同様ですが、ServerToAgentメッセージにはtype「resume」のCustomMessageが含まれ、成功時の新しいステータスは`{ "paused": false }`になります。

```
CustomMessage {
  capability: "io.opentelemetry.pause"
  type: "resume"
}
```

##### サービスディスカバリの例

サービスディスカバリは、Agentがテレメトリーを収集できる、Agentからアクセス可能な稼働中のサービスを発見することを含みます。この例では、Serverが利用可能なサービスを問い合わせるメッセージをAgentに送信します。Agentからは、利用可能なサービスに関する情報を含む応答が期待されます。

###### エージェントの接続

接続時、Agentはケーパビリティ「io.opentelemetry.discovery」を含むCustomCapabilitiesメッセージを送信します。それに応じて、Serverはケーパビリティ「io.opentelemetry.discovery」も含むCustomCapabilitiesメッセージを送信します。このケーパビリティがAgentとServerの両方でサポートされているため、このケーパビリティに関連するメッセージをディスカバリのサポートのために送信できます。

###### FindServices

Serverは、以下のCustomMessageを含むServerToAgentメッセージを送信します。これは追加情報を持たない単純なリクエストであるため、データは送信されません。

```
CustomMessage {
  capability: "io.opentelemetry.discovery"
  type: "find_services"
}
```

###### FindServicesResponse

サービスを発見した後、Agentは、発見したすべてのサービスに関する情報を含む複雑なデータ構造のJSONエンコーディングをバイナリデータに含むCustomMessageを含むAgentToServerメッセージを送信します。このデータ構造には、ディスカバリ中に発生したエラーのための任意のフィールドも含まれます。

```
CustomMessage {
  capability: "io.opentelemetry.discovery"
  type: "available_services"
  data: { ... }
}
```

### AvailableComponents Message

ステータス: [Development]

各Agentは複数のサブコンポーネントから構成されることがあります。これらのコンポーネントは、それぞれ関連するメタデータを持つことがあります。AvailableComponentsメッセージによって、Agentが自身が持つコンポーネントについてOpAMP serverに知らせることができます。

AvailableComponentsメッセージの構造は以下のとおりです。

```protobuf
message AvailableComponents {
    map<string, ComponentDetails> components = 1;
    bytes hash = 2;
 }
```

#### AvailableComponents.components

componentsフィールドには、一意なIDから[ComponentsDetails](#componentdetails-message)メッセージへのマップが含まれます。直前のServerToAgentメッセージでOpAMP serverがReportAvailableComponentsフラグを設定して明示的にリクエストしていない場合、このフィールドはメッセージから省略されることがあります。

##### 例

###### OpenTelemetry Collector

[OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector)のカスタムビルドに含まれるコンポーネントに関する情報を、ComponentDetailsがどのように保持できるかの例を示します。

```jsonc
{
  "receivers": {
    "sub_component_map": {
      "hostmetrics": {
        "metadata": {
          "code.namespace": "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver@v0.107.0",
        }
      }
    }
  },
  "processors": {
    "sub_component_map": {
      "batch": {
        "metadata": {
          "code.namespace": "go.opentelemetry.io/collector/processor/batchprocessor@v0.107.0"
        }
      },
      "transform": {
        "metadata": {
          "code.namespace": "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor@v0.107.0"
        }
      },
    }
  },
  "exporters": {
    "sub_component_map": {
      "nop": {
        "metadata": {
          "code.namespace": "go.opentelemetry.io/collector/exporter/nopexporter@v0.107.0"
        }
      }
    }
  }
  // ... Component list continues for extensions and collectors ...
}
```

###### Fluent Bit

Fluent Bitが利用可能なコンポーネントをどのように報告できるかの例を示します。

```jsonc
{
  "input": {
    "sub_component_map": {
      "tail": {}
    }
  },
  "parser": {
    "sub_component_map": {
      "json": {}
    }
  },
  "filter": {
    "sub_component_map": {
      "lua": {},
      "modify": {}
    }
  },
  "output": {
    "sub_component_map": {
      "null": {},
      "file": {},
    }
  }
}
```

#### AvailableComponents.hash

componentsフィールドのagent側で算出されたハッシュです。Agentは、自身が利用可能なコンポーネントを報告できる能力を持つ場合、このハッシュを含めなければなりません（MUST）。フォーマットはagentによって決められます。同じコンポーネントの集合を持つAgentであれば、ハッシュは同一であるべき（SHOULD）です。

#### 初回のハンドシェイク

送信されるデータ量を減らすため、AvailableComponentsメッセージは最初からコンポーネントの完全なマップを含みません。代わりに、AvailableComponentsメッセージは、componentsを空のマップにしたまま、agent側で算出されたハッシュのみを設定します。

最初のAgentToServerメッセージには、agent側で算出されたハッシュのみが設定されたAvailableComponentsメッセージを含めるべき（SHOULD）です。

OpAMP serverは、このハッシュを使って、AvailableComponentsの集合を記憶しているかどうかを判断できます。ハッシュがOpAMP server上に見つからない場合、serverはServerToAgentメッセージ内にReportAvailableComponentsフラグを設定することで、コンポーネントの完全なマップの報告をリクエストできます。このフラグが指定された場合、Agentはcomponentsフィールドに利用可能なコンポーネントの完全な記述を設定します。

serverはその後、任意でそのハッシュに対応するコンポーネントを永続化してもよく、それによって以降の接続で再度リクエストする必要がなくなります。

AvailableComponentsメッセージも[ステータスの圧縮](#エージェントステータスの圧縮)の対象であり、ハッシュが以前の値から変化していない場合は省略できます。

#### ComponentDetails Message

ComponentDetailsメッセージはAgentのコンポーネントを記述します。

ComponentDetailsの構造は、ComponentHealthの構造と1対1で一致する必要はありません。ComponentHealthは一般に現在稼働中のコンポーネントのインスタンスを指しますが、ComponentDetailsは、現在必ずしも稼働しているとは限らない、利用可能なコンポーネントの種類を指します。同じコンポーネント種別が複数の稼働中インスタンスを持つこともあります。

ComponentDetailsメッセージの構造は以下のとおりです。

```protobuf
message ComponentDetails {
    repeated KeyValue metadata = 1;
    map<string, ComponentDetails> sub_component_map = 2;
}
```

##### ComponentDetails.metadata

コンポーネントを記述するために使える追加のキー/値の組です。

キー/値の組は、該当する場合はセマンティック規約に準拠するべき（SHOULD）です。詳細は[OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/)を参照してください。

##### ComponentDetails.sub_component_map

一意なコンポーネントIDからサブコンポーネントの詳細へのマップです。基盤となるシステムを記述するために必要な深さでネストできます。

## 接続管理

### 接続の確立

ClientはHTTP(S)接続を確立してServerに接続します。

WebSocketトランスポートが使われる場合、接続はWebSocket標準で定義されているようにWebSocketにアップグレードされます。

接続が確立された後、Clientは最初の[ステータスレポート](#ステータスレポート)を送信し、それへの応答を待たなければなりません（MUST）。

ClientがServerへの接続を確立できない場合、Serverを過負荷にしないよう、ジッターを伴う指数バックオフ戦略を使って接続の再試行を行うべき（SHOULD）です。

接続の再試行を行う際、Clientは、Serverから受信する[スロットリング](#スロットリング)のレスポンスを尊重するべき（SHOULD）です。

### 接続のクローズ

#### OpAMPクライアントによるWebSocket接続のクローズ

接続をクローズするために、Clientはまずagent_disconnectフィールドが設定されたAgentToServerメッセージを送信しなければなりません（MUST）。その後、ClientはWebSocketの[Close](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1)制御フレームを送信し、WebSocket標準で定義された手順に従わなければなりません（MUST）。

#### サーバーによるWebSocket接続のクローズ

接続をクローズするために、ServerはWebSocketの[Close](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1)制御フレームを送信し、WebSocket標準で定義された手順に従わなければなりません（MUST）。

#### プレーンHTTPトランスポート

OpAMPのHTTPレスポンスが完了した時点で、Clientは論理的に切断されたと見なされます。Client接続は常にHTTPレスポンスの完了後に失われるものと暗黙的に見なされるため、Clientがagent_disconnectフィールドを設定したAgentToServerメッセージを送信する必要はありません。

ClientとServerはHTTPキープアライブを使ってもかまいませんが、これはOpAMPプロトコルの論理的な動作には影響しません。

Serverは、何をアクティブなAgent（例えば継続的にポーリングしているClient）と見なし、何を非アクティブなAgent（例えば特定の期間HTTPリクエストを行っていないClient）と見なすかを、自身のビジネスロジックで判断してもかまいません。このビジネスロジックはOpAMP仕様書の対象外です。

### WebSocket接続の復旧

確立済みのWebSocket接続が予期せず切断された場合、Clientは直ちに再接続を試みるべき（SHOULD）です。再接続が失敗した場合、Clientは[接続の確立](#接続の確立)で説明されたバックオフを使って接続の試行を続けるべき（SHOULD）です。

### 重複するWebSocket接続

各Clientインスタンスは、Serverに対して同時に1回を超えて接続するべきではありません（SHOULD NOT）。ClientがServerに再接続する必要がある場合、Clientはまずagent_disconnectメッセージを送信し、既存の接続をクローズし、その後にのみ再接続を試みなければなりません（MUST）。

Serverは、同じClientインスタンスが複数の同時接続を持っている、あるいは複数のAgentインスタンスが同じinstance_uidを使っていることを検出した場合、切断するかリクエストへの応答を拒否してもよい（MAY）ものとします。

Serverは（例えばAgentが不良なUID生成器を使っている場合や、Agentが動作しているVMの複製によって）重複する`instance_uid`を検出するべき（SHOULD）です。重複する`instance_uid`が検出された場合、Serverは新しい`instance_uid`を生成し、AgentIdentificationの`new_instance_uid`の値として送信するべき（SHOULD）です。

### 重複するinstance_uidの検出

Serverは、重複する`instance_uid`の値で接続してくるAgentを検出できることがあります。検出メカニズムは実装固有です。重複の検出は、Serverが`instance_uid`の再利用やなりすましを防ぎたい場合に望ましいものです。

これが発生した場合、Serverは重複したインスタンスからのリクエストを切断するか拒否してもよい（MAY）ものとします。Serverは新しい`instance_uid`を生成し、`ServerToAgent`レスポンスのAgentIdentificationの`new_instance_uid`の値として送信するべき（SHOULD）です。

### 認証

ステータス: [Beta]

ClientとServerは、[Basic](https://datatracker.ietf.org/doc/html/rfc7617)認証や[Bearer](https://datatracker.ietf.org/doc/html/rfc6750)認証といった、HTTPがサポートする認証方式を使ってもよい（MAY）ものとします。認証は、HTTP接続が確立され、WebSocket接続にアップグレードされる前に行われます。

Client認証が失敗した場合、Serverは[401 Unauthorized](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1)で応答しなければなりません（MUST）。

### 不正なリクエスト

Serverが不正な形式のAgentToServerメッセージを受信した場合、Serverは[error_response](#servertoagenterror_response)を適宜設定したServerToAgentメッセージで応答するべき（SHOULD）です。[type](#servererrorresponsetype)フィールドはBAD_REQUESTに設定されなければならず（MUST）、[error_message](#servererrorresponseerror_message)は、AgentToServerメッセージの問題を説明する人間が読める記述であるべき（SHOULD）です。

BAD_REQUEST応答を受信したAgentToServerメッセージについて、Clientは再送するべきではありません（SHOULD NOT）。

### メッセージの再送

Clientは、以下の場合にAgentToServerメッセージを再送してもよい（MAY）ものとします。

* 応答を必要とするAgentToServerメッセージを送信したが、合理的な時間内に応答が受信されなかった場合（このタイムアウトは設定可能でもよい（MAY）ものとします）。
* 応答を必要とするAgentToServerメッセージを送信したが、応答を受信する前に接続が失われた場合。
* [スロットリング](#スロットリング)節で説明されているように、ServerからUNAVAILABLE応答を受信した後。

応答を必要とするメッセージについて、Serverが同じメッセージを複数回受信した場合、Serverが重複を検出して1回だけ処理する場合でも、Serverは最初のメッセージだけでなく各メッセージに応答しなければなりません（MUST）。

接続が利用できない場合でも、Clientは、Serverに送信したいメッセージの増大するキューを保持する必要はない点に注意してください。Clientは通常、Serverに送信したい各種類のメッセージについて、最新の1つのメッセージだけを保持し、接続が利用可能になった時点で送信すればよいのです。

例えば、Clientは Agentのステータスを追跡し、最初の機会に送信できるAgentToServerメッセージを組み立てておくべきです。Clientが（例えば接続がまだ利用できないなどの理由で）AgentToServerメッセージを送信できない場合、Clientは、Agentのステータスが変化するたびに新しいAgentToServerメッセージを作成し、送信待ちのキューにこれらすべてのAgentToServerメッセージを保持する必要はありません。Clientは、単に最新の1つのAgentToServerメッセージを保持し、最初の機会に送信すればよいのです。これには当然、AgentToServerメッセージが、直前に報告された以降のすべての変更を含み、Agentの現在（最新）の状態を正しく反映している必要があります。

同様に、アドオンステータスレポートやエージェントパッケージインストールステータスレポートといった、他のすべてのAgentレポート機能についても、Clientは最新の1つのステータスメッセージだけを保持し、最初の機会に送信すればよいことが求められます。

逆方向についても、まったく同じロジックが当てはまります。Serverは通常、Agentに配送したい特定の種類の最新の1つのメッセージだけを保持し、Clientへの接続が利用可能になった時点で送信すればよいのです。

### スロットリング

#### WebSocketトランスポート

Serverが過負荷状態にあり、AgentToServerメッセージを処理できない場合、Serverは、[error_response](#servertoagenterror_response)に、[type](#servererrorresponsetype)フィールドをUNAVAILABLEに設定した値を入れたServerToAgentメッセージで応答するべき（SHOULD）です。Clientは切断し、待ち、その後再接続して動作を再開するべき（SHOULD）です。retry_infoフィールドには、再接続までにClientが待つべき時間を指定するretry_after_nanosecondsフィールドを任意で設定してもよい（MAY）ものとします。

```protobuf
message RetryInfo {
    uint64 retry_after_nanoseconds = 1;
}
```

retry_infoが設定されていない場合、Clientは再試行の間隔を段階的に増やす指数バックオフ戦略を実装するべき（SHOULD）です。

#### プレーンHTTPトランスポート

プレーンHTTPトランスポートが使われる場合、およびWebSocketが使われていてServerが過負荷でHTTP接続をWebSocketにアップグレードできない場合のいずれにおいても、Serverは[HTTP 503 Service Unavailable](https://datatracker.ietf.org/doc/html/rfc7231#page-63)または[HTTP 429 Too Many Requests](https://datatracker.ietf.org/doc/html/rfc6585#section-4)レスポンスを返してもよく（MAY）、Clientが再接続を試みるべきタイミングを示すために任意で[Retry-After](https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.3)ヘッダーを設定してもよい（MAY）ものとします。Clientは、HTTP仕様の対応する要件を尊重するべき（SHOULD）です。

注: Retry-Afterヘッダーは、Clientがサーバーへ再接続を試みる場合にのみ使われるべき（SHOULD）です。Agentが再接続中は、Clientは通常の[ハートビート](#opampconnectionsettingsheartbeat_interval_seconds)メッセージを送信しようとするべきではありません。

推奨される最小の再試行間隔は30秒です。

## セキュリティ

リモート設定とダウンロード可能なパッケージは、重大なセキュリティリスクです。悪意のあるServer側の設定や悪意のあるパッケージを送信することで、Serverは望ましくない作業をAgentに強制する可能性があります。この節では、Agentのセキュリティリスクを低減する推奨事項を定義します。

この節のガイドラインは実装上任意ですが、機密性の高いアプリケーションでは強く推奨されます。

### 一般的な推奨事項

Agentがゼロトラストのセキュリティモデルを採用し、Serverから受信するリモート設定やその他のオファーを自動的に信頼しないことを推奨します。Serverから受信したデータは、悪意のある攻撃者によって引き起こされる可能性のある被害を制限・防止するために、Agentによって検証・サニタイズされるべきです。以下を推奨します。

* Agentは、機密性の高いファイルへのアクセスや高権限操作の実行から自身を守るために、可能な限り最小の権限で動作すべきです。Agentはroot権限で動作するべきではありません。そうでなければ、侵害されたAgentが悪意のある攻撃者によるマシンの完全な制御につながる可能性があります。
* Agentがローカルデータを収集できる場合、収集を特定のディレクトリの集合に限定するべきです。この制限はローカルで指定されるべきであり、リモート設定によって上書き可能であってはなりません。この規則に従わない場合、リモート設定機能が悪用されてAgentのマシン上の機密情報にアクセスされる可能性があります。
* Agentが、稼働しているマシン上に存在する外部コードを実行できる能力を持ち、この機能をAgentの設定で指定できる場合、Agentはこの機能を、限定されたディレクトリの集合内にある特定のスクリプトのみに限定するべきです。この制限はローカルで指定されるべきであり、リモート設定によって上書き可能であってはなりません。この規則に従わない場合、リモート設定機能が悪用されてAgentのマシン上で任意のコードが実行される可能性があります。

### 設定の制限

Agentは、リモート設定によって強制される可能性のある動作を制限することが推奨されます。

特に、（テレメトリー収集Agentでよくあるように）設定によってAgentが稼働しているマシンからのデータ収集を指示できる場合、Agentがどのディレクトリやファイルを収集してよいかについてAgent側の制限を持つことを推奨します。リモート設定を受信した際、Agentは、その設定を制限の一覧と照合して検証しなければならず、制限に違反する場合は設定の全体または一部の適用を拒否するか、禁止されたディレクトリやファイルからデータを収集しないよう設定をサニタイズしなければなりません。

同様に、設定によってAgentが稼働しているマシン上でプロセスやスクリプトを実行するよう指示する手段が提供される場合、どのディレクトリのどの実行ファイルをAgentが実行してよいかについてAgent側の制限を持つことを推奨します。

これらの制限は、「拒否リスト」ではなく「許可リスト」の形式で指定されることが推奨されます。制限はハードコードされていてもよく、ローカルの設定ファイルでエンドユーザーが定義可能でもよいものとします。ServerからAgentへリモート設定を送信することでこれらの制限を上書きできてはなりません。

### オプトイン方式のリモート設定

リモート設定機能は、デフォルトではAgentで有効化されないことが推奨されます。この機能はユーザーによってオプトインされるべきです。

### コード署名

パッケージの一部である実行可能コードは、侵害されたServerが悪意のあるコードをAgentに配送することを防ぐために署名されるべきです。以下を推奨します。

* ダウンロード可能な実行可能コード（例えば実行可能パッケージ）はコード署名される必要があります。実際のコード署名と検証のメカニズムはAgent固有であり、OpAMP仕様書の関心の外にあります。
* Agentは、ダウンロードしたファイル内の実行可能コードについて、コード署名が有効であることを検証すべきです。
* ダウンロード可能なコードは、ファイル内容に含まれる署名で署名されることもあれば、DownloadableFileメッセージの[signature](#downloadablefilesignature)フィールドに記録された分離署名を持つこともあります。分離署名は、例えば[GPG signing](https://www.gnupg.org/gph/en/manual/x135.html#AEN160)で使われることがあります。
* コード署名にCertificate Authorityが使われる場合、侵害されたServerが悪意のあるコードに署名できないよう、Certificate Authorityとその秘密鍵はOpAMP Serverと同じ場所に配置しないことが推奨されます。
* Agentは、ダウンロードした実行可能コード（パッケージや、外部プロセスとして実行する任意のコード）が機密性の高いファイルへアクセスしたり高権限操作を実行したりしないよう、可能な限り最小の権限で実行すべきです。Agentは、ダウンロードしたコードをroot権限で実行するべきではありません。

## 相互運用性

### 部分的な実装の相互運用性

OpAMPは、Agentとサーバーに対して多数のケーパビリティを定義しています。これらのケーパビリティの大半は任意です。Agentまたはサーバーは、相手が特定のケーパビリティをサポートしていない場合に備えておくべきです。

AgentとServerはどちらも、最初のメッセージ交換で自身がサポートするケーパビリティを示します。ClientはAgentToServerメッセージ内でcapabilitiesビットフィールドを設定し、ServerはServerToAgentメッセージ内でcapabilitiesビットフィールドを設定します。

capabilitiesフィールド内で設定された各ビットは、特定のケーパビリティがサポートされていることを示します。Agentのケーパビリティの一覧は[こちら](#agenttoservercapabilities)にあります。Serverのケーパビリティの一覧は[こちら](#servertoagentcapabilities)にあります。

Serverは、特定のAgentのケーパビリティを把握した後、そのAgentがサポートしないケーパビリティの使用を停止しなければなりません（MUST）。

同様に、Agentは、Serverのケーパビリティを把握した後、Serverがサポートしないケーパビリティの使用を停止しなければなりません（MUST）。

相手が特定のケーパビリティをサポートしないことを検出した際にAgentとServerの振る舞いのどこがどう変わる必要があるかについては、この文書の関連する箇所で説明されています。

### 将来のケーパビリティの相互運用性

OpAMPが、この仕様書の現バージョンの実装と、この仕様書に記述されていない追加ケーパビリティを持つ将来の拡張版OpAMPの実装との間の相互運用性を可能にする方法は2つあります。

#### 無視可能なケーパビリティ拡張

相手側で黙って無視できるような形で機能を拡張する新しいケーパビリティについては、任意のProtobufメッセージに新しいフィールドを追加できます。この新しいケーパビリティを実装する送信側は新しいフィールドを設定します。新しいケーパビリティを認識しない、この仕様書の古いバージョンを実装している受信側は、単純に新しいフィールドを無視します。Protobufエンコーディングによって、残りのフィールドは受信側によって正しくデシリアライズされることが保証されます。

#### 無視不可能なケーパビリティ拡張

相手側で黙って無視できない形で機能を拡張する新しいケーパビリティについては、異なるアプローチが使われます。

AgentToServerおよびServerToAgentメッセージのcapabilitiesフィールドには、多数の予約ビットが含まれています。これらのビットは、将来OpAMPに追加される新しいケーパビリティのサポートを示すために使われるべき（SHOULD）です。

ClientとServerは、メッセージを送信する際にこれらの予約ビットを0に設定しなければなりません（MUST）。これにより、OpAMPの新しいバージョンを実装している受信側は、送信側が新しいケーパビリティをサポートしていないことを把握し、それに応じて自身の振る舞いを調整できます。

AgentToServerとServerToAgentのメッセージは、ClientとServerが最初に交換するメッセージであり、これによって双方が相手のケーパビリティを把握し、適切に振る舞いを調整できます。将来のケーパビリティに対して振る舞いが正確にどのように調整されるかは、その新しいケーパビリティの将来の仕様書で定義されなければなりません（MUST）。

#### Protobufスキーマの安定性

この仕様書は、OpAMP 1.0における[Protobuf定義](https://github.com/open-telemetry/opamp-spec/blob/v0.17.0/proto/opamp.proto)について、以下の安定性の保証を提供します。

- フィールドの型、番号、名前は変更されません。
- メッセージとenumの名前は変更されません。
- enumの選択肢に割り当てられた番号は変更されません。
- enumの選択肢の名前は変更されません。
- メッセージとenumの位置（トップレベルの字句スコープで宣言されているか、他のメッセージにネストされているか）は変更されません。
- パッケージ名とディレクトリ構造は変更されません。
- 既存のフィールドの`optional`と`repeated`の修飾子は変更されません。
- 既存のシンボルは削除されません。

上記の保証は、`[Beta]`とラベル付けされたメッセージとフィールドには適用されないことに注意してください。[Beta]のメッセージとフィールドは、[maturity matrix][beta]で定義されているように、より弱い保証の対象になります。

将来のバージョンのOpAMP仕様書は、この仕様書のバージョンで定義されたProtobufスキーマを修正することで拡張されることがあります。以下のProtobufスキーマの変更は、この仕様書の他の箇所で定義されている相互運用性の要件に準拠する限り許可されます。

- 既存のメッセージへの新しいフィールドの追加。
- 新しいメッセージまたはenumの追加。
- 既存のenumへの新しい選択肢の追加。
- 既存のoneofフィールドへの新しい選択肢の追加。

## 将来の可能性

非常に大量（数百万以上）のAgentを管理する際にServerへの接続数を減らすための中継用プロキシ（Concentrating Proxy）の仕様を定義する。

## 参考文献

### エージェント管理

* Splunkの[Deployment Server](https://docs.splunk.com/Documentation/Splunk/latest/Updating/Aboutdeploymentserver)
* vRealizeの[Log Insight Agents](https://docs.vmware.com/en/vRealize-Log-Insight/8.10/com.vmware.log-insight.agent.admin.doc/GUID-40C13E10-1554-4F1B-B832-69CEBF85E7A0.html)の集中管理設定
* Google Cloudの[Guest Agent](https://github.com/GoogleCloudPlatform/guest-agent)は、HTTPの[long polling](https://cloud.google.com/compute/docs/metadata/querying-metadata#waitforchange)を使用

### 設定管理

* [Uber Flipr](https://www.uber.com/blog/flipr/)
* Facebookの[Holistic Configuration Management](https://research.facebook.com/file/877841159827226/holistic-configuration-management-at-facebook.pdf)（push方式）

### セキュリティと証明書管理

* [mTLS in Go](https://web.archive.org/web/20240223142649/https://kofo.dev/how-to-mtls-in-golang)
* [ACME certificate management protocol](https://datatracker.ietf.org/doc/html/rfc8555)
* [ACME for client certificates](https://datatracker.ietf.org/doc/draft-moriarty-acme-client/)

### クラウドプロバイダーのサポート

* [AWS](https://aws.amazon.com/elasticloadbalancing/features/)
* [GCP](https://cloud.google.com/appengine/docs/flexible/go/using-websockets-and-session-affinity)
* [Azure](https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-websocket)

### その他

* Websocket Load Balancing（原文の参考文献リンクは署名付きの一時URLで失効しているため、[opamp-specリポジトリの参考文献節](https://github.com/open-telemetry/opamp-spec/blob/v0.17.0/specification.md#other)を参照してください）

[beta]: ../../spec/document-status/

